
Una de las nuevas características de Adobe Air 2.0 es la conexión a servidores mediante sockets encriptados (SSL).
Uno de los servicios mas comunes que utiliza una conexión encriptado son los proveedores de correo usando IMAP.
Para este ejemplo vamos a conectarnos al servidor IMAP de Gmail y obtener el numero de mensajes nuevos y mensajes en total que existen en nuestra cuenta.
Para fines prácticos de este ejemplo necesitamos:
- Una cuenta de Gmail, con IMAP habilitado
- FlashBuilder
- Adobe AIR 2.0
Lo primero es crear una interfaz sencilla en donde se pueda insertar el nombre de usuario, la contraseña, campos de output y un boton para iniciar el proceso.
[xml light="true"] <s:Label x="10" y="17" text="Gmail username:"/><s:Label x="10" y="45" text="Gmail password:"/>
<s:TextInput y="10" id="username_txt" change="_username = username_txt.text" right="10" left="120"/>
<s:TextInput y="40" id="password_txt" change="_password = password_txt.text" displayAsPassword="true" right="10" left="120"/>
<s:Button y="70" label="Tell me!!" click="start()" right="10"/>
<s:Label x="10" y="100" width="280" id="result_txt"/>
<s:TextArea id="out_txt" editable="false" right="10" bottom="10" top="130" left="10"/>
[/xml]
El resultado de esto se ve de esta forma:
Despues de tener nuestra interfaz vamos a declarar todas la variables y constantes que vamos a necesitar:
[as3 light="true"] /******************************************************************* Datos de la cuenta (Gmail IMAP)
* ***************************************************************/
private static const INCOMING_SERVER:String = "imap.gmail.com";
private static const PORT:int = 993;
private var _username:String = "";
private var _password:String = "";
/******************************************************************
* Socket && Stuff
* ***************************************************************/
private var _server:Socket;
private var _buffer:ByteArray;
private var _action:String;
private var _totalMessages:String;
private var _newMessages:String;
/******************************************************************
* Respuestas del servidor (para filtar el contenido)
* http://tools.ietf.org/html/draft-gahrns-imap-namespace-00
* ****************************************************************/
private static const CONNECT:String = "*";
private static const LOGIN:String = "A002";
private static const STATUS:String = "A006";
private static const CRLF:String = "rn";
private var _regSearch:RegExp = RegExp(""+_action+"rn");
[/as3]
Aqui hay un par de cosas que debo resaltar:
[as3 light="true"] private var _buffer:ByteArray;[/as3]
En esta variable almacenaremos las respuestas que nos envié el servidor.
[as3 light="true"] private var _regSearch:RegExp = RegExp(""+_action+"rn");[/as3]
Con esta expresion regular verificaremos si el mensaje que recibimos del servidor es util para nosotros.
[as3 light="true"] private static const CONNECT:String = "*";private static const LOGIN:String = "A002";
private static const STATUS:String = "A006";
[/as3]
Estas constantes son los identificadores de los mensajes que se mandan desde y hacia el servidor para saber mas de estos identificadores recomiendo esta lectura http://tools.ietf.org/html/draft-gahrns-imap-namespace-00
Ahora nos queda inicializar nuestro socket y crear los eventos que usaremos para comunicarnos con el servidor
[as3 light="true"] _server = new SecureSocket();_server.addEventListener(Event.CONNECT, onConnect);
_server.addEventListener(Event.CLOSE, onClose);
_server.addEventListener(ProgressEvent.SOCKET_DATA, onData);
_server.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
[/as3]
El único cambio con respecto a trabajar con Sockets sin encriptar es el constructor de la clase en lugar de usar
[as3 light="true"] _server = new Socket();[/as3]
Ahora debemos utilizar
[as3 light="true"] _server = new SecureSocket();[/as3]
Así de simple; Bien, hemos seteado los listeners para CONNECT, SOCKET_DATA, CLOSE y IO_ERROR vamos a concentranos solamente en SOCKET_DATA que es el evento que se dispara al momento de recibir un mensaje del servidor.
[as3 light="true"]
private function onData(e:ProgressEvent):void
{
out_txt.text += "Data " + e+"n";
var socket:Socket = e.target as Socket;
var bufferString:String;
socket.readBytes(_buffer, _buffer.length, socket.bytesAvailable);
bufferString = _buffer.toString();
out_txt.text += "SERVER: " + _buffer.toString()+"n";
if ( bufferString.search( _regSearch ) )
{
out_txt.text += "Es una respuesta valida parsean";
parseResponse();
}
cursorManager.removeBusyCursor()
}
[/as3]
En esta función recibimos el evento y tomamos el contenido del mensaje mediante el método readBytes y lo almacenamos en la variable _buffer
[as3 light="true"] socket.readBytes(_buffer, _buffer.length, socket.bytesAvailable);[/as3]
Verificamos que el mensaje corresponda a nuestra petición, en este caso solo usamos 3 peticiones CONNECT, LOGIN, STATUS y si el mensaje es correcto parseamos el contenido.
El primer mensaje que recibimos del servidor es el de conexión
SERVER: * OK Gimap ready for requests from 189.216.40.184 14if7528374pzk.62
Una vez que estemos conectados mandamos nuestras credenciales para que el servidor nos autentique y cambiamos el mensaje a LOGIN indicando que estamos esperando el mensaje de respuesta de la solicitud de logueo.
[as3 light="true"] if ( _action == CONNECT ){
out_txt.text += "Es es tag de conexion manda loginn";
_server.writeUTFBytes(LOGIN + " LOGIN " + _username + " " + _password + CRLF);
_action = LOGIN;
_server.flush();
return;
}
[/as3]
Se envia el mensaje y de inmediato recibimos la respuesta del servidor, en caso de login correcto:
A002 OK tmeister@gmail.com authenticated (Success)
Si es incorrecto:
A002 NO [ALERT] Invalid credentials (Failure)
Con esto es muy fácil saber el resultado de la operación, usemos indexOf para saber si la palabra “OK” existe
[as3 light="true"] if ( _action == LOGIN ){
out_txt.text += "Es la respuesta del loginn";
if( bufferString.indexOf("OK") != -1 )
{
out_txt.text += "El login es correcto, estamos autenticados – Pedir correos sin leern";
_action = STATUS;
_server.writeUTFBytes(STATUS + " STATUS inbox (MESSAGES UNSEEN)" + CRLF);
_server.flush();
}else
{
out_txt.text += "Ooops!! Las credenciales no son validas.n";
Alert.show("Ooops!! Las credenciales no son validas.", "Error");
_server.close();
}
return;
}
[/as3]
Excelente estamos autenticados, Ahora que? solo nos falta pedir el estatus de la cuenta pidiendo los mensajes totales y los mensajes nuevos de la carpeta Inbox
[as3 light="true"] _server.writeUTFBytes(STATUS + " STATUS inbox (MESSAGES UNSEEN)" + CRLF);[/as3]
El ultimo paso es recibir el mensaje del servidor con la información que pedimos y parsearla, al ser texto solo tenemos que usar indexOf y substring para acceder a lo que nos interesa
STATUS “inbox” (MESSAGES 61298 UNSEEN 5)
[as3 light="true"] if( _action == STATUS ){
out_txt.text += "Es la respuesta del Status " + bufferString+"n";
var startSub:int = bufferString.indexOf("(");
var endSub:int = bufferString.indexOf(")");
var message:String = bufferString.substring(startSub+1, endSub);
var slides:Array = message.split(" ");
var count:int = 0;
for each( var slide:String in slides )
{
switch( slide )
{
case "MESSAGES":
_totalMessages = slides[count + 1];
break;
case "UNSEEN":
_newMessages = slides[count + 1];
break;
}
count++;
}
result_txt.text = _newMessages + " mensajes nuevos, de " + _totalMessages + " en total";
out_txt.text += _totalMessages;
out_txt.text += _newMessages;
_action = null;
_server.close();
}
}
[/as3]
Sin duda, lo mas complejo al momento de trabajar con Sockets es saber utilizar e implementar el protocolo de comunicación pero esa ya es otra historia.
Por ultimo les dejo el código completo
[as3] <?xml version="1.0" encoding="utf-8"?><s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo"
creationComplete="init(event)"
width="300" height="250">
<fx:Script>
<![CDATA[
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.net.SecureSocket;
import flash.net.Socket;
import flash.utils.ByteArray;
import mx.controls.Alert;
import mx.events.FlexEvent;
private static const INCOMING_SERVER:String = "imap.gmail.com";
private static const PORT:int = 993;
private var _username:String = "";
private var _password:String = "";
private var _server:Socket;
private var _buffer:ByteArray;
private var _action:String;
private var _totalMessages:String;
private var _newMessages:String;
private static const CONNECT:String = "*";
private static const LOGIN:String = "A002";
private static const STATUS:String = "A006";
private static const CRLF:String = "rn";
private var _regSearch:RegExp = RegExp(""+_action+"rn");
protected function init(event:FlexEvent):void
{
_server = new SecureSocket();
_server.addEventListener(Event.CONNECT, onConnect);
_server.addEventListener(Event.CLOSE, onClose);
_server.addEventListener(ProgressEvent.SOCKET_DATA, onData);
_server.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
_buffer = new ByteArray()
}
private function onConnect(e:Event):void
{
out_txt.text += "Conexion establecida " + e.toString()+"n";
}
private function onClose(e:Event):void
{
out_txt.text += "Conexion cerrada " + e+"n";
}
private function onData(e:ProgressEvent):void
{
out_txt.text += "Data " + e+"n";
var socket:Socket = e.target as Socket;
var bufferString:String;
socket.readBytes(_buffer, _buffer.length, socket.bytesAvailable);
bufferString = _buffer.toString();
out_txt.text += "SERVER: " + _buffer.toString()+"n";
if ( bufferString.search( _regSearch ) )
{
out_txt.text += "Es una respuesta valida parsean";
parseResponse();
}
cursorManager.removeBusyCursor()
}
private function onIOError(e:IOErrorEvent):void
{
out_txt.text += "IOError " + e+"n";
}
private function parseResponse():void
{
var bufferString:String = _buffer.toString();
_buffer.clear();
if ( _action == CONNECT )
{
out_txt.text += "Es es tag de conexion manda loginn";
_server.writeUTFBytes(LOGIN + " LOGIN " + _username + " " + _password + CRLF);
_action = LOGIN;
_server.flush();
return;
}
if ( _action == LOGIN )
{
out_txt.text += "Es la respuesta del loginn";
if( bufferString.indexOf("OK") != -1 )
{
out_txt.text += "El login es correcto, estamos autenticados - Pedir correos sin leern";
_action = STATUS;
_server.writeUTFBytes(STATUS + " STATUS inbox (MESSAGES UNSEEN)" + CRLF);
_server.flush();
}else
{
out_txt.text += "Ooops!! Las credenciales no son validas.n";
Alert.show("Ooops!! Las credenciales no son validas.", "Error");
_server.close();
}
return;
}
if( _action == STATUS )
{
out_txt.text += "Es la respuesta del Status " + bufferString+"n";
var startSub:int = bufferString.indexOf("(");
var endSub:int = bufferString.indexOf(")");
var message:String = bufferString.substring(startSub+1, endSub);
var slides:Array = message.split(" ");
var count:int = 0;
for each( var slide:String in slides )
{
switch( slide )
{
case "MESSAGES":
_totalMessages = slides[count + 1];
break;
case "UNSEEN":
_newMessages = slides[count + 1];
break;
}
count++;
}
result_txt.text = _newMessages + " mensajes nuevos, de " + _totalMessages + " en total";
out_txt.text += _totalMessages;
out_txt.text += _newMessages;
_action = null;
_server.close();
}
}
private function start():void
{
out_txt.text = "iniciando Conexionn";
if( _server != null && _server.connected )
{
_server.close();
}
if( _username.length && _password.length )
{
_action = CONNECT;
_server.connect(INCOMING_SERVER, PORT);
cursorManager.setBusyCursor();
}else
{
Alert.show("Escribe tus credenciales..", "Error");
}
}
]]>
</fx:Script>
<s:Label x="10" y="17" text="Gmail username:"/>
<s:Label x="10" y="45" text="Gmail password:"/>
<s:TextInput y="10" id="username_txt" change="_username = username_txt.text" right="10" left="120"/>
<s:TextInput y="40" id="password_txt" change="_password = password_txt.text" displayAsPassword="true" right="10" left="120"/>
<s:Button y="70" label="Tell me!!" click="start()" right="10"/>
<s:Label x="10" y="100" width="280" id="result_txt"/>
<s:TextArea id="out_txt" editable="false" right="10" bottom="10" top="130" left="10"/>
</s:WindowedApplication>
Descarga
[drain file 11 show dTemplate]Espero les sea de ayuda y nos estamos leyendo.

esto esta genial!! gracias por compartir ^^
[...] se presento la beta 2 de AIR 2 con interesantes mejoras en cuanto a la impresión, soporte para comunicación con sockets y mejoras en el input de texto. También en labs se dio a conocer Flash Player 10.1 como beta al [...]