Trabajando con Android, muchos buscamos enviar mensajes de correo electrónico usando un servidor SMTP, utilizando autenticación y cifrado, dentro de de la propia aplicación android de forma que sea completamente transparente para el usuario.
Por desgracia javax.mail en Android no es una buena opción, ya que depende de las clases awt (problemas por la herencia) y no siempre funciona:no en vano ,de hecho algunas personas han tratado de adaptar la librería dado que no ser requiere todo el paquete awt , pero lamentablemente han tenido poco éxito con eso; !y eso sin mencionar a quienes que refactorizando javax.mail para Android (que lleva muchos años, sin ningún mantenimiento)!.
Apache Commons es un conjunto de proyectos de Apache Software Foundation, que originalmente formaron parte de Jakarta Project. El propósito de estos proyectos consiste en proveer componentes de software Java reutilizables, en código abierto. Según su grado de madurez y actividad, los proyectos se agrupan en las categorías: proper, sandbox o dormant.
Otra opción mas pragmática pues es usar Apache Commons ,puesto que la comunidad ha añadido un SMTPSClient y un AuthenticatingSMTPClient para el cliente SMTP original ,el cual con la aplicación de un pequeño parche para SSL y autenticación, se puede incrustar esta biblioteca en su aplicación para Android sin necesitar dependencias transitivas para enviar correo mediante la autenticación sobre una capa segura.
Autenticación SMTP y STARTTLS
STARTTLS es una extensión a los protocolos de comunicación de texto plano, que ofrece una forma de mejorar desde una conexión de texto plano a una conexión cifrada (TLS o SSL) en lugar de utilizar un puerto diferente para la comunicación cifrada.
El puerto utilizado para envíos de correos es generalmente 25 o el puerto 587 alternativo por lo que lo que se trata es de conectar al servidor SMTP en una conexión simple, se piden los comandos disponibles, y si se soporta STARTTLS,se usa ,y el resto de la comunicación está cifrado.
Ahora tomemos el ejemplo de gmail, ya que el servidor es smtp.gmail.com , como soporta autenticación y STARTTLS podemos enviar fácilmente correos si importamos la libreria Apache Commons
Un ejemplo de código funcional que podemos probar desde Android Studio es el siguiente:
com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.appindexing.Thing;
import com.google.android.gms.common.api.GoogleApiClient;
import org.apache.commons.net.smtp.AuthenticatingSMTPClient;
import org.apache.commons.net.smtp.SMTPClient;
import org.apache.commons.net.smtp.SMTPReply;
import org.apache.commons.net.smtp.SimpleSMTPHeader;
public void sendEmail()
{ String hostname = «smtp.gmail.com»;
int port = 587; String password = «xxxxxxxxx»;
// aqui necesitamos la pwd de al cuenta de gmail
String login = «[email protected]»;
//aqui necesitamos la cuenta de gmail desde //la que enviaremos los email desde la app
String from = login;
//aqui ponemos el nombre del correo de envío
// es el enviante
String subject = «subject«;
//asunto del correo
String text = «message»;
//texto del correo try
{ AuthenticatingSMTPClientclient = new AuthenticatingSMTPClient();
String to = «[email protected]»;
//destino del correo
// optionally set a timeout to have a faster
//feedback on errors client.setDefaultTimeout(10 * 1000);
// you connect to the SMTP server client.connect(hostname, port);
// you say ehlo and you specify the host you are connecting from, //could be anything client.ehlo(«localhost»);
// if your host accepts STARTTLS, we’re good everything will be encrypted,
// otherwise we’re done here
if (client.execTLS())
{ client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password);
checkReply(client); client.setSender(from);
checkReply(client);
client.addRecipient(to);
checkReply(client);
Writer writer = client.sendMessageData();
if (writer != null)
{ SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject); writer.write(header.toString()); writer.write(text); writer.close();
if (!client.completePendingCommand())
{
// failure
throw new Exception(«Fallo al enviar email » + client.getReply() + _ client.getReplyString()); } }
else
{ throw new Exception(«Fallo al enviar email » + client.getReply() + _ client.getReplyString()); } }
else
{ throw new Exception(«STARTTLS no fue aceptado » + client.getReply() +_ client.getReplyString());
} }
catch (Exception e) {
// some other problem
Toast.makeText(MainActivity.this, «Problema enviado email.», _ Toast.LENGTH_SHORT).show(); } }
private static void checkReply(SMTPClient sc) throws Exception
{ if (SMTPReply.isNegativeTransient(sc.getReplyCode()))
{ throw new Exception(«Transient SMTP error » + sc.getReply() + sc.getReplyString()); }
else if (SMTPReply.isNegativePermanent(sc.getReplyCode()))
{ throw new Exception(«Permanent SMTP error » + sc.getReply() + sc.getReplyString()); } }
//* * ATTENTION: This was auto-generated to implement the App Indexing API. * // See https://g.co/AppIndexing/AndroidStudio for more information. */
public Action getIndexApiAction()
{ Thing object = new Thing.Builder() .setName(«Main Page»)
// TODO: Define a title for the content shown.
// TODO: Make sure this auto-generated URL is correct.
.setUrl(Uri.parse(«http://[ENTER-YOUR-URL-HERE]»)) .build();
return new Action.Builder(Action.TYPE_VIEW) .setObject(object) ._ setActionStatus(Action.STATUS_TYPE_COMPLETED) .build(); }
@Override public void onStart()
{ super.onStart();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information. client2.connect(); AppIndex.AppIndexApi.start(client2, getIndexApiAction()); }
@Override public void onStop()
{ super.onStop();
// ATTENTION: This was auto-generated to implement the App Indexing API
. // See https://g.co/AppIndexing/AndroidStudio for more information. AppIndex.AppIndexApi.end(client2,
getIndexApiAction());
client2.disconnect(); }
No hay mucho que añadir aquí, por supuesto, la manipulación de excepciones podría ser optimizado si utiliza sus propias clases de excepción y por ejemplo tanto el login como el pwd de gmail pueden ser capturados en los ajustes de la aplicación para personalizarlo a voulntas.
Autenticación SMTP y SSL con Commons Net
Algunos servidores SMTP están configurados para aceptar sólo SSL por lo hay que asegurar la comunicación antes de emitir cualquier comando al servidor siendo generalmente el puerto usado el 465.
Por ejemplo LaPoste.net oferece cuentas gratuitas de correo electrónico ofrecidas por la publicación francesa, asi que enviar correos desde ahi seria similar a esto:
public void sendEmail() throws Exception { String hostname = "smtp.laposte.net"; int port = 465; String password = "password"; String login = "firstname.lastname"; String from = login + "@laposte.net"; String subject = "subject" ; String text = "message"; // this is the important part : you tell your client to connect using //SSL right away AuthenticatingSMTPClient client = new AuthenticatingSMTPClient("TLS",true); try { String to = "[email protected]"; // optionally set a timeout to have a faster feedback on errors client.setDefaultTimeout(10 * 1000); client.connect(hostname, port); client.ehlo("localhost"); client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password); checkReply(client); client.setSender(from); checkReply(client); client.addRecipient(to); checkReply(client); Writer writer = client.sendMessageData(); if (writer != null) { SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject); writer.write(header.toString()); writer.write(text); writer.close(); if(!client.completePendingCommand()) {// failure throw new Exception("Failure to send the email "+ client.getReply() +_ client.getReplyString()); } } else { throw new Exception("Failure to send the email "+ client.getReply() + _ client.getReplyString()); } } catch (Exception e) { throw e; } finally { client.logout(); client.disconnect(); } }
No repitimos el método checkReply () aquí, ya que es el mismo para ambos fragmentos de código
Usted habrá notado que el uso de SSL de inmediato significa que usted no tiene que buscar la respuesta execTls () (de hecho no funcionará si lo hace).
Dependencias
Eso es todo; Si desea hacer que estos ejemplos funcionen en su entorno, debe descargar la liberia apache commons net 3.3 jar y agregar el jar apache commons net 3.3 a su proyecto