El Delta del Ebro en fotos

Lástima que el cambio de hora nos privase de disfrutar más de estos bellos parajes. El Delta del Ebro produce aproximadamente la misma cantidad de arroz que Valencia así que los paisajes me resultan familiares: arrozales, arrozales y más arrozales, el Delta es a esta zona lo que la Albufera a Valencia.

Aquí hay una mezcla de campo y playa que descubre unos paisajes alucinantes, os los dejo en fotos.

Delta del Ebro

Delta del Ebro

Delta del Ebro

Delta del Ebro

Delta del Ebro

Delta del Ebro

 Delta del Ebro

Delta del Ebro

Delta del Ebro

 Delta del Ebro

Delta del Ebro

Delta del Ebro

Delta del Ebro

Comer en el Delta del Ebro

Para comer, y tras dar unas cuentas vueltas ya que parece que la mayoría de restaurantes cierran en invierno, dimos con Restaurante Gracia, en Deltebre, y salimos bastante contentos. Pedimos de primeros chipirones y anguila con salsa y a continuación un arroz con bogavante. Aunque estaba todo muy bueno y el arroz en su punto, la sorpresa nos esperaba en los postres, tanto que no llegué a tiempo para las fotos: tiramisú de la casa y flan blanco casero, fantásticos los dos.

ChipironesAnguila en salsa en el Delta del Ebro

Arroz con bogavante en el Delta del Ebro

Nos encantó la zona hasta tal punto que hemos pensado volver con más tiempo y unas bicis para recorrerla tranquilamente y apreciar todos los rincones ocultos que se nos escaparon.

Y seguimos con la falta de personal técnico cualificado

Tenía pendiente hacer un comentario desde la semana pasada.

¿Por qué se empeña Enrique Dans en contradecir a cientos de programadores? ¿En serio es creíble el argumento de que no hay personal adecuadamente cualificado en España? Suelo leer a Enrique y creo que es un tío normalmente serio y coherente en sus argumentaciones (estés o no de acuerdo con ellas), pero es que con ésta no puedo, no la veo reflejada por ningún lado en el mundo real.

Mi experiencia me dice lo contrario. Claro que ya no se reciben cientos de curriculums para una oferta de trabajo, pero es que aquello era lo anormal. ¿Acaso recibes docenas y docenas de currilums para un puesto importante de departamentos tradicionales (comercial, marketing, rrhh…)? ¿Por qué esperas entonces recibirlos para un puesto de programador? Un programador es, desde la base de su formación, personal altamente cualificado y han estado puteados durante muchos años. Resulta que ahora cuando comienza a verse un poco de dignidad en el sector, no hay gente, curioso ¿no?, es decir, ahora que queremos jornadas normales y salarios decentes, faltan programadores. Recuerda que, ante todo, buscas a la/s persona/s que diseñarán y construirán tu producto…

Lo he comentado ya en otras ocasiones pero creo que mis razones siguen siendo completamente válidas:

Sobran más palabras ¿no?.

Generador de códigos de barras en AS3

Hoy vamos a ver un ejemplo sobre cómo generar códigos de barras al vuelo utilizando ActionScript3. Un código de barras es la representación mediante líneas paralelas de distinto grosor de un código numérico o alfanumérico. Esta representación lineal los hace de fácil interpretación para un lector láser. No creo que se necesiten más explicaciones, todo el mundo sabe lo que es un código de barras.

Los códigos de barras lineales pueden ser de distintos tipos, en el ejemplo de hoy generaremos EAN y C128. EAN es el más utilizado a nivel mundial y el utilizado en España, así que será el que nos ocupe hoy. Mi trabajo está basado en la clase para generar códigos C128 de Friedrich Dimmel (lo siento, no localizo el link original). La clase que genera el código EAN no es ni más ni menos que la reescritura en AS3 de la que trae phpBarcode  y adaptada para generar la secuencia de líneas que se dibujan bajo la lógica de Friedrich Dimmel en vez de generar una imagen con GD como hace la clase original. Como casi siempre, este pequeño desarrollo lo hice hace un par de años para un sistema de control de stocks, no me critiquéis mucho el código :P.
swfobject.embedSWF(“/wp-content/uploads/2008/10/codigosdebarras1.swf”, “codigosdebarras”, “470”, “350”, “9.0.000”);

Flash del generador

Un código EAN son 13 dígitos, 12 de información (código de país, empresa y producto) y un dígito de control que ayuda a verificar que la decodificación posterior es correcta.
Para crear la imagen de nuestro código simplemente debemos llamar a la clase generadora  con los parámetros:

  • Código: la cadena que queremos convertir a código de barras.
  • Alto de la imagen generada.
  • Tipo de código (EAN o C128).
  • Mostrar el texto del código (opcional).
  • Color de fondo (opcional).

Por ejemplo:

BarcodeGenerator.generateBarcode("3456789012345", 50, "EAN");

Como hemos visto, el último dígito es de control, con lo que con introducir los 12 dígitos con valor será suficiente, la clase generará el treceavo automáticamente. No voy a explicar detalladamente como generar los códigos EAN, hay suficiente literatura acerca del algoritmo googleando un poco y para ver el resultado lo mejor es que examinéis el código fuente que os dejo más abajo. Creo que lo más curioso e importante es ver cómo generamos la imagen una vez tenemos generado el código de líneas.

public static function generateBarcode(code:String="0", height:Number=60, tipo:String="EAN", showText:Boolean=true, backgroundColor:uint=0xffffff):Canvas {
    var can:Canvas = new Canvas();
    can.horizontalScrollPolicy = "off";
    can.verticalScrollPolicy = "off";
    can.height = height;
    can.setStyle("backgroundColor", backgroundColor);
    var sBarcode:Shape;
    var rect:Rectangle;
    var bd:BitmapData;
    var img:Image;
    var bitmap:Bitmap;
    if(tipo=="EAN"){
      var barcode:BarcodeEAN = new BarcodeEAN();
      barcode.code = code;
      barcode.barHeight = height;
      if(showText) {
        barcode.barHeight -= 15;
      }
      var k:Object=barcode.barcode_encode_ean(code);
      if(!k.bars) return new Canvas();
      var bars:String=k.bars;
      var text:String=k.text;
      sBarcode = barcode.generateBarcode();
      rect = new Rectangle(0, 0, (sBarcode.width / 2) + 1, barcode.barHeight);
      bd = getBitmapData(sBarcode, rect);
      img = new Image();
      bitmap = new Bitmap(bd);
      img.source = bitmap;
      can.addChild(img);
      var chars:Array=text.split(" ");
      var n:String;
      var v:String;
      var total_y:Number=40;
      for(n in chars){
        v=chars[n];
        if (StringUtil.trim(v)){
          var inf:Array=v.split(":");
          var fontsize:Number=inf[1]/1.8;
          var fontheight:Number=30;
          var label:Label = new Label();
          label.text = inf[2];
          label.x = Number(inf[0])-2;
          label.y = fontheight;
          label.setStyle("fontSize", 9);
          label.setStyle("paddingBottom", 0);
          label.setStyle("paddingLeft", 0);
          label.setStyle("paddingRight", 0);
          label.setStyle("paddingTop", 0);
          can.addChild(label);
        }
      }
    }
    return can;
}

En realidad es tan sencillo como crear una imagen a partir de lo que nos devuelve la clase generadora EAN (un objeto de tipo Shape). Finalmente si queremos imprimir el código en la propia imagen tendremos que poner cada letra en el sitio que le toca, no van en cualquier lado.

¿Qué tal si hacemos el proceso contrario con un lector casero?

Os dejo el código fuente del proyecto.

El Maestrazgo de Teruel

Una serie de compromisos y casualidades nos llevaron a pasar un fin de semana en esta zona de la provincia de Teruel y, francamente, quedamos encantados.

El Maestrazgo es una comarca histórica y natural que se extiende por el norte de la provincia de Castellón y por el sudeste de Teruel.  En esta ocasión la ruta de fin de semana comprende sábado y domingo aunque también puedes completarla en dos días de fines de semanas distintos. Nosotros lo hicimos en un fin de semana y este fue el plan de nuestro viaje:

Ampliar

A – Castellote

A unos 220km de Valencia comenzamos nuestro fin de semana en Castellote, encantador pueblo de la provincia de Teruel. Nos alojamos en el Hostal Castellote un lugar tranquilo y muy agradable. El hostal tiene, además, un restaurante de calidad con comida casera de la zona a un precio asequible. Nos gustó mucho para pasar un fin de semana tranquilo.

Ya desde la llegada te quedas con la boca abierta.  En la foto de abajo podéis ver como es la entrada al pueblo. ¿No la véis? La carretera que se ve en esa enorme pared rocosa se pierde dentro de ella a través de un túnel de 300m de largo que te topas de frente tras una curva sin esperarlo, un túnel escavado artesanalmente y sin modernas perforadoras allá por 1899, todo un hito histórico para el pueblo. Por mucho que lo cuente no se puede entender la sensación de encontrarte este túnel, cruzarlo y hallarte en medio del pueblo.

Santuario del Llovedor

Una vez dentro sientes la serenidad y tranquilidad de los pueblos de antes. Puedes callejear por sus empedradas cuestas, llegar a la iglesia, la torre templaria o a los restos del antiguo castillo templario, destruido durante las Guerras Carlistas en una cruenta batalla donde los castellotanos demostraron su valor.

Castellote

CastelloteCastelloteCastellote

Justo a la entrada del pueblo, y tomando el desvío hacia Molinos, hay un pequeño camino a mano izquierda mal señalizado y en peor estado que te dirige a la Ermita del Llovedor, donde se venera la Virgen del Agua. Las vistas son sencillamente alucinantes desde aquí. La foto de entrada al túnel la tomé desde este paraje, pero esta de abajo con la ermita en medio tampoco tiene desperdicio alguno.

Santuario del Llovedor

Justo encima de la ermita hay una caída de agua desde lo alto de la montaña que en primavera debe ser espectacular, en esta época apenas caía.

B – Grutas de Cristal

Continuando por la misma carretera durante aproximadamente una hora (no es una autopista 😛 ) llegamos al pueblo de Molinos, pero en vez de desviarnos a mano derecha hacia el pueblo lo hacemos a la izquierda siguiento la señal a las Grutas de Cristal a donde llegamos tras unos quince minutos.

Por un módico precio de 7€ accedemos en visita guiada a estas impresionantes cuevas declaradas Monumento Natural por el Gobierno de Aragón en 2006.

Grutas de Cristal, Molinos, Teruel

Grutas de Cristal, Molinos, TeruelGrutas de Cristal, Molinos, TeruelGrutas de Cristal, Molinos, Teruel

Este Espacio Natural Protegido destaca por su completo sistema cárstico formado por una red de galerías y salas subterráneas muy peculiares por sus fomas. Estas formaciones conectan con el exterior a través de las Cuevas de las Graderas o Grutas de Cristal de la Cueva de las Baticambras, constituyendo uno de los mejores ejemplos de este tipo de formaciones de todo el país.

Dentro de la gran variedad de precipitados de carnonatos como son las estalactitas, estalagmitas, columnas, cortinas y cascadas, destaca la formación de estalactitas excéntricas, de crecimiento horizontal. Debido a sus peculiares características las Grutas de Cristal son un Punto de Interés Geológico de importancia nacional.

En la zona destaca la existencia de numerosa fauna fósil, asi como dos asentamientos humanos, uno de los cuales corresponde al Hombre de Molinos, homínido mas antiguo encontrado en Aragón, fechado en unos 25.000 años.

Grutas de Cristal, Molinos, Teruel

Grutas de Cristal, Molinos, TeruelGrutas de Cristal, Molinos, TeruelGrutas de Cristal, Molinos, Teruel

La visita dura alrededor de 45 minutos y son básicamente dos salas comunicadas por unos pasillos plagados todos de formas y figuras esculpidas por el agua y los minerales con el paso del tiempo. En una de las fotos podéis ver lo que un día fue una cascada de agua, hoy, por supuesto, seca, pero que conserva los surcos, lo que demostraría que la cueva estuvo ocupada por el agua.

El guía era un hombre pintoresco 😛 , pero acompañaba el paseo con anéctodas e historia de las cuevas. Muy interesantes sus explicaciones acerca de la formación de las estalacticas: 1cm cada 100 años, simplemente incríble el tamaño que tienen algunas. Al parecer ya se han encontrado muchísimas más cuevas, falta que encuentren la manera de comunicarlas entre sí para hacerlas visitables.

C – Molinos

Saliendo de las cuevas nos dirigimos de nuevo a la carretera principal y bajamos, esta vez sí, al pueblo. Molinos es un lugar tremendamente tranquilo donde se respira sosiego y relax.

Molinos, Teruel

Molinos, TeruelMolinos, TeruelMolinos, Teruel

Lo más destacable de Molinos es el impresionante Barranco de San Nicolás, salto de agua que divide literalmente el pueblo en dos.

Molinos, Barranco de San Nicolas, Teruel

Molinos, Barranco de San Nicolas, TeruelMolinos, Barranco de San Nicolas, Teruel

D – Pantano de Santolea

Regresamos de Molinos por la misma carretera por la que fuimos y nos desviamos hacia Dos Torres de Mercader. Ahora seguimos esa carretera (por llamarle de algún modo) hasta el final durante una hora aproximadamente regresando de nuevo a Castellote. Si lo tuyo son las autopistas, ni lo intentes, sin líneas, sin quitamiedos y en muchos tramos no cabe más que tu coche. Eso sí, lo más probable es que estés tu sólo 😛 . El estado de la vía se compensa con el bello paisaje, un regalo para tus ojos.

Pantano de Santolea, Teruel

Pantano de Santolea, TeruelPantano de Santolea, TeruelPantano de Santolea, Teruel

La carretera discurre paralela al pantano con lo que las vistas son espectaculares al estar éste rodeado por unas impresionantes paredes de roca.

Por toda esta zona no hay cobertura de móvil, ni casas, ni pasan más coches que el tuyo. En este caso iba yo sólo en mi coche y en determinados momentos puedes sentirte intranquilo por si ocurre algo.

Nuestra ruta del sábado termina regresando a Castellote y cenando en el hostal.

F – Cuevas de Cañart

El domingo comenzamos  remontando el río Guadalote por la carretera que atraviesa Cuevas de Cañart. Al llegar a este punto parece que se termine la carretera, pero al otro lado del pueblo continúa. De nuevo no te esperes dos carriles, líneas horizontales o quitamiedos, no deja de ser una pista forestal asfaltada.

Justo al cruzar Cuevas de Cañart te topas con una fantástica vista, lo que un día fue el Convento de Servitas (1790), monumental edificación en ruínas, que aún hoy en día guarda su esplendor.

Cuevas de Cañart

Cuevas de CañartCuevas de CañartCuevas de Cañart

Llegó a estar habitado por 100 monjes hasta que la orden fue diseminada. Posteriormente el convento sería bombardeado durante las Guerras Carlistas. En medio de las ruínas todavía pueden apreciarse restos de lo que fueron sus bajorelieves además de la fachada.

Cuevas de Cañart

G – Órganos de Montoro

Siguiendo la carretera y sin desviarnos del camino a pesar de que en un momento éste pierde el asfalto y se convierte en una pista de tierra, llegamos a este tramo donde realmente vamos al lado del Guadalote, lo que nos deja unos preciosos paisajes otoñales con el río como único sonido de fondo.

Rio Guadalote, Maestrazgo

Rio Guadalote, MaestrazgoRio Guadalote, MaestrazgoRio Guadalote, Maestrazgo

Un poco más arriba llegamos a los Órganos de Montoro, una recia muralla calcárea a la que el capricho de la erosión y el tiempo ha dado la aparente fragilidad de la arquitectura gótica. Está formada por capas paralelas en posición vertical, cinceladas por el agua hasta dar la forma de uno de sus tubos, de ahí su similitud con el órgano de una iglesia.

Organos de Montoro, Maestrazgo, Teruel

Organos de Montoro, Maestrazgo, TeruelOrganos de Montoro, Maestrazgo, TeruelOrganos de Montoro, Maestrazgo, Teruel

Párate todo el tiempo que necesites a contemplar y disfrutar de estos bellos parajes, no tienes prisa y vale la pena disfrutar de los caprichos de la naturaleza.

H – Río Pitarque

Siguiendo la carretera tomamos ahora el desvío hacia Pitarque lo que nos permitirá remontar el río que da nombre al pueblo. De nuevo los paisajes otoñales nos regalan postales de gran belleza.

Rio Pitarque

Rio PitarqueRio PitarqueRio Pitarque

Párate y disfruta, algo así no se ve todos los días. El río atraviesa cañones rocosos que forman junto con la vegetación estupendas composiciones otoñales.

Una vez llegas a Pitarque hay una ruta de senderismo de 4,5km (dos horas ida y vuelta) que te lleva al nacimiento del río. Queríamos haberla hecho pero nos levantamos demasiado tarde y no nos dió tiempo 😛 .

Rio Pitarque

Rio PitarqueRio PitarqueRio Pitarque

I – Villarluengo

Desde Pitarque volvemos hacia atrás y tomamos el desvío hacia Villarluengo. Sólo mira donde está situado.

Villarluengo

Siguiendo la carretera, y si tienes tiempo, puedes parar también en Tronchón, nosotros continuamos puesto que se nos hacía tarde para comer.

Mirambel

Y llegamos al punto final de nuestra ruta, uno de los lugares por excelencia del Maestrazgo y visita obligada en todas las rutas de la zona.

Mirambel

La primera parada es para comer, creo que sólo hay dos restaurantes en el pueblo, nos decantamos por Hostal Guimerá, de nuevo comida típica del Maestrazgo. Judías blancas con chorizo y potaje de garbanzos de primero, ternasco al horno de segundo y flan de cafe y pastel de la casa de postre.

Potaje de Garbanzos, Hostal Guimerá, MirambelJudías blancas con chorizo, Hostal Guimerá, Mirambel

Ternasco al horno, Hostal Guimerá, Mirambel

Flan de cafe y pastel de la casa, Hostal Guimerá, Mirambel

Tras descansar de la comida 😛 , vamos a conocer el pueblo.

Sencillo, tranquilo y pintoresco, Mirambel se encuentra en un estado de conservación y restauración envidiable. Es de esos pueblos Pin y Pon que de tan perfectos que están parecen prefabricados desde la República Independitente de tu Casa. Aún así te encantará pasear por sus calles admirando sus murallas, palacios, casonas y escudos.

Mirambel

MirambelMirambelMirambel

Aunque se desconoce cuando las tropas cristianas conquistaron el lugar de Mirambel, las primeras referencias documentales datan de 1157, cuando Alfonso II, primer monarca de la Corona de Aragón, condeció Fuero Libre a Mirambel. En 1243 la Orden del Temple, en su labor de avance y colonización, otorgó la Carta Puebla.

Los templarios construyeron un castillo-palacio en el núcleo de la villla y comenzaron a amurallar el perímetro, labor que finalizaron en el s.XV sus herederos, los sanjuanistas. Cinco espléndidos portales darían acceso, finalmente, al recinto.

Mirambel adquirió gra protagonismo durante las Guerras Caristas. Aquí se instaló durante bastante tiempo la Junta Suprema de Aragón, Valencia y Murcia; residieron notables carlistas, que llegaron a constituir una pequeña corte. Se asentaron los obispos de Orihuela y Mondoñedo, oficinas del tribunal de secuestros de la policía, curia eclesiástica, tribunal de diezmos y hospitales, intendencia, tribunal de alzada, tesorería general e imprenta real. A mediados del s.XIX en Mirambel y su término vivían 950 personas, hoy son apenas 150.

Mirambel

MirambelMirambelMirambel

Personalmente me recordó muchísimo, salvando las distancias, a Santillana del Mar, la mayor diferencia es la proyección turística de cada sitio (aquí no hay un Altamira 😛 ), pero creo que no tiene nada que envidiarle.

Mirambel

MirambelMirambelMirambel

Aqui terminamos un maravilloso fin de semana en medio de la naturaleza. Dejamos para otra ruta Cantavieja, La Iglesuela del Cid y Tronchón y tras 185km regresamos a Valencia, al día siguiente hay que trabajar 🙁 .

Primeros pasos con Flash Media Server, creando un chat

Hoy vamos a ver cómo crear una sencilla aplicación de chat con Flex. No debe tomarse este ejemplo como una aplicación completa sino simplemente como un método para comprender cómo funcionan los distintos elementos que intervienen. Tampoco debe compararse este proyecto con el cliente IRC que os presenté hace unos meses, aunque ámbos son aplicaciones de chat,  son sistemas distintos, como veréis más adelante.

Chat en Flex con FMS

Flash Media Server (FMS, antiguamente conocido Flash Communication Server) es el software de Adobe que permite crear aplicaciones multiusuario de una manera sencilla y bajo tecnología Flash. FMS permite no sólo intercambiar y sincronizar mensajes de texto entre los clientes sino tambien audio y vídeo. Si flv es el sistema de vídeo por excelencia en Internet, FMS es la tecnología para crear sistemas multimedia multiusuario. La principal pega de FMS es que es de pago, y no precisamente barato, sin embargo existe una licencia “developer” que te permite utilizar el 100% de las posibilidades del servidor, la única limitación es la cantidad de usuarios concurrentes que puedes tener, diez en este caso. Existe una alternativa Open Source a FMS, Red5, pero no lo he utilizado nunca así que no puedo opinar. La teoría con Red5 es exactamente la misma que con FMS, sólo cambia el medio (FMS se programa con ActionScript y Red5 con Java). Como ejemplo de aplicaciones que utilizan FMS está Yahoo Live, la aplicación de grabación directa desde tu webcam de Youtube o todos, absolutamente todos los videochats que puedes encontrar en Internet.

Como lo más probable es que no tengas una licencia de FMS, trabajaremos con la versión developer que hemos indicado ya que nos permitirá poner en práctica todos los conceptos necesarios.  Lo primero que debes hacer es, por tanto, descargar e instalar FMS, hay versiones para servidores Linux y Windows, escoge la que mejor te venga. La instalación no tiene ningún secreto así que lo dejaré en vuestras manos. En mi caso, como siempre, un CentOS5.

Los proyectos creados sobre FMS son sistemas cliente/servidor y necesitan dos aplicaciones para poder funcionar:

  1. Aplicación cliente, sería la parte Flash pura y dura, la que utilizarán los usuarios.
  2. Aplicación servidor, sería lo que corre bajo FMS y a lo que se conecta la aplicación cliente.

Digamos que FMS es la varita mágina del conjunto ya que hace completamente transparente al desarrollador la conexión entre usuarios y la inyección de mensajes entre ellos. Para entenderlo pensemos en un chat donde los usuarios se conectan al servidor y cada vez que uno de ellos escribe un mensaje éste se envía automáticamente a los demás usuarios. Imagina, además, que no sólo hablamos de texto sino que cuando un usuario enciende su webcam los demás usuarios pueden verla. Todo esto lo hace FMS por ti, sólo debes preocuparte de construir tu aplicación.

Programando una aplicación FMS

Empezaremos la parte práctica creando el código necesario en el servidor. Por defecto las aplicaciones FMS de instalan bajo /opt/adobe/fms/applications creando un nuevo directorio con el nombre de tu aplicación, en nuestro caso la llamaremos textchat.

Las aplicaciones se cargan y se mantienen en memoria una vez algún usuario accede por primera vez a la misma y, cuando pasado un tiempo prudencial no hay usuarios, se descarga para liberar recursos esperando a que otro usuario vuelva a acceder. Cada vez que se hacen cambios a la parte servidor de la aplicación hay que reiniciarla, pero no es necesario reiniciar el servidor FMS para reiniciar una aplicación, basta con descargar y volver a cargar esa aplicación desde la consola de administración como veremos más adelante. Desde esta misma consola podremos ver también la salida de los comandos trace que ocurran en el servidor y los objetos creados y su contenido, ayuda imprescindible mientras estás desarrollándola. Veremos también como activar la salida de estas opciones puesto que por defecto (por seguridad) vienen deshabilitadas.

Por defecto todas las aplicaciones deben tener un archivo main.asc con el código necesario.  Crea tu main.asc de esta manera:

load("netservices.asc");

application.onAppStart = function(){
  trace("inicia text chat");
  this.users_so = SharedObject.get("users_so", false);
  this.chat_so = SharedObject.get("chat_so", false);
  this.userList = {};
  this.uid = 1;
}

application.onConnect = function(client, nick, clave, sala){
  trace(nick+" "+clave+" "+sala);
  var valido=1;
  for( i in this.userList){
    if(String(this.userList[i].nick).toLowerCase()==String(nick).toLowerCase()){
      valido=0;
      break;
    }
  }
  if(valido==1){
    client.uid = this.uid;
    client.nick = nick;
    client.sala = sala;
    this.acceptConnection(client);
    this.userList[client.uid] = client;
    this.users_so.setProperty(client.uid, client);
    var msg = "<font color='#666666'><b>*** Entra " + client.nick + " ***</b></font><br>";
    this.chat_so.send("onMsgSend", msg);
    client.call("onUserid", null, client.uid, client.nick);
    this.uid++;
  }else{
    var err = new Object();
    err.message = "Ya hay un usuario con ese nick";
    trace(err.message);
    application.rejectConnection(client, err);
  }
}

application.onDisconnect = function(oldClient){
  if(oldClient.uid!=undefined){
    trace("Descontecta cliente. UID:  " +oldClient.uid);
    var msg = "<font color='#666666'><b>*** " + oldClient.nick + " sale ***</b></font><br>";
    this.chat_so.send("onMsgSend", msg);
    for( i in application.userList){
      if(Number(application.userList[i].uid)==Number(oldClient.uid)){
        application.userList[i]=undefined;
      }
      k++;
    }
    application.disconnect(oldClient);
    application.users_so.setProperty(oldClient.uid, null);
  }
}

El funcionamiento es muy sencillo. Nuestra aplicación maneja tres eventos:

  1. onAppStart: Se dispara cuando la aplicación se carga en memoria por primera vez.
  2. onConnect: Se lanza cuando un usuario se conecta a la aplicación.
  3. onDisconnect:Cuando un usuario se desconecta.

Veamos la teoría de un chat y comprenderemos rápidamente como funciona FMS en su lado servidor. En una aplicación chat típica, el servidor debe manejar una lista de los usuarios conectados actualmente (los nicks) y debe poder ser capaz de enviar mensajes a todos los usuarios. Por ejemplo, cuando un nuevo usuario se conecta y sin que éste escriba nada, el servidor tiene que poder enviar un mensaje tipo “Nick se ha conectado al chat” a todos los demás usuarios.  Para solucionar todo esto FMS tiene lo que se conocen como objetos compartidos (SharedObjects), objetos a los que todos los clientes pueden acceder.

Si analizas ahora el código de nuestro main.asc verás que el evento onAppStart crea las variables que se utilizarán en la aplicación:

  • userList: array que contendrá la lista de usuarios conectados al sistema.
  • uid:  identificador del último usuario conectado para poder localizar de manera inequívoca a un cliente.
  • users_so: objeto compartido de la lista de usuarios
  • chat_so: objeto compartido del chat propiamente dicho, a él se envíen los mensajes que escriben los usuarios.

Creo que sobran más explicaciones ya que es tan sencillo que se entiende sólo.

Veamos ahora la teoría de qué ocurre cuando se conecta un usuario, evento onConnect. Cada vez que un cliente accede a nuestro chat debemos comprobar, antes de nada, siya hay otro usuario con el mismo nick, en cuyo caso rechazamos su conexión. Si todo va bien y su nick es único debemos guardar al usuario en la lista de usuarios y avisar a todos los clientes ya conectados que hay un nuevo usuario en el chat.

Compara ahora la teoría con el código del evento. El parámetro cliente es automático y representa al cliente que se conecta. Es un objeto con distintos parámetros predefinidos. Los demás parámetros del evento se los pasaremos nosotros desde nuestro cliente flash. Para ver si un nick ya existe simplemente lo comparamos contra todos los demás del array userList que ya hemos visto que contiene a todos los usuarios conectados. El evento onConnect puede aceptar un cliente (acceptConnection) o rechazarlo (rejectConnection).

Si el usuario ha sido admitido, le damos el siguiente UID y tendremos entonces que avisar a los demás usuarios de que tenemos un usuario nuevo. Si recordamos las variables que teníamos, había un objeto compartido al que tienen acceso todos los clientes, users_so, solo tendremos que añadir nuestro nuevo cliente a este objeto:

this.users_so.setProperty(client.uid, client);

Cuando veamos la aplicación cliente veremos como detectamos los cambios en ese objeto, pero es sumamente sencillo.

Espera!. Nos falta enviar el mensaje de “Nick se ha conectado al chat”. Tan sencillo como:

this.chat_so.send("onMsgSend", msg);

Magia!. Como chat_so era un objeto compartido, todos los clientes y el servidor tienen acceso al mismo y lo que uno escriba lo recibirán automáticamente todos los demás. Esta es la verdadera magia de FMS, no tienes que preocuparte de cómo enviar los mensajes a los usuarios uno a uno, se sincronizan sólos.

Como último detalle, cuando aceptamos la conexión de un cliente llamamos remotamente al método onUserid de la aplicación cliente. Si! has leído bien!, FMS llama a una función de tu SWF!. Más adelante veremos para qué, simplemente recuerda que cuando te conectas al chat el servidor llama a un método de la aplicación pasándole su propio UID.

Finalmente veamos el último evento del servidor, bastante obvio si has seguido lo explicado anteriormente y lo has entendido, la desconexión de un usuario.

Sin pensar mucho más entenderemos que habrá que eliminar al cliente de userList y de users_so. Pues eso mismo, no hay mucha más ciencia.

Ya tenemos la parte del servidor terminada. Sencillo ¿no?. Es posible que aún no entiendas algunas cosas. Tranquilo, cuando veamos el cliente lo entenderás todo y verás que realmente es muy sencillo crear aplicaciones de este tipo con FMS.

La consola de administración

Es una aplicación Flash desde la que podemos conectarnos como administradores al servidor FMS y realizar operaciones de mantenimiento, entre ellas cargar y descargar instancias de aplicación, ver el log del sistema (los trace), examinar objetos (en modo debug)…

Cuando instalaste FMS te pidió un usuario y una clave además de un puerto donde instalar esta consola de administración. No tienes más que conectarte a ella y comenzarás a ver estos datos.

Consola de administración de FMS

Hay, sin embargo, algunas particularidades de administración que conviene tener en cuenta. Para habilitar las opciones de debug de objetos habrá que modificar el fichero conf/_defaultRoot_/_defaultVHost_/Application.xml cambiando:

<AllowDebugDefault>false</AllowDebugDefault>

a true. Reinicias el servidor para que el cambio se active y verás algo como esto que te servirá para depurar tu aplicación.

Consola de administración de FMS

En la barra de opciones de aplicación (Live log, clients…) tienes dos pequeños botones que te permitirán recargar una instancia de la aplicación o descargarla por completo, las opciones que comentábamos antes que te ayudarán durante el desarrollo para no tener que reiniciar todo el servidor.

La aplicación cliente

Y llegamos al último punto. La aplicación, el chat, lo que ve el usuario.

Ya os he puesto una captura del resultado final. Veremos ahora los entresijos. Abajo de todo tenéis el código fuente de todo el proyecto de demostración.

Me meteré directamente al meollo de conectar con FMS y enviar y recibir datos, la interfaz de usuario y la pantalla inicial de login la dejo para vosotros. Como veréis hay una clase Chat.as que centraliza casi todo el proceso de comunicación con el servidor.

chat = new Chat();
chat.addEventListener(UserIDEvent.USER_ID, onUID);
chat.addEventListener(NCEvent.STATUS, onStatus);
chat.addEventListener(NCEvent.NONICK, onStatus);

salaSeleccionada=sala.selectedItem.toString();
chat.init("rtmp://"+host+"/textchat/"+salaSeleccionada, StringUtil.trim(nick.text), StringUtil.trim(clave.text), salaSeleccionada);

private function onUID( event:UserIDEvent ):void{
    miuid=event.userid;
    currentState = "chatState";
    msg.setFocus();
    CursorManager.removeBusyCursor();
    this.addEventListener(KeyboardEvent.KEY_UP,keyHandler);
    picker.addEventListener(DropdownEvent.CLOSE, pickerClose);   
}

private function onStatus( event:NCEvent ):void{
    CursorManager.removeBusyCursor();
    if (event.status == "closed"){
        status.text = "Desconexión completa.";
    }
    if (event.status == "nickexiste"){
        status.text = "Usuario o contraseña incorrectos.";
    }
    currentState = "";
}

Al crear la instancia de Chat haremos que se detecten un par de eventos que ejecutarán:

  •  onUID: se dispara cuando el servidor, como hemos visto antes, llama al método cliente onUserid. Nos sirve para cambiar el estado de la interfaz de usuario desde la ventana de login a la de chat ya que sólo si hemos conectado recibiremos el UID.
  • onStatus: nos ayudará adetectar conexiones rechazadas y tu propia desconexión.

Para conectar véis que llamamos al método init de Chat con cuatro parámetros, la url de la instancia de nuestra aplicación (definida como host+aplicacion+sala de chat), el nick, la clave (para un futuro) y la propia sala de chat. Si examinas este método verás qué sencillo es iniciar una conexión. El método más importante es

private function netStatus( event:NetStatusEvent ):void{
    var info:Object = event.info;
    switch(info.code)
    {
    	case "NetConnection.Connect.Success":
    	    users_so = SharedObject.getRemote("users_so", nc.uri, false);
    	    users_so.addEventListener(SyncEvent.SYNC, usersSync);
    	    users_so.addEventListener(NetStatusEvent.NET_STATUS, onSOnetStatus);
    	    users_so.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSOSecurityError);
    	    users_so.client = this;
    	    users_so.connect(nc);
    	    chat_so = SharedObject.getRemote("chat_so", nc.uri, false);
    	    chat_so.addEventListener(NetStatusEvent.NET_STATUS, onSOnetStatus);
    	    chat_so.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSOSecurityError);
    	    chat_so.client = this;
    	    chat_so.connect(nc);
    	    break;
    	case "NetConnection.Connect.Rejected":
    	    dispatchEvent( new NCEvent(NCEvent.NONICK, "nickexiste"));
    	    break;
    	case "NetConnection.Connect.Closed":
    	    dispatchEvent( new NCEvent(NCEvent.STATUS, "closed"));
    	    break;
    }
}

Es el que detecta el estado de la conexión, si el servidor nos acepta o nos rechaza tal como veíamos antes e incluso si nos desconecta. Fíjate que si somos aceptados estaremos conectándonos a los SharedObjects que creamos antes en el servidor 😉 . Ahora es Chat quien hará absolutamente todo el trabajo, la aplicacion mxml es la interface de usuario que transmite a Chat los comandos necesarios. Veamos por ejemplo como indicamos al chat que envíe a los demás usuarios un mensaje que hayamos escrito nosotros.

private function sendMsg():void{
    if ( msg.text.length > 0 ){
        chat.sendMsg(msg.text);
        msg.text = "";
    }
}

Vale sí, pero has hecho trampas, aquí estás dejando el meollo en manos de Chat 😛 . Bien, veamos qué ocurre en sendMsg.

public function sendMsg( color:String, msg:String ):void{    
    var outmsg:String = "";    
    outmsg = "<font color='#" + color + "'><b>" + this.username+":</b> " + msg + "</font><br>";
    chat_so.send("onMsgSend", outmsg);
}

¡ Sólo eso ! Como ya estamos conectados al SharedObject chat_so, le decimos a éste que llame al método onMsgSend de sus clientes y le pase el texto que hemos escrito.

De acuerdo, entonces ¿cómo leemos los mensajes que escriben los demás usuarios?. Te lo acabo de decir 😛 . Cuando un usuario escribe algo, llama (da igual cómo) a un método local de cada usuario que se encarga de escribir el texto en su ventana de chat.

public function onMsgSend( msg:String ):void{
    chatHistory += msg;        
    dispatchEvent(new Event("histChange"));
}

Obviamente este método tiene que ser público. Al despachar ese evento simplemente hace que se actualice el texto del textarea.

Hasta ahora creo que lo voy entendiendo, pero no has explicado como detectamos las entradas y salidas de usuarios. Cierto, veámoslo pues. Acabamos de ver que cuando entramos al chat y somos admitidos conectamos con los SharedObjects que creamos en el servidor. Si te has fijado (y si no hazlo ahora), al de usuarios le poníamos un listener distinto:

users_so.addEventListener(SyncEvent.SYNC, usersSync);

Esto significa que cada vez que haya un cambio en el objeto users_so todos los clientes serán notificados de ello. Por ejemplo, cada vez que entra o sale un usuario vimos que añadíamos o quitábamos un elemento de este objeto que representaba a ése usuario. Cada una de estas operaciones hará que nuestra aplicación cliente reciba una llamada a usersSync.

private function usersSync(event:SyncEvent):void{
    var results:Object = event.target.data;
    var usersArray:Array = new Array();
    var existe:Boolean=false;
    //debo actualiza solo los datos
    for( var i:String in results ){
        if(results[i].sala==this.sala){
            existe=false;
            for(var j:Number=0; j<dpUsers.length; j++){
                if(dpUsers[j].uid==results[i].uid){
                    existe=true;
                    break;
                }
            }

            if(existe){
                //por si se ha cambiado el nick
                dpUsers[j].nick=results[i].nick;
            }else{
                var clientObj:Object=new Object();   
                clientObj.nick = results[i].nick;
                clientObj.uid = results[i].uid;
                dpUsers.addItem(clientObj);
            }
        }
    }
    //ahora tengo que eliminar las bajas
    //elimino empezando por la ultima para no cambiar los indices
    for(j=dpUsers.length-1; j>=0; j--){
        existe=false;
        for(i in results ){
            if(dpUsers[j].uid==results[i].uid && results[i].sala==this.sala){
                existe=true;
            }
        }

        if(!existe){
            dpUsers.removeItemAt(j);
        }
    }
    //finalmente ordenamos los usuarios
    var sort:Sort = new Sort();
    sort.fields = [new SortField("nick",true)];
    dpUsers.sort = sort;
    dpUsers.refresh();
    dispatchEvent(new Event("dpChange"));           
}

Esta método repasará todos los usuarios del SharedObject comparándolos con los de la lista de usuarios del canal donde nos encontramos chateando y añadiendo los nuevos usuarios y eliminando los que hayan salido.  Quizás no sea el mejor método para manejar los usuarios y habría sido mucho más sencillo tener una pareja de métodos addUsuario y delUsuario que manejen todo automáticamente con llamadas remotas igual que con los mensajes, pero quería que viésemos el modo de recibir avisos de cambios en un SharedObject.

Conclusiones

Hemos visto cómo de una manera sencillísima podemos crear nuestro propio chat sin excesivas complicaciones. Piensa además que esto es, seguramente, lo más sencillo, el primer paso con FMS, las posibilidades reales son infinitas.

Quizás en otros artículos ampliemos las posibilidades de este pequeño ejemplo añadiendo vídeo y audio o, incluso, hagamos un pequeño sistema de grabación de vídeo con tu webcam similar al de Youtube.

Como siempre, aquí os dejo el código completo del proyecto. Recuerda que es un ejemplo de lo que se puede hacer, no debes usarlo tal cual sino aplicar lo que hemos explicado. Del mismo modo habrá cosas que no sean perfectas y se puedan hacer mejor, no lo dudo, mi intención era explicar los conceptos básicos de FMS y que se vea su potencial.

Cancelan el SIMO, bueno ¿y que?

Creo que todos los que vivimos de esto (o en esto) lo veíamos venir.

La última vez que fui sería allá por 2002 o 2003 y, la verdad, no me quedaron muchas más ganas de vover los años siguientes en que el SIMO sufrió una decadencia progresiva sin que nadie dentro de la organización quisiera verlo, no hay más que ver los comentarios acerca de las últimas ediciones.

Hace 10 años era increíble el movimiento que suponía. Estaba en Teleco-Vigo y, acercándose las fechas del SIMO, sólo se hablaba de eso. La feria era como introducirte en Matrix o en Blade Runner, cientos y cientos de cosas nuevas, hardware y software por doquier que a saber cuando llegaría a este país. Había ordenadores con acceso gratuito a Internet (esto, hoy, puede sonar a coña, pero era un atractivo enorme de aquella). Todos los fabricantes esperaban a este evento para presentar productos nuevos. El SIMO no era la única feria tecnológica, había otras más pequeñas por todo el territorio nacional. Hoy todo esto se ha perdido. La globalización, los países emergentes e Internet han acelerado al máximo las presentaciones que, salvo contadas excepciones, ya no son por todo lo alto. Hoy se presenta un producto y mañana tienes un análisis completo en Internet, pasado lo puedes comprar en tiendas de Hong Kong y la semana que viene en el Media Markt.

El SIMO se había convertido en eso, un Corte Inglés o un Media Markt sin mayores pretensiones. Una feria donde lo más llamativo eran las azafatas (triste pero cierto) y donde la gente se dedicaba a llenarse de bolsas y bolígrafos, porque cosas interesantes, en realidad, ninguna.

Pero espera, si había una parte profesional, de hecho, salvo el último día, todo era profesional. Lancemos una tímida sonrisa. ¿Acaso ahora todo el mundo es profesional? ¡Claro!, las entradas e invitaciones circulaban por todas las esquinas.

No creo, pues, que a nadie le coja por sorpresa la decisión. Creo que una feria profesional del sector es necesaria en este país, pero no creo que la fórmula adecuada actualmente sea la del SIMO. Quizás ahora mismo estaban dando los primeros pasos correctos, pero han llegado tarde. Las grandes marcas han perdido el interés en la feria. Este año había sólo un 55% de los presentes el pasado y no creo que a gente como Telefónica o Microsoft les afecte la crisis o no puedan hacer frente a lo que cuesta tener presencia en el SIMO, una cifra ridícula al lado de sus presupuestos. Simplemente han visto que el sector ya no necesita ferias, tienen Internet.

Aprovechando la bajada de persiana (temporal) del SIMO, muchos han comparado esta feria con el Mobile World Congress (MWC), antiguo 3gsm, que se celebra en Barcelona desde hace unos años. He estado en el SIMO y en el MWC y, en serio, no hay color, no tienen nada que ver. La profesionalización del MWC es patente desde que llegas al recinto, y no lo digo por el coste de la entrada, lo digo por la gente que viene, los stands, los expositores… nada que ver con el SIMO. Posiblemente el MWC acabe igual que el SIMO, pero aún queda mucho tiempo para que llegue ese día, el movimiento profesional, los ciclos de conferencias y los workshops hacen del MWC algo distinto, algo hacia lo que debió comenzar a enforcarse el SIMO desde hace al menos cinco años. El público en general no tiene cabida en el MWC, apenas hay azafatas o regalos en los stands, sin el inglés estás incomunidado y lo peor de todo, la mayoría de cosas que se ven no son de consumo así que, por tanto, no interesan al público en general. En el MWC tampoco hay grandes lanzamientos ni presentaciones, pero genera una expectación inusitada.

Además, ¿por que no separar la parte mas lúdica de la feria y crear un SIMO dedicado al sector de juegos en exclusiva? Creo que tiene potencial y mercado suficiente y atraería a un público muy concreto y especializado. Aunque, por otro lado, si hoy en día eliminamos la parte de juegos y la de móviles (ya está el MWC) del SIMO, ¿quedaría algo realmente interesante?. Más aún, ¿debe una feria de este tipo ser interesante para el gran público o con que lo sea para el profesional es más que suficiente?. Quizás sea esta la pregunta fundamental ya que el SIMO se enfocó en sus últimos años al gran público, olvidándose del profesional al cual se suponía que ya interesaría de por sí.

Corren nuevos vientos para las ferias tecnológicas.

Cómo emitir webcams en vivo con un servidor Linux y en formato FLV

Hace un tiempo tuve un cliente que quería visualizar las cámaras que había instalado en su oficina desde su casa. En aquel momento eran cámaras IP cableadas. Este tipo de cámaras tienen un servidor web integrado desde donde puedes configurar su dirección IP además de ver el vídeo que transmiten. Por lo general estas cámaras transmiten en formato mjpeg, un flujo de imágenes JPG normales, aunque también podría ser que lo hiciesen directamente en mpeg. Firefox es capaz de reproducir este formato directamente, no así Internet Explorer que necesita de un applet Java para hacerlo.

El reto consistía, por tanto, en transmitir las cámaras en un formato que se pudiese reproducir fácilmente y que se pudiese integrar en una página web, sin programas de terceros para visualizarlas. Así llegamos a la solución que hoy os presento y que no es exclusiva de cámaras IP sino que puedes utilizar cualquier webcam USB que tengas por casa. El formato que hemos escogido para el flujo de vídeo será flv,  con lo que con una sencilla aplicación Flash podremos visualizar cada una de nuestras cámaras evitando, de paso, los applets Java.

Para el proyecto de hoy necesitaremos:

  • Servidor Linux, imprescindible, en mi caso Centos5
  • Apache instalado en el servidor.
  • ffmpeg, para hacer la conversión de formatos
  • ffserver, parte del paquete ffmpeg, para transmitir elvídeo

Si no tienes el software necesario, el primer paso es instalarlo. En mi caso:

yum install ffmpeg apache

Comenzaremos configurando ffserver desde /etc/ffserver.conf:

Port 8090
BindAddress 0.0.0.0
MaxClients 1000
MaxBandwidth 10000            

<Feed feed1.ffm>
  File /tmp/feed1.ffm
  FileMaxSize 5M
</Feed>
<Feed feed2.ffm>
  File /tmp/feed2.ffm
  FileMaxSize 5M
</Feed>            

<Stream camara1.flv>
  Feed feed1.ffm
  Format flv
  VideoCodec flv
  VideoBitRate 128
  VideoBufferSize 500
  VideoFrameRate 5
  VideoSize 320x240
  NoAudio
  Preroll 5
</Stream>            

<Stream camara2.flv>
  Feed feed2.ffm
  Format flv
  VideoCodec flv
  VideoBitRate 128
  VideoBufferSize 500
  VideoFrameRate 5
  VideoSize 320x240
  NoAudio
  Preroll 5
</Stream>            

<Stream stat.html>
  Format status
</Stream>

ffserver funciona como un servidor, transmitiendo en el puerto que le indiquemos lo que recibe en los archivos que se indican en los feed’s. A continuación se indican los flujos de vídeos que vamos a utilizar (feed’s), en mi caso dos cámaras. Finalmente definimos los streams que será a lo que realmente nos conectaremos nosotros, un stream por cada feed en formato flv. Podríamos crear tantos streams de cada feed como creamos oportuno en distintos formatos, consulta la ayuda de ffserver y ffmpeg.  Finalmente creamos un stream especial, el de status, donde veremos información sobre el estado del servidor y clientes contectados.

Lanzamos el servidor ffserver tal cual.

ffserver

Con esta configuración tendremos acceso a dos flujos de vídeo en formato FLV en las direcciones:

http://localhost:8090/camara1.flv
http://localhost:8090/camara2.flv

Vale, sí, aún no hemos transmitido el vídeo, eso viene ahora. Debemos enlazar el flujo de vídeo de nuestras cámaras con ffserver para que éste haga la conversión de formato y lo emita por las urls indicadas. Para hacerlo recurrimos al inseparable compañero de ffserver, ffmpeg.

Si tus cámaras están conectadas al servidor bien sea por USB o por capturadoras de vídeo (debes tenerlas configuradas previamente):

ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video0 http://localhost:8090/feed1.ffm
ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video1 http://localhost:8090/feed2.ffm

Si tus cámaras fuesen IP habría que capturar primero el vídeo desde la cámara y pasárselo a ffmpeg, por ejemplo:

curl http://ip-de-tu-camara:8080 | ffmpeg -er 4 -y -v quiet -an -f mjpeg -r 4 -i - http://localhost:8090/feed1.ffm

Sea como sea el formato de tus cámaras, y si todo ha ido bien, tendrás a ffmpeg enviando el vídeo a ffserver para que lo convierta a FLV y lo publique en las URL’s anteriormente indicadas.

Debes tener cuidado con un detalle importante si conectas más de una cámara USB, debes enchufarlas a concentradores distintos de tu equipo para diversificar adecuadamente los flujos de vídeo y el ancho de banda que ocupan, si conectases dos cámaras al mismo concentrador no tendrían ancho de banda suficiente y se bloquearían entre ellas.

Veamos ahora como visualizar los flujos de vídeo. Necesitaremos el editor de Flash de toda la vida. Creamos un nuevo documento y arrastramos al escenario un objeto FLVPlayback. Como nombre de instancia le ponemos “video” y en sus propiedades ponemos isLive a true. En el primer frame añadimos el siguiente código:

video.contentPath=path_video;

De esta manera podremos indicar desde html la url del flujo de vídeo sin tener que tener un swf por cada cámara. Compila y publica el proyecto puesto que ya hemos terminado.

No me voy a preocupar ahora del código html generado, eso es cosa tuya. Dentro del archivo html que te genera el editor de flash tendrás algo como:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="webcam1" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="webcam.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="webcam.swf" mce_src="webcam.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="webcam1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

Vamos a convertirlo en esto:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="webcam1" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="webcam.swf?path_video=http://192.168.90.1:8090/camara1.flv" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="webcam.swf?path_video=http://192.168.90.1:8090/camara1.flv" mce_src="webcam.swf?path_video=http://192.168.3.2:8090/camara1.flv" quality="high" bgcolor="#ffffff" width="550" height="400" name="webcam1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

Es decir, añadimos al parámetro de la película la variable path_video con la url del flujo tal y como lo hemos generado antes. Debe ser una URL a la que se pueda acceder externamente pues el swf se conectará a ella para recoger el flujo flv generado por ffserver, si tienes un router o un firewall de por medio, deberás abrir ese puerto. Copiaremos este código tantas veces como cámaras tengamos modificando la URL del flujo de vídeo.

Voila! esto es todo, ya tienes publicadas tus cámaras en un formato sencillo de visualizar como es el FLV.  Abre la página html que has creado y comenzarás a visualizar tus cámaras.

Pero… vayamos un paso más allá. Con lo que hemos visto hasta ahora tendremos que abrir el puerto 8090 de buestra máquina para permitir el acceso a los flujos de vídeo, pero nosotros no queremos hacer esto, ya tenemos el puerto 80 con Apache a la escucha. ¿Qué hacemos? Sencillo, mod_proxy viene a nuestro rescate. Asegúrate de tenerlo habilitado en la configuración de tu Apache. Después simplemente habrá que añadir las siguientes líneas a la configuración del virtual host en el que quieres publicar los streams:

ProxyPass /camara1.flv http://127.0.0.1:8090/camara1.flv
ProxyPassReverse /camara1.flv http://127.0.0.1:8090/camara1.flv        

ProxyPass /camara2.flv http://127.0.0.1:8090/camara2.flv
ProxyPassReverse /camara2.flv http://127.0.0.1:8090/camara2.flv

Con esto estaremos enviando automáticamente cada stream desde su url original en el puerto 8090 a una url de tu virtual host del tipo http://tudominio.com/camaraX.flv. Esa ruta no existe físicamente, es virtual y Apache sabe que debe redirigirla al stream del ffserver.

Finalmente queda un detalle importante. Hasta ahora habíamos incializado ffserver por un lado y los flujos de las cámaras por otro con ffmpeg. Esto es un rollo y no nos gusta, queremos que ffserver lo inicie todo automáticamente. Pues vale, añadamos una directiva Launch a cada feed de la configuración de ffserver con los parámetros que utilizábamos para llamarlo desde la línea de comandos. En el ejemplo de nuestra primera cámara quedaría:

<Feed feed1.ffm>
  File /tmp/feed1.ffm
  FileMaxSize 5M
  Launch  ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video0
</Feed>

A partir de ahora, cada vez que lancemos ffserver tecleando simplemente ese comando, se inciarán, además del propio servidor, los streams que hayamos definido.

Hasta aquí hemos llegado por hoy. De una manera extremadamente sencilla hemos publicado nuestras webcams. Es responsabilidad tuya y sólamente tuya el asegurar el acceso a los flujos y a la página de visualización que hemos creado. Otro día quizás veamos cómo montar un sistema de videovigilancia casero con detector de presencia 😉 .

Os dejo el código de la aplicación Flash de visualización de los streams.

Belchite, memoria de una guerra

24 de agosto de 1937.

Tras el fallido intento de avanzar hacia Brunete para asegurar el frente de Madrid, las tropas republicanas deciden asediar Zaragoza para frenar de nuevo la toma del frente norte en Santander por parte de los sublevados.

El asedio se hace a través de varios puntos. En el frente sur, Quinto, Mediana y Codo caen sin problemas, pero el ejército del gobierno se encuentra con la resistencia de Belchite, que no caería hasta el 7 de septiembre. Precisamente el empecinamiento en tomar esta localidad provoca retrasos en el acecho a Zaragoza y da tiempo al ejercito nacional a reorganizarse y reforzar sus posiciones.

Este es el resultado de los bombardeos de aquél verano de 1937.

Pueblo viejo de Belchite

Pueblo viejo de BelchitePueblo viejo de BelchitePueblo viejo de Belchite

Recupero con este artículo mis rutas de fin de semana e intentaré hacerlas habituales, al menos una por mes. Llevaba ya un tiempo con ganas de ir a este lugar que me recomendó Alvaro. Belchite está a unos 300 km de Valencia, antes de llegar a Zaragoza. Es algo lejos para ir y volver en el día pero vale la pena madrugar un poco para las tres horas de viaje.

En cuanto divisas Belchite desde la carretera la angustia se apodera de ti, las ruínas cortan de cuajo el paisaje creando una fantasmal postal que te produce curiosidad a la vez que escalofríos.

Lo que ves es, en realidad, lo que se conoce como Pueblo Viejo de Belchite. Justo al lado de éste se construyó uno nuevo para acoger a todos los habitantes que habían sobrevivido a la Guerra Civil.

Pueblo viejo de Belchite

Pueblo viejo de BelchitePueblo viejo de BelchitePueblo viejo de Belchite

Tras la sublevación del 18 de julio de 1936, Zaragoza y los pueblos de su entorno, entre ellos Belchite, quedaron bajo el bando nacional. El ejército ocupó los pueblos e instaló cuarteles en los mismos, ocupando casas privadas. A Belchite le tocó la suerte (mala) de pasar a la historia como una de las batallas más cruentas de la Guerra Civil. Los nacionales se hicieron fuertes en este pueblo y el ejército republicano, decidido a tomarlo, envalentonado por la relativa facilidad con que ocuparon los pueblos de alrededor y, a la vez, enfurecido con la resistencia que estaban demostrando, no  dudó en descargar toda la fuerza de su artillería sobre la población civil dejando el pueblo sumido en ruínas.

Alrededor de seis mil personas murieron en el asedio a Belchite dejándolo hundido en la pobreza, la miseria y la destrucción.

La toma del pueblo por los republicanos no supuso ningún avance en la contienda, de hecho, poco tiempo después (marzo de 1938), el ejército nacional volvería a recuperarla de una manera mucho más sencilla a como la perdieron, pero ya nada sería igual en Belchite y en sus gentes.

Pueblo viejo de Belchite

Pueblo viejo de BelchitePueblo viejo de BelchitePueblo viejo de Belchite

Con el fin de la Guerra Civil y el triunfo de los sublevados, se plantearon dos opciones, restaurar el pueblo destruído o construir uno nuevo. Se escogió esto último y Franco decidió no derruir el Pueblo Viejo, prefirió dejarlo tal cual quedó para orgullo del Movimiento y verguenza de los vencidos, de hecho allí mismo hizo construir un monumento a los caídos en el que aún se celebran actos franquistas (¿no estaban prohibidos? 😉 ).En la inauguración del Pueblo Nuevo Franco quiso convertir el pueblo en lugar de devoción:

Belchite fue bastión que aguantó la furia rojo-comunista. En los frentes de batalla y en las guerras a unos les corresponde ser yunque y a otros maza. Belchite fue yunque, fue el reducto que había de aguantar mientras se desarrollaban las operaciones del norte. Belchite tenía que poner el pecho de sus hijos para que fuese posible la victoria. Y de aquella sangre derramada, de aquel esfuerzo heroico de hombres, mujeres y niños, de ahí nació nuestra victoria.

Y vaya si pusieron el pecho, seis mil pechos para ser exactos.

Para la construcción del nuevo pueblo se utilizaron prisioneros republicanos, a modo de redención. Se creó un campo de concentración con una población permanente de unos mil presos que se encargaron de levantar un pueblo donde no había nada. Son muchas las voces que creen que esto fue un error, el Pueblo Viejo era una joya de la arquitectura mudéjar y se perdió todo en las ruínas y en el olvido.

Pueblo viejo de Belchite

Pueblo viejo de BelchitePueblo viejo de BelchitePueblo viejo de Belchite

Cuando nosotros estuvimos en Belchite tuvimos la suerte de coincidir con una señora que paseaba por las ruínas con su familia. Esta mujer había nacido en Belchite y la casa de su familia fué ocupada como cuartel por los nacionales. Nos relataba como hay cosas que nunca se olvidarán en Belchite, pueblo que sigue divido por el recuerdo de lo ocurrido. Hay al menos dos generaciones nuevas tras la Guerra y sigue habiendo vecinos que no se hablan con otros vecinos, ni sus hijos, ni sus nietos: Ni se te ocurra hablar con fulano, él mandó matar a tu abuelo. Y es que queridos lectores, eso fué la Guerra Civil, familiares, amigos y vecinos enfrentados por las circustancias. Éticamente es más que reprochable que 70 años después siga existiendo ese rencor, pero el sentimiento y la familia no entienden de ética. Creo que hay que entender a todo el mundo.

Éste rencor es una de las razones por las que es más que probable que nunca se intente una recuperación histórica de Belchite, nunca habrá un acuerdo sobre qué se debe hacer y cómo se debe hacer.

 Pueblo viejo de Belchite

Pueblo viejo de BelchitePueblo viejo de BelchitePueblo viejo de Belchite

La leyenda popular dice que el pueblo está tal cual quedó tras la Guerra Civil. Con un pequeño análisis de la historia uno se da cuenta de que esto no es posible. Primero, el pueblo estuvo aún habitado unos 15 años, lo que se tardó en levantar el nuevo pueblo, no sería posible vivir en las ruínas que se ven hoy en día. Segundo, han pasado nada más y nada menos que 70 años desde la destrucción del pueblo (55 desde el traslado al nuevo). Los saqueos y los fenómenos meteorológicos harían el resto.

Aún así, los restos que existen dan fé de la pena y sufrimiento que tuvo que pasar aquella gente.  Los seis mil caídos durante el asedio han convertido Belchite en uno de los puntos más calientes para los aficcionados a la parapsicología. Infinidad de grabaciones y estudios creen demostrar la existencia de espíritus de los fallecidos aquéllos días.

Pueblo viejo de Belchite

Pueblo viejo de BelchitePueblo viejo de Belchite
Pueblo viejo de BelchitePueblo viejo de Belchite

Belchite es memoria viva de todo esto que os he contado, pero lo importante no es la historia, ésta está ahí y es inamomible. Lo realmente importante es la sensación que tienes cuando paseas por las ruínas. La soledad, el silencio, la solemnidad… Te cruzas con más visitantes y entre todos hay un halo de respecto, pena y tristeza.

Las iglesias, la Torre del Reloj, el convento (en restauración actualmente), los restos de las viviendas de la Calle Mayor… Todo en Belchite son pruebas de la Guerra. Muchos edificios  están llenos de agujeros de proyectiles cual queso de Gruyère, incluso se puede ver todavía uno de ellos incrustado en lo que era la Iglesia de San Agustín. Impresiona pensar que lleva ahí 70 años.

A medida que avanzas por lo que un día fueron las calles del pueblo te das cuenta que no era un pueblecito sino un señor pueblo. Aquella señora que nos encontramos nos contaba que era uno de los más grandes de la zona, no en vano, como ya he comentado, había tres iglesias y un convento.

En Belchite no hay folletos turísticos ni carteles que te guíen.  El que va, sabe a dónde va y lo que se va a encontrar. Y aún así más de diez mil personas al año visitan este vestigio mudo de la Guerra Civil.

Pueblo viejo de BelchitePueblo viejo de BelchitePueblo viejo de Belchite

Mucha gente del pueblo sigue pensando que Belchite fue destruido por la República, gobierno legítimo y elegido democráticamente por los españoles. No fueron los republicanos sino los nacionales los que se sublevaron contra el gobierno. Sobran más palabras.

Belchite se ha convertido con los años en un escenario cinematográfico habitual, sobre todo en películas relacionadas con la Guerra Civil. El laberinto del Fauno es una de las más recientes.

Dónde comer

Como estamos en una ruta de fin de semana, es indispensable hacer parada y fonda. Podemos continuar nuestra excursión hacia Cariñena. En nuestro caso elegimos el Hotel Cariñena, con un interesante menú del día, pero mejor aún resultó el menú especial. Nosotros comimos espárragos con gambas y entremeses de jamón de pato, jamón de bodega y foie de primeros, ternasco y confit de pato de segundos y sorbete de mandarina y pasteles variados de postre, todo ello regado con una botella de Monasterio de las Viñas, D.O. Cariñena (cómo no 😛 ), aceptable, nada que objetar para estar incluída en el menú.

Esparragos con gambasJamon, jamon de pato y foie
Ternasco
Confit de pato
Sorbete de mandarinaPastelitos variados

Por la tarde y de regreso a Valencia puedes terminar tu excursión visitando cualquier pueblo de Teruel, no quedarás defraudado. Yo recomiendo una visita al castillo de Peracense.

No soy ningún experto en historia ni mucho menos. Lo que aquí cuento es obra de un ejercicio de documentación y googleo ya que me interesó mucho el tema cuando estuve allí. No dudo, pues, que pueda haber errores históricos. Estaré agradecido a quien quiera corregirlos.

Usando certificados SSL de cliente como sistema de autenticación web

A menudo creamos aplicaciones web con un backend de gestión que, por ser también web, exponemos públicamente a cualquiera que consiga averiguar la URL. Habitualmente estos sistemas son de acceso restringido, sólo un pequeño grupo de usuarios lo utiliza.

En escenarios donde tenemos un número de usuarios acotado y se necesita autentificación, se puede utilizar un mecanismo de certificados que aporten mayor seguridad al sistema, de esta manera solo aquellos usuarios que tengan el certificado en cuestión tendrán acceso a la máquina.

Hoy veremos como permitir el acceso a nuestra aplicación a aquellos usuarios que dispongan de un certificado que previamente les habremos enviado mientras que si no lo tienen no podrán acceder de ningún modo. Este método se puede combinar, además, con el tradicional usuario/clave para dar mayor seguridad. Podremos incluso verificar que el nombre de usuario que se intenta utilizar se corresponde con el certificado de usuario que le hemos enviado y no intenta autentificarse con otro.

Conceptos básicos sobre certificados SSL

El método que vamos a ver se basa en certificados SSL. Se utilizan para asegurar la información entre un cliente y el servidor y prevenir escuchas ya que la información viaja encriptada. Ésta es su función y la hace aunque no esté firmado por una autoridad certificadora (CA) oficial o, incluso, aunque esté caducado. Sigue asegurando las comunicaciones.

Los navegadores web reconocen, por defecto, una serie de autoridades certificadoras como Verisign o Thawte, aunque hay muchas más. Puedes verlas todas en las opciones de tu navegador. Pero, ¿qué es realmente lo que hace una Autoridad Certificadora? Firmar. Firma tu certificado SSL asegurando que os pertenece a ti y a tu dominio. Cuando un cliente accede a tu dominio y descarga el certificado SSL, busca dentro de sus certificados de CA‘s si hay alguno que lo haya firmado. Si lo encuentra, acepta tu certificado y no ocurre nada especial, pero si no encuentra la CA lanza un aviso indicando que no se reconoce la autoridad que lo firma. Esto no quiere decir que el certificado no sea válido, lo único que ocurre es que no sabe quien lo firma. Esto significa, por tanto, que tú mismo puedes ser tu propia autoridad certificadora y firmar tus certificados, funcionarán perfectamente y cumplirán su cometido de asegurar las comunicaciones cliente/servidor.

Comercialmente o en sistemas de acceso público en general, no se recomiendan certificados autofirmados ya que el aviso de autoridad de certificación no reconocida generará desconfianza entre tus usuarios, pero en entorno intranet o de paneles de adminsitración es un método ideal.

El servidor puede requerir, además, otro certificado al cliente, de manera que ámbos extremos autentifiquen la comunicación. Esto es precisamente lo que vamos a hacer hoy en este artículo.

Según lo que hemos explicado, los certificados autofirmados son igual de seguros que los firmados por una autoridad certificadora. Como en el ejemplo que estamos viendo estamos asegurando el acceso a nuestra aplicación para un grupo reducido de usuarios, no hay ningún problema en utilizar un certificado firmado por nosotros mismos ya que nuestros usuarios sabrán que no hay ningún problema. Pero esto no es todo, por esta misma razón podemos decir a los usuarios que se instalen el certificado público de nuestra CA, tal y como hacen las CA oficiales, y automáticamente el navegador comenzará a confiar en nuestros certificados ya que, ahora sí, tiene un certificado de una CA que firma los certificados SSL.

Como resumen, nuestro trabjo consistirá en:

  • Crear nuestra autoridad certificadora y su certificado.
  • Crear el certificado SSL para nuestro servidor web firmado por nuestra CA.
  • Crear los certificados de cliente para nuestros usuarios.
  • Habilitar la lectura de los datos SSL desde PHP.

Servidor web SSL

Utilizaremos el paquete Openssl para generar los certificados. Si aún no lo tienes instalado en tu servidor, es el momento. Explicaré de manera rápida como crear crear certificados SSL para asegurar las comunicaciones ya que es el primer paso necesario para añadir certificados de cliente, sin embargo no es el objeto principal de este artículo y hay mucha documentación, googlea un poco 😉 .

Primero creamos el certificado y la clave privada de nuestra autoridad de certificación:

openssl req -x509 -newkey rsa:2048 -days 3650 -keyout CAXplotaKey.pem -out CAXplotacert.pem

Lo más importante de este comando es el parámetro days, ya que no queremos que dentro de un año nos caduque el certificado de nuestra propia entidad. Yo le pongo 10 años. Este comando genera dos archivos, la clave privada con la que firmaremos nuestros futuros certificados y el certificado con la clave pública que instalaremos, si queremos no recibir avisos, en el navegador. Este comando te pedirá algunos datos (nombre de empresa, país…) y, sobre todo, una contraseña. Deberás recordarla cada vez que vayas a firmar un certificado SSL, así que no la olvides. Ya tenemos nuestra CA creada.

Creamos ahora el certificado SSL para nuestro dominio:

openssl genrsa -des3 -out claveprivada.pem 2048
openssl req -new -key claveprivada.pem -out certificado.pem

El primer comando crea la clave privada de nuestro certificado. Te pedirá otra contraseña, esta vez para la clave privada. Recuérdala también.

El segundo comando genera la petición de certificado sobre la clave privada anterior. Te pedirá la contraseña de la clave privada anterior.

En este punto tenemos cuatro archivos, la clave y el certificado de tu CA y la clave y la solicitud de tu certificado SSL. Sólo queda firmar el certificado con nuestra CA y ya podremos utilizarlo.

Para poder firmarlo debemos generar primero un fichero de texto con algunos parámetros de configuración:

cat configservidor.cnf

basicConstraints = critical,CA:FALSE
extendedKeyUsage = serverAuth

Y firmamos el certificado.

openssl x509 -CA CAXplotacert.pem -CAkey CAXplotaKey.pem -req -extfile configservidor.cnf -in certificado.pem -days 3650 -CAcreateserial -sha1 -out certificado-servidor.pem

Ya tenemos un certificado SSL preparado para utilizar en nuestro servidor web. En nuestro caso es certificado-servidor.pem. Vamos a configurar Apache para que lo utilice.

Habrá que editar httpd.conf y añadir

LoadModule ssl_module modules/mod_ssl.so
Listen 443

Es probable que, si no has compilado tu propio Apache y has instalado un paquete precompilado, tengas ya algún ssl.conf. Activándolo tendrás este paso preparado. En mi caso, un CentOS5, sólo hay que incluir /etc/httpd/conf.d/ssl.conf.

Finalmente hay que indicar en el virtual host que quieres asegurar que use nuestro nuevo certificado. Para hacerlo añadimos un nuevo virtual que escuche en el puerto 443 y añadimos las siguientes líneas:

SSLEngine on
SSLCertificateFile /ruta/a/certificado-servidor.pem
SSLCertificateKeyFile /ruta/a/claveprivada.pem

Si ahora reinicias Apache y accedes a tu dominio con https verás que en tu navegador aparece el candado indicando que la información es segura. Verás también que te aparece un aviso de que no se confía en la autoridad certificadora. Veremos más adelante como solucionarlo.

Añadiendo certificados de cliente

Ahora que ya tenemos nuestro servidor web seguro con nuestros certificado autofirmado llega el momento de crear certificados para nuestros clientes de manera que si alguien intenta acceder a nuestra aplicación sin uno de ellos se le prohíba el paso.

Crearemos primero un archivo de configuración con los parámetros que necesitaremos.

cat configcliente.cnf

basicConstraints = critical,CA:FALSE
extendedKeyUsage = clientAuth

Con esto daremos instrucciones de que es un certificado cliente a la hora de firmar el certificado.

Creamos ahora, igual que hacíamos antes, la clave privada y la solicitud de certificado.

openssl genrsa -des3 -out clave-cliente.pem 2048
openssl req -new -key clave-cliente.pem  -out certificado-cliente-req.pem

Como antes, al generar la clave te pedirá una contraseña que deberás introducir después, al hacer la solicitud de certificado. Los datos que te pide esta solicitud, como ocurría antes, los podrás leer posteriormente para comprobar datos o lo que estimes oportuno, así que es importante que prestes atención.

Firmamos ahora el certificado con nuestra CA:

openssl x509 -CA CAXplotacert.pem -CAkey CAXplotaKey.pem -req -in certificado-cliente-req.pem -days 3650 -extfile configcliente.cnf  -CAcreateserial -sha1 -out certificado-cliente.pem

Como véis, el proceso es el mismo para generar el certificado de servidor, pero cambiamos el contenido del archivo de configuración que le indica que es un certificado cliente, no servidor.

Vale, bien, pero ¿no quedamos que es el cliente el que debe instalar el certificado? Sí, ahí vamos ahora. El certificado que acabamos de generar lo debes instalar en tu navegador web, no en el servidor, así que habrá que convertirlo a algún formato que puedan entender. Para esto hacemos lo siguiente:

openssl pkcs12 -export -in certificado-cliente.pem -inkey clave-cliente.pem -certfile CAXplotacert.pem -out cliente.p12

Nos pedirá la contraseña de la clave privada del certificado y nos solicitará otra para el que va a generar. Es importante poner contraseña al certificado final ya que es el que vas a enviar a tus usuarios y pretendes que sólo estos puedan utilizarlo, así que poner una contraseña nunca está demás.

Ya tienes tu certificado cliente.p12. Puedes probarlo tu mismo. Ah no, espera, que no hemos configurado el servidor web para que solicite sí o sí un certificado al cliente. Añade a la configuración SSL de tu virtual host de Apache:

SSLCACertificateFile /path/a/CAXplotacert.pem
SSLVerifyClient require

Y reinicia tu servidor web. Si ahora intentas acceder a tu dominio verás como salta una ventana solicitando un certificado que no tienes. Desde las opciones de tu navegador, busca la sección de certificados SSL y añade un nuevo certificado. Selecciona tu cliente.p12. Te pedirá la contraseña que pusiste al convertirlo a pk12. Ya está. Accede ahora y verás como te aparece el certificado en la ventana de certificiados disponibles y, si lo seleccionas, te deja acceder a tu aplicación.

Revocar certificados

A veces puede ser necesario prohibir el paso con determinados certificados, bien porque el usuario ya no colabora o trabaja contigo, bien porque hay posibilidades de que el certificado haya sido robado, etc. Una opción es, a la hora de crear el certificado, y si sabes de antemano que el usuario lo necesitará pocos días (cuenta de prueba, usuario esporádico o cualquier razón similar), generarlo con una validez limitada (10 días, un mes, etc.), de modo que pasado este tiempo el certificado caduca y el usuario no puede entrar. El problema más importante lo tendrás cuando necesites prohibir el acceso a usuarios con certificados a largo plazo. Para hacerlo debemos crear una lista de revocación de certificados.

En mi caso (CentOS5 y RHEL5) tuve algunos problemas de configuración. Openssl debería de mantener una lista de los certificados que emite y sus números de serie sin embargo tal como hemos generado nuestros certificados no lo hace. Para solucionarlo, creamos a mano la lista.

touch /etc/pki/CA/index.txt

Ahora debemos editar el archivo de configuración de Openssl para reflejar algunos parámetros. Por defecto indica una ruta relativa hacia los directorios donde dejará los certificados, pero no me funcionó bien hasta que no le puse rutas absolutas. Modificamos pues la ruta al directorio y le indicamos donde están la clave privada y el certificado de nuestra CA.

Editar /etc/pki/tls/openssl.cnf

dir             = /etc/pki/CA              # Where everything is kept
certificate     = /path/a/CAXplotacert.pem    # The CA certificate
private_key     = /path/a/CAXplotaKey.pem # The private key

Ahora sí, podemos revocar un certificado de manera tan sencilla como:

openssl ca -revoke certificado-cliente.pem

A partir de la lista con el estado de los certificados debemos crear una lista de revocación que usaremos para indicar al servidor web los certificados que no debe permitir.

openssl ca -gencrl -out recovados.crl

Es posible que al lanzar este comando te apareza otro error relacionado con crlnumber. Esto viene relacionado con lo que os indicaba antes y es simplemente que no existe el archivo de números de serie. Para solucionarlo, simplemente lo creamos:

echo "01" > /etc/pki/CA/crlnumber

Y volvemos a generar la lista de revocación. Cada vez que revoques un certificado deberás repetir esta operación para tener la nueva lista.

Ya sólo falta indicarle a Apache qué certificados no debe permitir. Añadimos a la configuración de nuestro virtual host, en el mismo sitio donde configuramos anteriormente el certificado SSL, la siguiente línea:

SSLCARevocationFile path/a/revocados.crl

Eso es todo, ya tenemos nuestro servido SSL funcionando, nuestra aplicación protegida con certificados de cliente y la opción para revocar certificados.

Comprobando los certificados con PHP

Queremos ir un poco más allá. Queremos que, desde nuestra aplicación, podamos leer el certificado SSL del cliente y comprobar quién es. Seguro que a ti se te ocurren mejores cosas que hacer con estos datos 😉 . Lo primero que tendremos que hacer será configurar Apache para que pase los datos del certificado. En la configuración de tu virtual host añades:

<Files ~ ".(cgi|shtml|phtml|php?)$">
 SSLOptions +StdEnvVars +ExportCertData
</Files>

Ya está. Si ahora haces un print_r($_SERVER) en la aplicación donde tienes instalado tu certificado SSL de cliente verás entre todos los datos algo como:

[SSL_CLIENT_S_DN] => /C=ES/ST=Valencia/L=Valencia/O=Osusnet/CN=blog.osusnet.com/emailAddress=osusENosusnet.com
[SSL_CLIENT_S_DN_C] => ES
[SSL_CLIENT_S_DN_ST] => Valencia
[SSL_CLIENT_S_DN_L] => Valencia
[SSL_CLIENT_S_DN_O] => Osusnet
[SSL_CLIENT_S_DN_CN] => blog.osusnet.com
[SSL_CLIENT_S_DN_Email] => osusENosusnet.com

Tienes toda la información que necesitas, son los datos que se solicitaban al crear la solicitud de certificado. Generando estos parámetros de manera adecuada podrás saber quién entra a tu aplicación a partir del certificado cliente que usa.

Eso es todo por hoy. De una manera muy sencilla hemos añadido un poco más de seguridad a una aplicación web de acceso privado y con un número de usuarios acotado. También hemos aprendido y poco más sobre certificados SSL y hemos visto cómo firmar nuestros propios certificados, igual de fiables que los comerciales. Espero que os sirva de algo 😛 .

Error con el smtp autentificado con los clientes de Nokia a través de Qmail

Como os decía hace unos días, recientemente he cambiado el Nokia N70 que llevaba desde hace algo más de dos años por un Nokia E51. La característica más importante de este nuevo terminal es la disponibilidad de conexión WIFI, con lo cual, si tienes un poco de suerte y encuentras un punto de acceso abierto, puedes tener acceso a tu correo desde cualquier punto. No he tenido muchos problemas para encontrar puntos de acceso y habitualmente he podido leer el correo. El problema llega a la hora de enviarlo ya que siempre obtengo un error en el envío. Cansado de no saber qué ocurría y dado que voy a estar unos días fuera de casa, decidí investigar un poco.

Comencé por ver cual era la comunicación del cliente de correo con mi servidor. Para ello lancé un sniffer en mi máquina, en este caso Wireshark, nombre actual del Ethereal de toda la vida. Para no volverme loco con todo el tráfico que iba a ver añadí un filtro, de manera que sólo se mostraría el tráfico que venía desde la IP de mi conexión ADSL (11.22.33.44), así sólo vería lo que estaba generando mi propio móvil.

tshark -i2 -f "src host 111.222.333.444"

Y este fué el resultado:

3.197223 11.22.33.44 -> 55.66.77.88 TCP 49537 > smtp [SYN] Seq=0 Win=64240 Len=0 MSS=1460 TSV=1218449730 TSER=0 WS=0
3.197263 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=389435772 TSER=1218449730 WS=7
3.347274 11.22.33.44 -> 55.66.77.88 TCP 49537 > smtp [ACK] Seq=1 Ack=1 Win=64240 Len=0 TSV=1218663480 TSER=389435772
3.356257 55.66.77.88 -> 11.22.33.44 SMTP Response: 220 servidor ESMTP
3.509426 11.22.33.44 -> 55.66.77.88 SMTP Command: EHLO
3.509451 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [ACK] Seq=29 Ack=22 Win=5888 Len=0 TSV=389436084 TSER=1218828105
3.509490 55.66.77.88 -> 11.22.33.44 SMTP Response: 250-servidor
3.663467 11.22.33.44 -> 55.66.77.88 SMTP Command: AUTH CRAM-MD5
3.813384 11.22.33.44 -> 55.66.77.88 SMTP Command: amlxxxxxxWFzIDkxNTM4MxxxxxxyYjVjOTdmYxxxxxxxDFhZDg2MjEw
3.853554 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [ACK] Seq=197 Ack=95 Win=5888 Len=0 TSV=389436428 TSER=1219132230
8.817212 55.66.77.88 -> 11.22.33.44 SMTP Response: 535 authorization failed (#5.7.0)

Es decir, me estaba fallando la autentificación a través de CRAM-MD5 a pesar de que en el programa de correo del móvil tenía correctamente introducidos el usuario y la clave y el servidor, obviamente, se presentaba diciendo que soportaba este método.

[osus@servidor ~]# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 servidor ESMTP
ehlo
250-servidor
250-AUTH LOGIN CRAM-MD5 PLAIN
250-PIPELINING
250 8BITMIME

Investigando un poco averigué que para habilitar la autentificación CRAM-MD5 en Qmail, el MTA de correo que utilizo, además de instalar el parche qmail-smtp-auth es necesario sustituir el original checkpasswd  por cmd5checkpw. Con este argumento caí en la cuenta de que nunca jamás he tenido esta autentificación funcionando en mis servidores 😀 , el servidor indicaba que sí pero la realidad era que no 😛 . Ocho años utilizando Qmail y ahora me doy cuenta de que no funcionaba.

No quería complicarme mucho así que, si hasta ahora no la tenía, tampoco me importaba seguir así. Pensé entonces que, si en vez de responder a los clientes que soportaba CRAM-MD5, no se lo decía, el cliente del E51 escogería el AUTH LOGIN de toda la vida y funcionaría. ¿Cómo hacer esto? Sencillo, Qmail es de código abierto y tenemos el código fuente a nuestra disposición.

Parcheando Qmail

En los fuentes de mi Qmail (con todos sus parches) busqué la cadena en cuestión:

grep  CRAM-MD5 *

Y ahí me apareció el objetivo de nuestro parche, qmail-smtpd.c.

Dicho y hecho, abrimos el archivo con vi y buscamos la cadena en cuestión (:/CRAM-MD5) llegando a:

out("rn250-AUTH LOGIN CRAM-MD5 PLAIN");

Esto va a ser sencillo. Modificamos esa/s línea/s de manera que quede/n:

out("rn250-AUTH LOGIN PLAIN");

De este modo, cuando el cliente pregunte le diremos que soportamos LOGIN y PLAIN, pero no CRAM-MD5. He explicado esto en plural porque es más que probable que tengas dos líneas iguales y no sólo una. Puedes dejar sólo una modificada como he indicado o modificar las dos.

Compilamos ahora con un simple make. Como sólo hemos  modificado este archivo únicamente se compilará este. Hacemos una copia de seguridad de nuestro /var/qmail/bin/qmail-smtpd (por si algo no va bien) y copiamos el nuevo a esta ruta dejando los permisos, owner y group igual que tenía el que había.

Si ahora pruebas a enviar un email desde el Nokia E51 comprobarás que funciona correctamente, el cliente del teléfono escoge otro de los sistemas. Utilizando de nuevo el sniffer verás que ahora escoge AUTH LOGIN como método de autentificación.

Ya puedo enviar correo desde mis servidores con el teléfono. Gracias a un sencillo ejercicio de escucha del tráfico de red hemos averiguado cual era el problema.