Java A Tope: JavaMail en Ejemplos - Rojas, Sucino - 2006
Java A Tope: JavaMail en Ejemplos - Rojas, Sucino - 2006
( JavaMail en ejemplos )
AU T O RE S: SE RGIO GÁ L VE Z RO JA S
IGN A CIO GA RCÍA SU CIN O
IL U ST RA CIÓ N
D E P O RT A D A : J O SÉ MIGU E L GÁ L VE Z RO JA S
J A VIE R MA CÍA S GÁ L VE Z
Sun , el lo go t ip o de Sun , Sun M icro sy st em s y Jav a so n m arcas o m arcas regist radas de Sun
M icro sy st em s In c. en lo s E E .U U . y o t ro s p aíses.
JavaMail(JavaMail en ejemplos)
Índice
Prólogo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
i
Índice
Capítulo 6: Seguridad. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.1 Visión general. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.2 Autenticación frente a un servidor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.2.1 Clases Authenticator y PasswordAuthentication . . . . . . . . . . . 79
6.2.2 Ejemplo completo.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.3 Conexión segura con SSL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.3.1 Proveedores de seguridad y propiedades. . . . . . . . . . . . . . . . . . 83
6.3.2 Ejemplo completo.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
ii
Java a tope: JavaMail en ejemplos
iii
Índice
iv
Java a tope: JavaMail en ejemplos
Prólogo
E
l correo electrónico es, actualmente, uno de los principales medios de
comunicación electrónicos asíncronos. De hecho, el primer uso que se
dio a la, hoy omnipresente, red internet fue precisamente el de
comunicar entre sí a los miembros de un equipo de investigación dispersos
geográficamente mediante emails. Y aún hoy día, a pesar del enorme auge de la
World Wide Web, el tráfico que soporta internet debido a las comunicaciones vía
correo electrónico supone un buen porcentaje del total.
Aparte de la indudable importancia de este tipo de comunicaciones realizadas
a través de aplicaciones clientes como Eudora, Outlook, Thunderbird, etc. en las que
intervienen directamente personas escribiendo y leyendo los correos, también resulta
muy interesante el que las aplicaciones sean capaces de enviar y recibir
automáticamente mensajes ante ciertas circunstancias detectadas de forma autónoma.
Por ejemplo, resulta conveniente informar a un administrador de bases de datos
cuándo se ha alcanzado un tamaño crítico en los espacios de tablas disponibles para
almacenar información, o cuándo se ha intentado algún tipo de acceso sospechoso no
autorizado. Análogamente, una aplicación puede ser capaz de recibir mensajes con
un formato predeterminado y procesarlos autónomamente: pedidos de almacén,
solicitudes de vigilancias para exámenes, etc. informando asimismo de la correcta o
incorrecta recepción de las peticiones.
Para todo ello, el lenguaje Java incorpora la API JavaMail que permite
gestionar cualquier tipo de correo electrónico actual, ya sea a través del protocolo
POP, IMAP o cualquier otro que pueda surgir en el futuro. Con JavaMail es posible
manipular mensajes de texto plano o HTML, e incluso manejar múltiples adjuntos,
imágenes incrustadas, prioridades o solicitar acuse de recibo por parte del
destinatario.
Aún más importante, JavaMail es relativamente fácil de manejar siempre y
cuando se conozcan sus fundamentos. Por este motivo, los capítulos que viene a
continuación establecen las bases de funcionamiento y, ejemplo a ejemplo, se va
profundizando en cada uno de los temas más interesantes proponiendo, en cada caso,
el código al completo para que el lector pueda hacer uso de él directamente.
v
Prólogo
vi
Java a tope: JavaMail en ejemplos
Capítulo 1
Fundamentos del correo electrónico
1
BBN es una empresa de alta tecnología que en la actualidad trabaja en proyectos de
vanguardia como seguridad en Internet, reconocimiento avanzado del habla o criptografía
cuántica. Sus siglas se corresponden con las de sus fundadores Leo Beranek, Richard Bolt y
Robert Newman.
1
Fundamentos del correo electrónico
el término empleado como divisor entre el usuario y la máquina fue la arroba porque
ésta en inglés se pronuncia “at” (en), lo que hace que una dirección x@y se lea como
“usuario x en máquina y”. A partir de ahí la expansión fue imparable. Surgieron los
primeros programas de correo electrónico como RD (el primero en crearse), NRD,
WRD y MSG considerado el primer programa moderno de gestión de correo
electrónico. Ya en en 1973 un estudio señalaba que el correo electrónico representaba
el 75% del tráfico en ARPANET.
En 1989 desapareció ARPANET y, por otro lado, el investigador Tim
Berners-Lee del centro europeo CERN en Suiza, desarrolló una propuesta de sistema
de hipertexto, lo que daría lugar a la World Wide Web (www).
En la actualidad el correo electrónico es la aplicación más popular de
Internet, y se calcula que la cantidad de información que se mueve a través del correo
supera varias veces a la información contenida en páginas Web.
En cuanto a España, el primer correo electrónico a través de Internet se envió
a finales de 1985 desde la Escuela Técnica Superior de Ingenieros de
Telecomunicación de la Universidad Politécnica de Madrid, donde se montó el primer
nodo español conectado a la red EUNET.
EUNET era la parte europea de la red USENET, que daba servicio de correo
electrónico a la mayoría de universidades de los Estados Unidos. USENET -el
equivalente en sus inicios a un foro de debate e intercambio en la Internet que
conocemos hoy día- había convergido con Internet a principios de los ochenta,
creando un dominio único de correo electrónico basado en el formato RFC 822. Este
estándar especifica la sintaxis de los mensajes de correo electrónico de tipo texto
enviados a través de Internet. RFC son las siglas de Request For Comment o Petición
de Comentarios; se trata de una serie de documentos o informes técnicos que edita el
IAB (Internet Architecture Board - Consejo de Arquitectura de Internet), con el
propósito de regular los mecanismos de trabajo y procedimientos de comunicación a
través de Internet
El primer nodo de correo instalado en España se denominó «Goya» y se
conectaba con el nodo central de EUNET en Holanda, en el Mathematical Centre de
Amsterdam. En 1992 se creó el primer proveedor comercial de servicios de Internet
en España: Goya Servicios Telemáticos.
En la actualidad se extienden distintos tipos de envíos perniciosos y
amenazas a través de este sistema de comunicación. Cabría destacar como los más
empleados los virus, los hoax y el spam. El ataque a través de virus consiste en enviar
ficheros adjuntos infectados por algún virus. El hoax es un mensaje con contenido
falso o engañoso y que normalmente es distribuido en cadena. El spam coincide con
el hoax en que es distribuido de forma masiva y satura las redes, pero sin embargo
suele tratarse de información publicitaria, no solicitada por el receptor del mensaje.
2
Java a tope: JavaMail en ejemplos
3
Fundamentos del correo electrónico
4
Java a tope: JavaMail en ejemplos
5
Fundamentos del correo electrónico
6
Java a tope: JavaMail en ejemplos
Existen muchos más campos (no obligatorios en la mayoría de los casos) que
forman la cabecera de un mensaje. El lector que desee conocer todas las posibilidades
puede consultar el RFC 2822 y los RFC que tratan sobre MIME: RFC 2045 a 2049.
7
Fundamentos del correo electrónico
8
Java a tope: JavaMail en ejemplos
el servidor.
T QUIT: en esta fase hace que se pase a la fase de actualización.
• La última fase es la fase de actualización, en la que se eliminan los mensajes
marcados y se eliminan los recursos asociados a la conexión.
Por último señalar que los servidores basados en POP3 sólo ofrecen al
usuario acceso a un único buzón, mostrado normalmente a modo de carpeta por el
servidor. En otras palabras, el servidor no mantiene ninguna estructura de carpetas,
sino que ésta debe ser creada y mantenida por un programa MUA.
9
Fundamentos del correo electrónico
trabajar con SMTP seguro sobre TLS2 , lo que permite un transporte más seguro de
los datos; esta revisión se define en el RFC 3207. Por último, comentar la extensión
definida en el RFC 2554 que introduce el comando AUTH, lo que permite
autenticación frente al servidor.
Si bien los problemas que planteaba el SMTP básico se han mejorado, el
problema del spam sigue sin solucionarse, por lo que ya se proponen mecanismos
alternativos para definir una infraestructura de correo, como Internet Mail 2000
(http://www.im2000.org./).
2
Transport Layer Security: versión estandarizada por el IETF (Internet Engineering
Task Force) del protocolo SSL (Secure Sockets Layer), que consiste en un protocolo
criptográfico que permite cifrar la conexión y garantizar la autenticación.
10
Java a tope: JavaMail en ejemplos
Por todas estas características se observa que IMAP4 es más potente que
POP3, si bien aún se manejan los dos protocolos.
1.5.4 MIME (Multipurpose Internet Mail Extension)
Las extensiones MIME son una ampliación del RFC 822, definidas en los
R F C 2 0 4 5 a 2 0 4 9 ( h t t p : / / www. r f c - e s . or g / r fc/ r fc2 0 4 5 - e s . t x t ,
http://www.rfc-es.org/rfc/rfc2046-es.txt, http://www.rfc-es.org/rfc/rfc2047-es.txt),
pensadas para superar algunos de los problemas impuestos por las restricciones de
SMTP y del formato de mensaje del RFC 822. Algunos de estos problemas son los
siguientes:
• El contenido del mensaje sólo puede ser texto por lo que no se pueden
transmitir ficheros ejecutables, binarios o de cualquier otro tipo.
• El conjunto de caracteres aceptado está limitado al US-ASCII, de 7 bits, por
lo que no se admiten caracteres de otros idiomas, que necesitan de 8 bits para
su representación.
• El tamaño total de los mensajes no puede superar un determinado tamaño.
• Problemas en la conversión entre distintos formatos.
MIME describe una serie de mecanismos que, cooperando entre sí,
solucionan la mayoría de estos problemas sin añadir incompatibilidades a los correos
basados en RFC 822. Estos mecanismos se basan en:
• Definición de cinco nuevos campos en la cabecera del mensaje:
• Versión MIME: este campo debe contener al menos la versión 1.0
• Tipo de contenido: indica el tipo y subtipo de los datos contenidos
en el cuerpo del mensaje.
• Codificación para la transferencia del contenido: tipo de
codificación que se ha usado con el cuerpo del mensaje.
• Identificador de Contenido y Descripción de contenido: amplían la
descripción de la información contenida en el mensaje. Por ejemplo
para añadir información sobre una imagen o un fichero de voz.
• Se definen varios tipos de contenido y dentro de estos tipos varios subtipos.
A continuación se detallan algunos de los más importantes; el tipo principal
se separa del subtipo mediante una barra inclinada, por ejemplo
application/msword:
- application:
- msword: ficheros propios de Microsoft Office Word.
- pdf: formato de documento comprimido de Adobe Acrobat.
- zip: ficheros comprimidos con el formato zip.
- audio:
- ac3: estándar de compresión de audio.
11
Fundamentos del correo electrónico
12
Java a tope: JavaMail en ejemplos
Capítulo 2
Primeros pasos
13
Primeros pasos
lado, la tecnología de JavaBeans, cuando se utiliza junto con JSP, permite adaptar al
mundo web el patrón MVC (modelo-vista-controlador) que ya se había aplicado con
éxito a interfaces gráficas. La máxima expresión de este patrón aplicado al mundo
web ha venido de la mano de JavaServer Faces, tecnología que aporta un entorno de
trabajo que similar al de la API Swing pero orientado a páginas JSP.
Java llegó a ser aún más popular cuando Sun Microsystems introdujo la
especificación J2EE (Java 2 Enterprise Edition). Este modelo permite, entre otros,
una separación entre la presentación de los datos al usuario (JSP o applets), el modelo
de datos (EJB-Enterprise Java Beans), y el control (servlets). EJB es una tecnología
de objetos distribuidos que ha logrado el sueño de muchas empresas como Microsoft
e IBM de crear una plataforma de objetos distribuidos con un monitor de
transacciones. Con este nuevo estándar, empresas como BEA, IBM, Sun
Microsystems, Oracle y otros, han creado servidores de aplicaciones que han tenido
gran acogida en el mercado.
Además de programas del servidor, Java permite escribir programas de
cliente como cualquier otro lenguaje de programación, pudiéndose crear aplicaciones
con una interfaz gráfica o textual. También se pueden ejecutar programas Java como
parte de una página web en forma de applets de manera que son interpretados porlos
navegadores web. No obstante, esto no ha llegado a popularizarse tanto como se
esperaba en un principio.
Los programas fuente en Java son compilados a un lenguaje intermedio
formado por bytecodes (código máquina de un microprocesador virtual almacenado
en un fichero de extensión .class), que luego son interpretados por una máquina
virtual (JVM - Java Virtual Machine). Esta última sirve como una plataforma de
abstracción entre el ordenador y el lenguaje permitiendo que se pueda «escribir el
programa una vez, y ejecutarlo en cualquier lado». En la figura 2.1 puede apreciarse
este proceso.
14
Java a tope: JavaMail en ejemplos
Algunos de los paquetes más importantes incluidos en Java son los siguientes
(un paquete no es más que un directorio que contiene un conjunto de clases Java):
• java.lang: contiene las clases más importantes del lenguaje como la clase
Object (de la que heredan todas las demás), String y StringBuffer (manejo
de cadenas), System (permite usar recursos del sistema), etc.
• java.io: proporciona los recursos para poder realizar operaciones de
entrada/salida. Contiene clases como File (representación abstracta de
ficheros y directorios), FileReader y FileWriter (lectura y escritura de
ficheros), etc.
• java.util: conjunto de clases de utilidades como por ejemplo estructuras de
datos (Vector, Hashtable, etc.), Date (manejo de fechas),
GregorianCalendar (gestión avanzada de fechas), la interfaz Observable
(permite a los objetos notificarse entre sí cuando han sufrido alguna
modificación), etc.
• java.net: proporciona soporte para realizar distintas operaciones de red.
Incluye clases para dar soporte a TCP/IP, manejar Sockets, recursos URL,
etc.
• java.applet: provee las clases necesarias para poder crear y usar applets.
• java.awt: proporciona los mecanismos necesarios para crear interfaces de
usuario y para dibujar gráficos e imágenes. Se suele usar junto con el paquete
javax.swing.
15
Primeros pasos
16
Java a tope: JavaMail en ejemplos
usar otros protocolos, o desarrollar por nuestra cuenta soporte para algún
protocolo no definido por defecto en JavaMail, podemos es posible consultar
la página de Sun, http://java.sun.com/products/javamail/Third_Party.html,
donde se detallan una serie de protocolos que podemos usar, y que no se
ofrecen por defecto con JavaMail ya que no son estándares.
• Manejar adjuntos: JavaMail ofrece la posibilidad de añadir adjuntos a los
mensajes de salida, así como obtener y manipular los adjuntos contenidos en
un mensaje de entrada.
• Búsquedas: JavaMail ofrece la posibilidad de buscar mensajes dentro de la
cuenta de correo en el propio servidor -sin necesidad de descargar todo el
correo- que concuerden con un criterio de búsqueda previamente definido.
Para ello dispone de varias clases que permiten buscar mensajes con un
subject determinado, un from concreto, etc., devolviendo un array con los
mensajes que cumplan estos criterios. En el capítulo 7 veremos esto con más
detenimiento.
• Acuse de recibo y prioridad: Se puede incluir un acuse de recibo en los
mensajes de salida, para que el remitente sepa si un mensaje ha sido leído o
no por el destinatario, si bien no todos los servidores soportan esta
funcionalidad. Además se puede establecer la prioridad de un mensaje de
forma muy sencilla.
17
Primeros pasos
supuesto, antes de proceder a la instalación hay que comprobar que tenemos instalada
la versión de JDK 1.1.x o una posterior.
En el caso de tener instalada una versión de Java J2EE igual a la 1.3 o
posterior no tendremos que realizar ninguna instalación de JavaMail, pues ésta ya
forma parte de las API incorporadas por J2EE.
Los pasos para instalar JavaMail son los siguientes:
• Descargar la API de JavaMail. Para ello se accede a la dirección
http://java.sun.com/products/javamail y se descarga la versión que creamos
más oportuna. Se recomienda la versión 1.4, ya que incorpora algunas
mejoras sobre el tratamiento de los componentes MIME. De esta manera se
obtiene un fichero .zip llamado javamail-version, donde version se
corresponde con la versión de JavaMail que descargada.
• Descomprimir el fichero y copiar el fichero mail.jar en el directorio de
extensiones de Java. Suponiendo que Java está instalado en una máquina con
sistema operativo Microsoft Windows XP en el directorio
C:\j2sdk1.4.2_09, el fichero mail.jar se debe copiar en el subdirectorio
C:\j2sdk1.4.2_09\jre\lib\ext.
• Una alternativa a copiar el fichero mail.jar en el directorio de extensiones
es modificar el CLASSPATH, añadiéndole la ruta del directorio en el que
se haya copiado el fichero. Por ejemplo, en una máquina con Microsoft
Windows XP (ver figura 2.2):
• Con el botón derecho sobre Mi Pc se selecciona Propiedades.
• Escoger la pestaña Opciones avanzadas y luego pulsar el botón
Variables de entorno.
• Aparecerá una ventana dividida en dos cuadros: variables de
usuario y de sistema. Hay que mirar en estos dos cuadros si aparece
la variable Classpath (puede aparecer en los dos), se la debe
seleccionar y pulsar el botón Modificar.
• Aparecerá un cuadro con el nombre de la variable y su valor. En
valor de la variable, añadimos la ruta donde se haya copiado el
fichero mail.jar y se pulsa Aceptar. Por ejemplo si mail.jar se ha
copiado en un directorio llamado javamail que cuelga del directorio
home de java, se debe añadir al final del valor de la variable: <valor
anterior>;C:\java_home\javamail\mail.jarM;.. Nótese que el
punto al final es necesario.
• En una máquina con sistema operativo Unix/Linux, habría que escribir
(suponiendo que el contenido del fichero .zip que contiene JavaMail se
descomprime en usr/java/javamail):
$CLASSPATH=$CLASSPATH:usr/java/javamail
18
Java a tope: JavaMail en ejemplos
19
Primeros pasos
2.5.1 Directorios
El directorio \docs contiene ficheros de texto correspondientes a cada versión
de JavaMail en los que se explica los cambios introducidos en cada una de ellas.
Contiene también dos ficheros en formato Adobe Acrobat, llamados
javamail-version.pdf y providers.pdf que son el tutorial oficial de JavaMail
desarrollado por Sun y un tutorial sobre cómo desarrollar un proveedor de servicios,
es decir, dar soporte a algún protocolo no recogido en la implementación estándar de
JavaMail. Por último, contiene un directorio llamado \docs\javadocs, en el que está
la ayuda sobre la API en formato HTML generado por javadoc. Este es el formato en
el que se suele consultar la información sobre las APIs que componen Java, aunque
resulta tan cómodo de utilizar que, últimamente, su uso se está extendiendo a otros
lenguajes de programación.
El directorio \demo contiene una serie de ejemplos en los que se usa
JavaMail y que nos pueden servir para hacernos una idea de sus posibilidades. En el
fichero readme.txt es posible consultar una lista en la que, de forma muy somera, se
comenta que hace cada uno de estos ejemplos.
20
Java a tope: JavaMail en ejemplos
fichero mail.jar por mailapi.jar y repitiendo el proceso con el resto de ficheros .jar
necesarios en función del protocolo a usar. Un ejemplo de esto puede ser que
queramos realizar un pequeño applet cuya única función es enviar mensajes. En tal
caso es recomendable que la aplicación ocupe poco espacio. Además no necesita
manejar los protocolos POP3 ni IMAP, por tanto lo ideal sería que esa aplicación
usara sólo mailapi.jar y smtp.jar.
La utilización de los ficheros .jar por separado en lugar de mail.jar entraña
las siguientes consideraciones:
• Para usar más de un fichero .jar de los relacionados con los protocolos, hay
que tener instalada una versión de Java igual o superior a J2SE 1.2. Con el
mail.jar no sucede esto, pudiéndose usar incluso con la versión de J2SE 1.1.
• No es posible mezclar los .jar que dan soporte a los protocolos si éstos
pertenecen a distintas versiones de JavaMail.
21
Primeros pasos
22
Java a tope: JavaMail en ejemplos
Capítulo 3
Enviar y recibir mensajes básicos
23
Enviar y recibir mensajes básicos
24
Java a tope: JavaMail en ejemplos
Nombre Descripción
mail.protocolo.port Número de puerto del servidor que hay que usar. P.ej.
props.put (“mail.pop3.port”, “995")
Hay que indicar que para enviar el correo a través de algunos servidores no es
suficiente con indicar sólo su nombre sino que, además, hay que autenticarse y/o
utilizar una conexión segura. Esto se explica con detalle en el capítulo 6.
A continuación es necesario crear una sesión de correo usando para ello un
objeto de la clase Session; para ello hay que hacer uso del objeto de tipo Properties
creado en el paso anterior. Un objeto de la clase Session representa una sesión de
correo y reúne sus principales características, de manera que cualquier operación
posterior de envío/recepción de mensajes se hace en el contexto de una sesión. Dichos
objetos se crean haciendo uso de dos métodos estáticos que devuelven un objeto de este
tipo.
El primero de ellos devuelve la sesión por defecto, siempre la misma cuantas
veces sea invocado este método. Este método recibe como parámetro el objeto de tipo
Properties creado anteriormente y un objeto de la clase Authenticator. En el caso
que nos ocupa, como segundo parámetro enviaremos null, y en el capítulo 7 se verá
cómo usar convenientemente el objeto de tipo Authenticator:
Session sesion = Session.getDefaultInstance(props,null);
25
Enviar y recibir mensajes básicos
Constructor Comportamiento
Tabla 3.2 Constructores de la clase MimeMessage. Nótese que los tres últimos constructores
son de tipo protected y sólo pueden ser utilizados directamente por clase hijas de
MimeMessage.
26
Java a tope: JavaMail en ejemplos
posterioridad. En la tabla 3.2 pueden verse estos constructores. Así pues, en este
ejemplo la creación del mensaje queda como:
Message mensaje = new MimeMessage(sesion);
A continuación hay que proceder a rellenar los atributos (asunto, fecha de
envío, remitente, etc.) y el contenido del mensaje. Existe una gran variedad de
métodos para rellenar y obtener tanto estos atributos como el contenido en sí. Para
consultarlos todos podemos ver la correspondiente documentación de la API de
JavaMail; no obstante en la tabla 3.3 se detallan algunos de los más importantes. Debe
tenerse en cuenta que la clase MimeMessage no sólo hereda de Message sino que
también implementa las interfaces Part y MimePart, por lo que puede usarse
cualquier método de estas clases/interfaces.
Así, lo más fácil es rellenar el asunto del mensaje usando el método
setSubject qué recibe como parámetro la cadena que constituirá el asunto:
mensaje.setSubject("Hola Mundo");
Nótese que, dado que estos métodos pueden elevar excepciones (principalmente del
tipo MessagingException), hay que colocar el código encerrado en el
correspondiente try-catch.
El campo from hay que rellenarlo para que el receptor del mensaje sepa de
Método Comportamiento
Tabla 3.3 Métodos para establecer atributos en un mensaje. Todos pueden elevar la excepción
MessagingException. Los métodos de color azul son de la clase Message y los de color verde
de la interfaz Part..
27
Enviar y recibir mensajes básicos
quién proviene: método setFrom, que requiere como parámetro un objeto de tipo
Address que modela una dirección cualquiera en el sentido más general. Como esta
clase es abstracta hay que usar en su lugar alguna clase que la implemente, en nuestro
caso InternetAddress. Esta clase representa una dirección de Internet que cumpla con
la sintaxis especificada en el estándar RFC 822, y que suele ser de la forma
usuario@máquina.dominio o bien simplemente usuario si, por ejemplo tanto el
remitente como el destinatariuo poseen sus cuentas de correo en la misma máquina.
La clase InternetAddress posee diversos constructores, de los que usaremos uno que
sólo requiere una dirección pasada en forma de String. Nótese que aquí se podría
haber incluido cualquier dirección y no necesariamente nuestra dirección real de
correo, motivo por el cual resulta perfectamente posible enviar correo de forma
anónima e incluso aparentar ser otra persona. Así pues se tiene:
mensaje.setFrom(new InternetAddress(from));
A continuación se debe rellenar el atributo correspondiente al receptor del
mensaje. Para ello se utiliza el método addRecipient de la clase Message al que se
le pasan dos parámetros. El primero es un objeto del tipo Message.RecipientType
(tipo de destinatario), que es una clase interna a la clase Message, que tiene tres
constantes: TO, CC (Carbon Copy) y BCC (Blind Carbon Copy), que son los tres
tipos de recipientes o destinatarios. Con esto se especifica si la dirección del
destinatario se añade en el TO, el CC o el BCC del mensaje. El segundo parámetro
es la dirección que se añadirá al recipiente y es del tipo InternetAddress, al igual que
en el método setFrom. En este ejemplo sólo habrá un destinatario:
mensaje.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
Existe otro método que permite añadir varias direcciones de una sola tacada
en vez de hacerlo de una en una. Para ello se utiliza un array de direcciones en lugar
de una sola, y el método a emplear es addRecipients, en plural. Por ejemplo:
Address [] direcciones = new Address []{
new InternetAddress ("[email protected]"),
new InternetAddress ("[email protected]"),
};
mensaje.addRecipients(Message.RecipientType.TO, direcciones);
Este apartado finaliza rellenando ahora el contenido del mensaje, para lo que
se usa el método setText de la clase Message tal y como se vio en la tabla 3.3. Este
método además de establecer como contenido del mensaje el que se le especifique a
través de un String, establece que el tipo de contenido es texto plano (text/plain).
mensaje.setText("Este es el cuerpo del mensaje");
28
Java a tope: JavaMail en ejemplos
encarga de esto de una forma muy sencilla mediante sus métodos estáticos. En este
ejemplo se usará el método send:
Transport.send(mensaje);
Esta clase tiene sobrecargado el método send, de forma que también permite
enviar un mensaje a varios destinatarios, mandándole a este método un array de
direcciones. De esta forma se ignoran las direcciones que pudiesen estar definidas en
el mensaje y se envía sólo y exclusivamente a las especificadas en el array:
Transport.send(String mensaje,Address [] direcciones);
Por último, y como ya se ha indicado anteriormente, los métodos con los que
se crea el mensaje, con los que se rellena y el método send que lo envía pueden lanzar
excepciones por lo que es necesario capturarlas o relanzarlas. En el caso que nos
ocupa todas estas instrucciones se incluyen dentro de un bloque try-catch que captur
la excepción javax.mail.MessagingException, que es la clase base para manejar las
excepciones provocadas por los mensajes:
try {
Message mensaje = new MimeMessage(sesion);
mensaje.setSubject("Hola Mundo");
mensaje.setFrom(new InternetAddress(from));
mensaje.addRecipient( Message.RecipientType.TO,
new InternetAddress(to));
mensaje.setText("Este es el cuerpo del mensaje");
Transport.send(mensaje);
} catch (MessagingException e) {
System.out.println(e.getMessage());
}
29
Enviar y recibir mensajes básicos
30
Java a tope: JavaMail en ejemplos
31
Enviar y recibir mensajes básicos
32
Java a tope: JavaMail en ejemplos
Folder a partir de la clase Store y que haga referencia a esta carpeta. A continuación
se usa el método open y se expecificael modo en que se desea abrirla:
• Si sólo queremos leer los mensajes, sin borrarlos, se debe usar el modo de
sólo lectura READ_ONLY.
• En caso de que se quieran borrar los mensajes se debe usar el modo
READ_WRITE. En el capítulo 4 profundizaremos más sobre manejo de
carpetas y borrado de mensajes.
Así pues, el código quedaría:
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Con la carpeta ya abierta resulta sumamente sencillo recuperamos los
mensajes almacenados en ella. Para ello usaremos el método getMessages de la clase
Folder, sin parámetros, que devuelve un array de mensajes de tipo Message. Este
método también permite que se le pase como parámetro un array de enteros
(ordinales) con los números de los mensajes que queremos recuperar, o también que
le pasemos dos enteros que indican que se desean recuperar los mensajes almacenados
entre esas dos posiciones, ambas inclusive. En el caso que nos ocupa emplearemos la
primera fórmula:
Message [] mensajes = folder.getMessages();
33
Enviar y recibir mensajes básicos
Método Comportamiento
Tabla 3.4 Métodos para recuperar atributos de un mensaje. Todos pueden elevar la excepción
MessagingException. Los métodos de color azul son de la clase Message y los de color verde
de la interfaz Part..
mensajes[i].writeTo(os);
os.close();
}
En el caso de usar esta alternativa, hay que tener en cuenta que el método
writeTo puede lanzar una excepción de tipo IOException, por lo que al final del
bloque try-catch hay que añadir un nuevo catch para que capture esta excepción;
además hay que importar el paquete java.io.
Por otro lado, nótese que los atributos de los mensajes han sido obtenidos
usando métodos contrarios a los de la tabla 3.3. Entre otros, estos métodos de
recuperación aparecen en la tabla 3.4.
El último paso consiste en cerrar el Folder y el Store creados. Al cerrar el
Folder se lepasa como parámetro un valor boolean. Si este vale true se aplicarán los
cambios realizados sobre los mensajes, es decir, si hemos marcado un mensaje como
borrado entonces éste se borrará. En este ejemplo no se han marcado los mensajes de
ninguna forma, así que le pasamos false como parámetro. En el apartado 4.2 se
explicará esto con más profundidad. El código sería:
folder.close(false);
store.close();
En cuanto a las excepciones, podrían producirse en los siguientes casos
(todas de tipo MessagingException a no ser que se indique lo contrario):
34
Java a tope: JavaMail en ejemplos
35
Enviar y recibir mensajes básicos
36
Java a tope: JavaMail en ejemplos
Capítulo 4
Uso de banderines y gestión de buzones
4.1 Introducción
En este capítulo abordaremos la gestión de mensajes en el servidor desde dos
puntos de vista diferentes. Por un lado, es posible marcar los mensajes en el servidor
con objeto de realizar con ellos determinadas operaciones o bien con el único de fin
de dejar constancia de que han sido procesados de una determinada manera: han sido
vistos, han sido respondidos, son mensajes nuevos (recibidos después de la última
conexión), etc. Por otro lado, mediante el protocolo IMAP es posible mantener una
estructura jerárquica de buzones en el servidor, lo que facilita el correo eléctronico vía
WebMail.
37
Uso de banderines y gestión de buzones
}
• Por último, al cerrar el objeto de tipo Folder hay que pasarle como
parámetro el valor lógico true, para indicar que se apliquen todas las marcas
o banderines establecidos lo que, en el caso que nos ocupa, implica que se
eliminen todos los mensajes marcados como borrados:
folder.close(true);
Como ya se ha visto el borrado de mensajes implica el uso de banderines
(flags). Un banderín define el estado de un mensaje que está contenido en una carpeta.
Existen diferentes tipos de banderines, de manera que es posible activar en cada
mensaje banderines distintos. Los banderines (de la clase Flag) pueden ser definidos
por el usuario, aunque lo más normal es usar los que vienen predefinidos en JavaMail
a través de la clase Flags.Flag. Los banderines predefinidos y su significado se
muestran a continuación en la tabla 4.1.
Banderín Cometido
USER Indica que la carpeta que contiene a este mensaje soporta Flags
definidos por el usuario.
38
Java a tope: JavaMail en ejemplos
39
Uso de banderines y gestión de buzones
40
Java a tope: JavaMail en ejemplos
41
Uso de banderines y gestión de buzones
Método Comportamiento
boolean create(int type) Crea una carpeta que será del tipo indicado usando
las constantes de la clase Folder
Folder[] list(String pattern) Retorna las carpetas contenidas en esta carpeta que
se correspondan con el patrón indicado. Usando "*"
nos devuelve la estructura completa de carpetas
incluida la que llama al método
Tabla 4.2 Métodos principales de la clase abstracta Folder. Todos pueden elevar la excepción
MessagingException.
42
Java a tope: JavaMail en ejemplos
existentes o el poder crear carpetas anidadas son características que dependen del
servidor que estemos usando.
43
Uso de banderines y gestión de buzones
44
Java a tope: JavaMail en ejemplos
45
Uso de banderines y gestión de buzones
46
Java a tope: JavaMail en ejemplos
lectura/escritura, pues es donde vamos a copiar los mensajes. Una vez obtenidos los
dos primeros mensajes de INBOX esta carpeta ya puede ser cerrada.
Por último, estos se añaden a la cola de la lista de mensajes de la carpeta
Trash, usando el método copyMessages(...) que comentamos anteriormente:
Message [] mensajesCopiar = new Message[2];
Folder trashFolder = store.getFolder("INBOX.Trash");
trashFolder.open (Folder.READ_WRITE);
Folder INBOXFolder = store.getFolder("INBOX");
INBOXFolder.open (Folder.READ_ONLY);
mensajesCopiar[0] = INBOXFolder.getMessage(1);
mensajesCopiar[1] = INBOXFolder.getMessage(2);
INBOXFolder.copyMessages(mensajesCopiar, trashFolder);
INBOXFolder.close(false);
Para mostrar que, efectivamente, ha funcionado la copia de mensajes entre
carpetas, a continuación recuperaremos los mensajes de la carpeta Trash y los
mostraremos por pantalla de igual manera a como se hizo anteriormente con la
carpeta INBOX, pero indicándo como nombre INBOX.Trash. Para hacer esto puede
reutilizarse la variable trashFolder creada en el paso anterior:
47
Uso de banderines y gestión de buzones
48
Java a tope: JavaMail en ejemplos
System.out.println("Carpeta:" +
listaCarpetas[i].getFullName());
IMAPFolder imf = (IMAPFolder)listaCarpetas[i];
String [] atributos = imf.getAttributes();
System.out.println ("\t" + "Atributos: ");
for (int j= 0; j < atributos.length; j++)
System.out.println ("\t" + atributos[j] + "\n");
//Carpeta padre de cada carpeta
System.out.println ("\t"+ "Carpeta Padre: " +
listaCarpetas[i].getParent().getName() + "\n");
}
}
//==========================
// Copiar mensajes
//==========================
Message [] mensajesCopiar = new Message[2];
Folder trashFolder = store.getFolder("INBOX.Trash");
trashFolder.open (Folder.READ_WRITE);
Folder INBOXFolder = store.getFolder("INBOX");
INBOXFolder.open (Folder.READ_ONLY);
// Concatenar a la lista de mensajes de la carpeta que le pasamos
// como parámetro, el array de mensajes formado con los mensajes
// de la carpeta origen
mensajesCopiar[0] = INBOXFolder.getMessage(1);
mensajesCopiar[1] = INBOXFolder.getMessage(2);
INBOXFolder.copyMessages(mensajesCopiar,trashFolder);
INBOXFolder.close(false);
//==============================================
// Obtener los mensajes de la carpeta INBOX.Trash
//==============================================
Message [] mensajes = trashFolder.getMessages();
// Procesar los mensajes
for (int i= 0; i < mensajes.length; i++) {
System.out.println("Mensaje " + i + ":\n" +
"\tAsunto: " + mensajes[i].getSubject() + "\n" +
"\tRemitente: " + mensajes[i].getFrom()[0] + "\n" +
"\tFecha de Envío: " + mensajes[i].getSentDate() + "\n" +
"\tContenido: " + mensajes[i].getContent() + "\n");
}
trashFolder.close(false);
//==========================
// Borrar una carpeta
//==========================
universidadFolder.delete(false);
store.close();
} catch (MessagingException me) {
System.err.println(me.toString());
} catch (IOException ioe) {
49
Uso de banderines y gestión de buzones
System.err.println(ioe.toString());
}
}
}
Paquete Descripción
50
Java a tope: JavaMail en ejemplos
51
Uso de banderines y gestión de buzones
Propiedad Descripción
Tabla 4.4 Propiedades más comunes al establecer una sesión de correo electrónico
52
Java a tope: JavaMail en ejemplos
53
Uso de banderines y gestión de buzones
54
Java a tope: JavaMail en ejemplos
Capítulo 5
Correo electrónico multiparte
5.1 Introducción
En este capítulo trataremos sobre los correos electrónicos que, o bien poseen
ficheros adjuntos, o bien están formados por texto e imágenes en formato HTML. En
cualquier caso, cuando se trabaja con correos de este tipo es necesario usar las
extensiones MIME.
Como ya se comentó en el apartado 1.5.4, MIME está orientado a
proporcionar facilidades para el intercambio de distintos tipos de contenidos, ya sea
audio, video o imágenes en diferentes formatos, documentos procedentes de una
amplia diversidad de aplicaciones ofimáticas, etc., además de proporcionar soporte
para la internacionalización del lenguaje, es decir, poder incluir caracteres propios de
distintos alfabetos, como es el caso de la letra eñe (‘ñ’) del idioma español. Así, con
el estudio de los ejemplos de este capítulo se pondrán en evidencia las características
que JavaMail proporciona para el manejo de los tipos MIME.
A modo de resumen podemos decir que las clases que más intervienen
cuando se trabaja con correos en HTML o formados por adjuntos, son las clases
Message, MimeMessage, MultiPart, MimeMultiPart, BodyPart, MimeBodyPart
y la interfaz Part, que iremos viendo en los siguientes apartados. El proceso general
55
Correo electrónico multiparte
es igual que en los ejemplos ya vistos en capítulos anteriores pero, además, tendremos
que crear una serie de objetos de tipo BodyPart. Cada uno de estos objetos se
corresponderá con uno de los adjuntos, con el texto del mensaje en sí o con una de las
imágenes si se trata de correo HTML. Luego se van añadiendo a un objeto MultiPart
y con éste se rellena el mensaje. En la figura 5.5 puede verse gráficamente este
proceso.
56
Java a tope: JavaMail en ejemplos
es la del logotipo de Google; para conocer su URL original basta situar el cursor del
ratón sobre la imagen, pulsar el botón derecho y seleccionar la opción de
"Propiedades"; a continuación se selecciona la dirección que aparece en "Dirección
(URL)" y se copia al portapapeles; véase la figura 5.7.
57
Correo electrónico multiparte
una ventana del Bloc de Notas con el código HTML de la página. En este
maremagnum de código, hay que sustituir en la línea donde pone:
<IMG height=110 alt=Google src="Google_archivos/logo.gif" width=276>
por
<IMG height=110 alt=Google
src="http://www.google.com/intl/en/images/logo.gif" width=276>
que es la URL de la imagen copiada en el paso anterior. Una vez hecho esto se
selecciona Archivo->Guardar para almacenar los cambios.
Para que sea más fácil este proceso, usar en el Bloc de Notas la opción de
menú Editar->Buscar..., y buscar la cadena "src". En la figura 5.8 se muestra como
debe quedar el fichero “Google.html”.
Figura 5.8 Modificación de la URL del logo de Google con el Bloc de Notas
Una vez realizados estos pasos, ya es posible proceder a construir el
programa que envíe el mensaje de correo electrónico basado en HTML. El código es
similar al del epígrafe 3.3.1 -en el que se enviaba un mensaje de texto plano- pero con
algunas modificaciones. En este caso también se debe crear una sesión con las
propiedades del sistema, habiendo añadido a estas propiedades la dirección del
servidor SMTP que se va a usar, almacenándolo en la cadena smtpHost. Por tanto
comenzamos importando las librerías necesarias y realizando estos pasos, que ya bien
conocemos:
import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.*;
58
Java a tope: JavaMail en ejemplos
if (args.length != 2) {
System.out.println("Ha de enviar dos parámetros\n" +
"java EnviarCorreo fromAddress toAddress");
System.exit(1);
}
String from = args [0];
String to = args [1];
String smtpHost = "smtp.ono.com";
Properties props = System.getProperties();
props.put("mail.smtp.host",smtpHost);
Session sesion = Session.getDefaultInstance(props,null);
El paquete java.io se ha importado porque son necesarias las clases
BufferedReader, FileReader y la clase IOException con objeto de leer el contenido
del fichero “Google.html”.
El siguiente paso, que también es igual al de enviar un correo de texto plano,
será crear un mensaje vacío y rellenar su asunto, el emisor y el receptor.
try {
Message mensaje = new MimeMessage(sesion);
mensaje.setSubject("Google en HTML");
mensaje.setFrom(new InternetAddress(from));
mensaje.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
Lo siguiente consiste en rellenar el contenido del mensaje pero, a diferencia
de cuando se envía un correo de texto plano, no debe usarse el método setText()
pasándole una cadena como contenido del mensaje, sino que el contenido del mensaje
será el fichero HTML que antes se guardó; el método que hay que utilizar ahora es
setContent() pues es necesario indicar que el mensaje contiene código HTML que
debe interpretar el destinatario. En cualquier caso, para leer el fichero “Google.html”
deben usarse las clases BufferedReader y FileReader.
La clase FileReader se usa para leer un flujo de caracteres desde un fichero,
el cual es pasado como parámetro a su constructor, indicando su ruta y nombre. Si el
constructor de esta clase falla porque no se encuentra el fichero indicado o porque no
se puede abrir, se lanza una excepción de tipo FileNotFoundException que hereda
de IOException; en caso contrario se devuelve un canal de entrada de caracteres que
será, a su vez, el parámetro de entrada del constructor de la clase BufferedReader,
que constituye un estrato que proporciona funcionalidades de buffer cuando la
entrada/salida se produce desde un dispositivo de almacenamiento lento.
De la clase BufferedReader usaremos el método readLine() para leer líneas
del flujo de caracteres de entrada devuelto por el FileReader. Este método lee línea
por línea del flujo de entrada y devuelve cada una en forma de String, devolviendo
null cuando llegue al final del flujo, es decir, cuando se haya alcanzado el final del
59
Correo electrónico multiparte
60
Java a tope: JavaMail en ejemplos
61
Correo electrónico multiparte
System.err.println(me.getMessage());
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
}
62
Java a tope: JavaMail en ejemplos
63
Correo electrónico multiparte
64
Java a tope: JavaMail en ejemplos
65
Correo electrónico multiparte
66
Java a tope: JavaMail en ejemplos
67
Correo electrónico multiparte
68
Java a tope: JavaMail en ejemplos
mp.addBodyPart(adjunto1);
Ahora se añade el segundo adjunto al mensaje. No obstante, como el método
attachFile() sólo está disponible a partir de la versión 1.4 de JavaMail, y si se emplea
una versión anterior no podremos usarlo, vamos a estudiar la alternativa que se usaba
en versiones anteriores. Para ello se crea otro BodyPart y un objeto de la clase File,
indicándole la ruta completa y el nombre del fichero a adjuntar. Un objeto File no es
más que un modelo de abstracción de un fichero, que facilita las operaciones con él:
BodyPart adjunto2 = new MimeBodyPart ();
File path = new File ("c:/Temp/logo.gif");
A continuación se crea un objeto de la clase DataSource (fuente de datos)
que proporciona mecanismos de acceso sobre un conjunto de datos de cualquier tipo,
en este caso el fichero adjunto. Como esta clase es abstracta, se debe utilizar una
instancia de la clase FileDataSource, que hereda de ella y que provee servicios de
clasificación de datos. El constructor de esta clase recibe como parámetro el objeto
File creado anteriormente. Finalmente queda añadir el adjunto al BodyPart, para lo
cual usamos el método setDataHandler() de la interfaz Part, que simplemente añade
al objeto que lo llame, el DataHandler (manejador de datos) que se le pase como
parámetro, y que será quien se encargue de operar sobre los datos y añadirlos en el
BodyPart. El constructor de DataHandler recibe como parámetro la fuente de datos
sobre la que tiene que operar, en este caso el DataSource creado:
DataSource source = new FileDataSource(path);
adjunto2.setDataHandler(new DataHandler(source));
Por último, no se nos debe olvidar indicar el nombre del adjunto usando, para
ello, el método setFileName() de la interfaz Part. A este método se le pasa como
parámetro el nombre del fichero usando el método getName() de la clase File, que
devuelve el nombre del fichero sin la ruta. Con esto el adjunto ya está insertado
correctamente en el BodyPart, y sólo queda añadir éste al Multipart.
adjunto2.setFileName(path.getName());
mp.addBodyPart(adjunto2);
Una vez completado el Multipart, ya se puede rellenar el contenido del
mensaje usando el método setContent(), y enviar el mensaje:
mensaje.setContent(mp);
//Enviamos el mensaje
Transport.send(mensaje);
69
Correo electrónico multiparte
import javax.mail.internet.*;
import java.io.*;
import javax.activation.*;
70
Java a tope: JavaMail en ejemplos
71
Correo electrónico multiparte
72
Java a tope: JavaMail en ejemplos
73
Correo electrónico multiparte
almacenados dentro del objeto Multipart cada uno en una posición, como si fuese un
array:
Part part = mp.getBodyPart(i);
Ahora debe conocerse la disposición o naturaleza del BodyPart
recuperado,para lo que se usa el método getDisposition() de la interfaz Part. La
disposición consiste en un String que informa de cómo debe presentarse al usuario el
objeto de tipo Part, es decir, sirve para diferenciar entre el contenido en sí del
mensaje, los adjuntos y las imágenes. Esta cadena puede valer null, en cuyo caso este
BodyPart porta el contenido del mensaje, o ser distinta de null e igual (ignorando
mayúsculas y minúsculas) a la cadena ATTACHMENT (se trata de un adjunto) o a
la cadena INLINE (se trata de una imagen incrustada en el mensaje). Estas cadenas
están presentes como campos de la interfaz Part:
String disposition = part.getDisposition();
Si la disposición vale null se estará procesando un BodyPart que tiene el
contenido del mensaje. En este ejemplo se contemplan tres posibles casos para tratar
el contenido cuando la disposición vale null:
• multipart/alternative
• text/plain
• text/html
Para hacer estas diferenciaciones, se obtiene el tipo MIME del BodyPart y
se comprueba si es de tipo multipart/alternative o text/plain, usando el método
isMimeType() de la interfaz Part, que informa de si el BodyPart se corresponde o
no con el tipo MIME que se le pasa como parámetro en forma de String:
if (disposition == null) {
if(part.isMimeType("multipart/alternative")||
part.isMimeType("text/plain")) {
En este ejemplo hemos tenido que contemplar el tipo MIME
multipart/alternative (versiones alternativas de la misma información) porque algunos
servidores de correo establecen de este tipo el contenido de los mensajes con adjuntos
cuando los envían. Cuando se trata con un BodyPart que se corresponde con este tipo,
estamos tratando en realidad con un Multipart que contiene varios BodyPart con la
misma información, presentada de distintas formas. Por tanto hay que hacer un
casting al contenido del BodyPart para convertirlo en MultiPart y extraer el primer
BodyPart que lo compone (y posteriormente su contenido), es decir, la primera
versión de la información (generalmente en texto plano, que es el formato que se
desea obtener) y almacenar su contenido en una cadena para visualizarlo a
continuación por pantalla:
String cuerpoMensaje;
if (part.isMimeType("multipart/alternative")) {
Multipart mp2 = (Multipart) part.getContent();
Part part2 = mp2.getBodyPart(0);
cuerpoMensaje = (String)part2.getContent();
74
Java a tope: JavaMail en ejemplos
}
En caso de que la disposición sea null y el tipo MIME text/plain sólo hay que
almacenar su contenido en un String. Y, tanto si se trata de multipart/alternative o
de text/plain, el contenido del mensaje debe visualizarse por pantalla:
else
cuerpoMensaje = (String)part.getContent();
System.out.println(cuerpoMensaje);
El último caso a contemplar cuando la disposición es null es cuando el
contenido del mensaje est en formato HTML. En este caso, al ser un mensaje
multiparte, estará formado por el texto de un mensaje HTML junto con imágenes
incrustadas. Al igual que hicimos cuando se trataba de un mensaje de texto con
formato HTML hay que asegurarse de que el tipo MIME es text/html. Si es así, se
convierte el objeto Part en un objeto MimeBodyPart para poder usar su método
saveFile(), que se encarga de salvar su contenido en disco, en el fichero que se le
indique como parámetro:
else {
if (part.getContentType().indexOf("text/html") != -1) {
MimeBodyPart mbp = (MimeBodyPart)part;
mbp.saveFile("c:/Temp/contenido.html");
}
}
El método saveFile() solo está disponible a partir de la versión 1.4 de
JavaMail, por lo que si se usa una versión anterior hay que sustituir el código anterior
por las siguientes líneas (el casting anterior tampoco sería ya necesario):
DataHandler dh = part.getDataHandler();
OutputStream os = new FileOutputStream ("c:/Temp/contenido.html");
dh.writeTo (os);
os.close();
Si la disposición de esta parte del mensaje es distinta de null, es que no se
trata del cuerpo principal, por lo que hay que comprobar si se está frente a un adjunto
o ante una imagen embebida en el mensaje. En cualquiera de los dos casos se puede
usar el método getFileName() de la interfaz Part para obtener su nombre y grabarlo;
si no tiene nombre lo llamaremos ”adjunto” + posición que ocupa este BodyPart
dentro del Multipart, usando para ello el índice del bucle:
else if ((disposition != null) &&
(disposition.equalsIgnoreCase(Part.ATTACHMENT)||
disposition.equalsIgnoreCase(Part.INLINE)))) {
String nombrePart = part.getFileName();
if (nombrePart == null)
nombrePart = "adjunto" + i;
y, al igual que se hacía antes, convertimos el objeto Part en un objeto MimeBodyPart
para poder usar el método saveFile() de esta clase:
75
Correo electrónico multiparte
76
Java a tope: JavaMail en ejemplos
77
Correo electrónico multiparte
mbp.saveFile("c:/Temp/contenido.html");
}
}
}
// Si entra por aquí es que se trata de un adjunto o de una
// imagen embebida en el mensaje
else if ((disposition != null) &&
(disposition.equalsIgnoreCase(Part.ATTACHMENT) ||
(disposition.equalsIgnoreCase(Part.INLINE)))) {
String nombrePart = part.getFileName();
if (nombrePart == null)
nombrePart = "adjunto" + i;
// Procesar el adjunto o imagen
MimeBodyPart mbp = (MimeBodyPart)part;
mbp.saveFile("c:/Temp/" + nombrePart);
}
}
}
folder.close(false);
store.close();
} catch (MessagingException me) {
System.err.println(me.toString());
} catch (IOException ioe) {
System.err.println(ioe.toString());
}
}
}
78
Java a tope: JavaMail en ejemplos
Capítulo 6
Seguridad
79
Seguridad
if (args.length != 4) {
System.out.println("Ha de enviar dos parámetros\n" +
"java EnviarCorreoAutenticacion " +
"fromAddress toAddress user password");
System.exit(1);
}
String from = args[0];
String to = args[1];
user = args[2];
password = args[3];
Resulta imprescindible añadir a las propiedades del sistema una propiedad
para indicarle al servidor que sí vamos a autenticarnos en él. Esto se hace añadiendo
la siguiente línea en el código:
props.put("mail.smtp.auth", "true");
La principal diferencia entre enviar un correo sin autenticarse y haciéndolo
es, además de indicarlo en las propiedades, que al obtener la sesión con el método
getDefaultInstance() de la clase Session es necesario pasar como segundo
parámetro un objeto de la clase Authenticator.
La clase Authenticator sirve para representar a objetos que proporcionan
cualquier autenticación en una conexión de red. Para usarla hay que codificar una
clase que herede de ella, pues Authenticator es abstracta; básicamente, el único
método que hay que implementar es getPasswordAuthentication(), que es invocado
automáticamente por el sistema para conocer la información de autenticación. Este
método simplemente devolverá un objeto de la clase PasswordAuthentication que
puede crearse de manera sencilla usando el nombre de usuario y la clave que
recibimos como parámetros en el método main(). Esta clase es tan sólo un repositorio
o lugar de almacenamiento, que se usa para almacenar un nombre de usuario junto
a su clave, datos que se pasan a su constructor en el momento de la creación. En la
tabla 6.1 podemos ver a modo de resumen esta clase.
Para realizar los pasos que se han explicado hay que crear una clase anidada,
Método Descripción
80
Java a tope: JavaMail en ejemplos
llamada MiAutenticador, dentro de la clase principal que envía el correo. Esta clase
será la que extenderá a Authenticator y devolverá el objeto
PasswordAuthentication. La clase debe ser estática pues es llamada desde el método
estático main():
private static class MiAutenticador extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
}
Una vez creada esta clase, desde el método main() sólo hay que crear un
objeto Authenticator usando la clase anidada recién creada. Por último se obtiene la
sesión usando el método getDefaultInstance() de la clase Session, pero sin pasar
null como segundo parámetro -pues requerimos autenticarnos- sino que recibirá el
objeto Authenticator creado:
Authenticator auth = new MiAutenticador();
Session sesion = Session.getDefaultInstance(props,auth);
81
Seguridad
}
String from = args[0];
String to = args[1];
// Inicializar las dos variables de clase
user = args[2];
password = args[3];
// Obtener las propiedades del sistema y establecer el servidor
// SMTP que se va a usar
String smtpHost = "smtp.correo.yahoo.es";
Properties props = System.getProperties();
props.put("mail.smtp.host",smtpHost);
// Esta línea indica que vamos a autenticarnos en el servidor SMTP
props.put("mail.smtp.auth", "true");
Authenticator auth = new MiAutenticador();
// Obtener una sesión con las propiedades anteriormente definidas
Session sesion = Session.getDefaultInstance(props,auth);
// Capturar las excepciones
try {
// Crear un mensaje vacío
Message mensaje = new MimeMessage(sesion);
// Rellenar los atributos y el contenido
// Asunto
mensaje.setSubject("Hola Mundo autenticado");
// Emisor del mensaje
mensaje.setFrom(new InternetAddress(from));
// Receptor del mensaje
mensaje.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Rellenar el cuerpo del mensaje
mensaje.setText("Este es el cuerpo del mensaje");
// Enviar el mensaje
Transport.send(mensaje);
} catch (MessagingException e) {
System.err.println(e.getMessage());
}
}
}
82
Java a tope: JavaMail en ejemplos
otro protocolo similar, denominado TLS (Transport Layer Security) y desarrollado por
el IETF (Internet Engineering Task Force) como evolución del SSL, por lo que son
muy similares y muchas veces se hace referencia a uno u otro de forma indistinta.
Java implementa estos protocolos a través de la API JSSE (Java Secure
Socket Extension). Este manual no tiene por objetivo el estudio de esta API en
profundidad, por lo que sólo nos centraremos en el uso de funciones relacionadas con
SSL en JavaMail. Para ver más información sobre JSSE se puede consultar la
dirección http://java.sun.com/products/jsse.
Descargar mensajes de un servidor usando una conexión segura es muy
similar a hacerlo de forma convencional, como veíamos en el epígrafe 3.3. En este
ejemplo se mostrarán las diferencias que aparecen y al final del epígrafe se mostrará
el código completo que realiza esta tarea.
83
Seguridad
Nombre Descripción
Si en vez de POP3 se trabajase con un servidor basado en IMAP sólo hay que
sustituir en estas propiedades la palabra “pop3” por “imap”:
props.put("mail.imap.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
props.put("mail.imap.socketFactory.fallback", "false");
props.put("mail.imap.port", "995");
props.put("mail.imap.socketFactory.port", "995");
84
Java a tope: JavaMail en ejemplos
import java.security.Security;
85
Seguridad
86
Java a tope: JavaMail en ejemplos
87
Seguridad
}
public Socket createSocket( String s, int i, InetAddress inaddr, int j) throws
IOException {
return factory.createSocket( s, i, inaddr, j);
}
public Socket createSocket( String s, int i) throws IOException {
return factory.createSocket( s, i);
}
public String[] getDefaultCipherSuites() {
return factory.getSupportedCipherSuites();
}
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
}
Por último, en lugar de registrar al proveedor de sockets como:
props.put("mail.pop3.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
debe hacerse como:
Security.setProperty( "ssl.SocketFactory.provider",
"SSLSocketFactorySimple");
88
Java a tope: JavaMail en ejemplos
Capítulo 7
Acuse de recibo y prioridades. Búsquedas
7.1 Introducción
En este capítulo se cubren los últimos flecos interesantes de la API JavaMail.
En concreto, se analiza cómo enviar y recibir mensajes de diferentes prioridades y/o
con acuse de recibo, así como las posibilidades de búsquedas de mensajes en el propio
servidor y sin necesidad de efectuar descargas a las aplicaciones clientes. Esto último
puede tener una gran utilidad en servidores IMAP, en los que los mensajes pueden
clasificarse en carpetas antes de ser accedidos por los clientes.
89
Acuse de recibo y prioridades. Búsquedas
90
Java a tope: JavaMail en ejemplos
sólo hay que colocar ésta como segundo parámetro, es decir, no es obligatorio poner
aquí la misma dirección que en el from:
mensaje.addHeader("Disposition-Notification-To",from);
Si en vez de mandar un acuse de recibo, lo que se desea es comprobar si un
mensaje lo tiene o no, se debe usar la siguiente línea de código:
String [] acuseRecibo =
mensajes[i].getHeader ("Disposition-Notification-To");
donde acuseRecibo[0] contiene la dirección a la que hay que enviar la notificación
de lectura, o bien null si el mensaje no contiene acuse de recibo.
Por último, es importante hacer notar que el manejo del acuse de recibo y de
la prioridad (que se verá en el siguiente epígrafe) son muy similares, pues en ambos
casos se trata de incorporar propiedades a la cabecera del mensaje.
91
Acuse de recibo y prioridades. Búsquedas
7.3 Prioridad
La prioridad de un mensaje es simplemente un indicativo que se añade al
mensaje en el momento de enviarlo, para que el destinatario sepa la importancia de
éste. Esto no quiere decir que por modificar la prioridad de un mensaje, éste vaya a
transportarse más rápido o se vaya a leer antes que cualquier otro mensaje sino que,
simplemente, al ver su prioridad el destinatario decidirá si lo abre primero o no. Hay
que indicar que en la mayoría de las ocasiones no se suele usar, y los mensajes se
envían con la prioridad que tengan establecida por defecto.
En este epígrafe se tratan por separado dos casos:
• Establecer la prioridad de un mensaje a la hora de enviarlo
• Obtener la prioridad de un mensaje cuando se recibe
92
Java a tope: JavaMail en ejemplos
se desea establecer la prioridad del mensaje, y como segundo parámetro una cadena
con el valor de dicha prioridad.
JavaMail maneja cinco valores de prioridad, correspondientes a los números
del 1 al 5 en forma de String, donde el número 1 representa la prioridad más alta y
el 5 la prioridad más baja. Será responsabilidad del programa que reciba el mensaje
interpretar estas prioridades y mostrarlas al usuario de una forma adecuada. Si al usar
addHeader() se especifica como valorPrioridad una cadena alfanumérica distinta
de éstas (por ejemplo "0", "6", "a"), será equivalente a añadir un valor de prioridad
3, es decir, prioridad Normal. En la tabla 7.1 pueden verse estos valores y su
significado.
“2” Alta
“3” Normal
“4” Baja
93
Acuse de recibo y prioridades. Búsquedas
94
Java a tope: JavaMail en ejemplos
95
Acuse de recibo y prioridades. Búsquedas
con lo que habrá que acceder a la posición número cero del array y mostrarla por
pantalla.
String [] prioridad = mensajes[i].getHeader ("X-Priority");
String prioridadMensaje;
if (prioridad != null)
prioridadMensaje = prioridad[0];
else
prioridadMensaje = "No tiene prioridad establecida";
De esta forma la prioridad se mostraría por pantalla de la forma numérica
1, 2 , 3, 4 y 5. Para dar una interpretación más clara a estos valores, se puede usar una
sentencia de selección if en cascada, para visualizar por cada valor su correspondiente
significado. Hemos de tener en cuenta que cuando se tiene como valor de prioridad
cualquier valor alfanumérico, distinto de ”1”, ”2”, ”3”, ”4” o ”5”, como por ejemplo
”a”, ”6”, etc., será equivalente a disponer del valor ”3”, es decir, prioridad normal;
por tanto la rama de la sentencia if que controla el ”3”, o cualquier otra cadena
distinta de ”1”, ”2”, ”4” o ”5”, será la misma.
Hay que tener en cuenta también, que cada gestor de correo puede establecer
la prioridad de forma diferente. Por ejemplo, si se envía un mensaje desde Mozilla
Thunderbird con la prioridad más alta, al obtener ésta con JavaMail devolverá la
cadena “1 (Highest)”. Por tanto hay de comprobar que la prioridad más alta contenga
un 1, no que sea igual a 1. A su vez, con esta comprobación surge el problema de que,
si por ejemplo se recibe un valor “15” de prioridad, y tan sólo se comprueba si
contiene un “1”, tal comprobación devolverá true y parecerá que la prioridad es la
más alta, cuando en realidad se corresponde con la normal. Para evitar esto puede
hacerse uso de la clase StreamTokenizer tal y como aparece a continuación:
96
Java a tope: JavaMail en ejemplos
97
Acuse de recibo y prioridades. Búsquedas
7.4 Búsquedas
JavaMail incorpora la capacidad de buscar mensajes que cumplan ciertas
características dentro de una carpeta concreta. Esta búsqueda se hace directamente en
el servidor, sin necesidad de que los mensajes sean descargados a la aplicación cliente.
Así pues, para realizar las búsquedas en JavaMail se utilizan las clases del
paquete javax.mail.search. Concretamente, en lugar de descargar los mensajes de
un objeto Folder, a través del método getMessages(), se empleará el método
search() también de la clase Folder. Este método está sobrecargado y se puede usar
con las dos signaturas que proporciona y que se muestran en la tabla 7.2.
Para realizar búsquedas es necesario construir uno o varios criterios de
filtrado, usando para ello la clase SearchTerm y sus clases herederas, que
implementan métodos de coincidencia de búsqueda específicos. Se trata de una clase
abstracta, que tiene un sólo método, con la cabecera:
public abstract boolean match(Message msg)
Este método, una vez construido un criterio de búsqueda, indica para cada mensaje
que recibe como parámetro si concuerda o no con el criterio a que representa.
98
Java a tope: JavaMail en ejemplos
Método Comportamiento
Tabla 7.2 Métodos para búsqueda de mensajes que proporciona la clase Folder.
99
Acuse de recibo y prioridades. Búsquedas
100
Java a tope: JavaMail en ejemplos
101
Acuse de recibo y prioridades. Búsquedas
102
Java a tope: JavaMail en ejemplos
}
// Buscar los mensajes que tengan un tamaño mayor de 1024 bytes
SearchTerm stSize = new SizeTerm (ComparisonTerm.GT, 1024);
Message [] mensajesTamanyo = folder.search(stSize);
// Procesar los mensajes
System.out.println ("Mensajes con un tamaño mayor de 1024 bytes\n");
for (int i= 0; i < mensajesTamanyo.length; i++) {
System.out.println("Mensaje " + i + ":\n" +
"\tAsunto: " + mensajesTamanyo[i].getSubject() + "\n" +
"\tRemitente: " + mensajesTamanyo[i].getFrom()[0] + "\n" +
"\tFecha de Envío: " + mensajesTamanyo[i].getSentDate() + "\n" +
"\tTamaño: " + mensajesTamanyo[i].getSize() + "\n" +
"\tContenido: " + mensajesTamanyo[i].getContent() + "\n");
}
// Buscar los mensajes que en el campo asunto contengan la palabra hola
// y tengan un tamaño mayor de 1024 bytes
SearchTerm stAnd = new AndTerm (stAsunto,stSize);
Message [] mensajesAnd = folder.search(stAnd);
// Procesar los mensajes
System.out.println ("Mensajes que contienen en el campo asunto la" +
"palabra hola y \n tienen un tamaño mayor de 1024 bytes \n");
for (int i= 0; i < mensajesAnd.length; i++) {
System.out.println("Mensaje " + i + ":\n" +
"\tAsunto: " + mensajesAnd[i].getSubject() + "\n" +
"\tRemitente: " + mensajesAnd[i].getFrom()[0] + "\n" +
"\tFecha de Envío: " + mensajesAnd[i].getSentDate() + "\n" +
"\tTamaño: " + mensajesTamanyo[i].getSize() + "\n" +
"\tContenido: " + mensajesAnd[i].getContent() + "\n" );
}
// Cerrar el Folder y el Store
folder.close(false);
store.close();
} catch (MessagingException me) {
System.err.println(me.toString());
} catch (IOException ioe) {
System.err.println(ioe.toString());
}
}
}
103
Java a Tope:
( JavaMail en ejemplos )
ISBN 84-690-0697-5
90000 >
9 788469 006979