Archivo de la categoría: PHP

Acelera tus scripts PHP con eAccelerator

Como ya sabréis la mayoría, PHP es un lenguaje de programación de script, es decir, el código fuente no se compila generando un binario o un bytecode sino que el intérprete de PHP lo lee, compila y ejecuta cada vez que se llama a ese script. No hay que ser muy espabilado para darse cuenta de que este proceso de compilación permanente tiene que tener penalizaciones en cuanto a rendimiento, supongo que se compensa con la facilidad de desarrollo al no tener que compilar cada prueba que se quiere hacer.

Hace ya unos años descubrí eAccelerator, fork del conocido Turck MMCache. La primera vez que lo probé e hice tests de rendimiento no me creía la diferencia que había entre el antes y el después, poco a poco y a base de más pruebas me fui convenciendo y hoy en día es parte fundamental de todos los servidores a los que tengo que meter mano. eAccelerator trabaja compilando los scripts PHP y cacheando esta compilación de manera que la siguiente vez que se solicita está ya compilado y no hay que volver a hacerlo. Es importante hacer notar que maneja automáticamente los cambios de versiones a través de la fecha de modificacion (mtime). Si el script es más nuevo que el que tiene cacheado, lo vuelve a compilar, con lo que, de cara al desarrollador, el sistema sigue teniendo la ventaja de no necesitar compilados ni empaquetados.

La instalación es muy sencilla, descargamos el código fuente desde la web oficial y lo compilamos.

phpize
./configure
make
make install

Ya está el módulo instalado, ahora tendremos que activarlo. En mi CentOS es muy sencillo, simplemente debo añadir un nuevo archivo a /etc/php.d y será leído automáticamente en el siguiente reinicio de Apache. El contenido del archivo será algo así:

[osus@servidor ~]# cat /etc/php.d/eacclerator.ini
zend_extension="/usr/lib/php/modules/eaccelerator.so"
eaccelerator.shm_size="16"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
eaccelerator.allowed_admin_path="/ruta/a/tu/servidor/de/control"

Todos los parámetros están bien explicados en la documentación oficial. El más importante es el último ya que, como veremos más adelante, indica una ruta dentro de tu servidor web desde donde podrás ver un pequeño panel de control de eAccelerator.

Ahora reinicia Apache y comprueba si funciona de verdad:

[osus@servidor ~]# php -v
PHP 5.1.6 (cli) (built: Nov 12 2008 11:22:34)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies
    with eAccelerator v0.9.5.2, Copyright (c) 2004-2006 eAccelerator, by eAccelerator

Parece que sí. Aprovecharemos para configurar la interfaz web que comentaba que nos permite controlar algunas funcionalidades. Para ello copiamos desde la carpeta con el código fuente el archivo control.php a la ruta indicada anteriormente y configuramos los parámetros de autenticación que nos pedirá, usuario y clave, al principio de este archivo.

if (!function_exists('eaccelerator_info')) {
    die('eAccelerator isn't installed or isn't compiled with info support!');
}

/** config **/
$user = "osus";
$pw = "bombona";
/** /config **/

Ya está, sólo queda acceder a esta ruta web para ver algo semejante a esta imagen: información del estado de la caché, posibilidad de desactivar el cacheo, limpiar caché…

Eaccelerator admin

Analizando el rendimiento

Todo esto está muy bien y suena muy bonito, pero ¿mejora de verdad el rendimiento? ¿qué beneficios reales obtenemos? La pregunta del millón.

Utilizaremos Apache Benchmark para hacer pequeñas pruebas de carga sobre el servidor. Esta utilidad viene siempre con Apache, con lo que no tendrás que instalar ningún software adicional. La sintaxis es muy sencilla:

ab -n 100 -c 5 http://dominioentuservidor.com/script.php

Con esto estaremos diciendo a “ab” que lance 100 peticiones contra la url indicada con una concurrencia máxima de 5 solicitudes. El resultado seria algo similar a esto:

[osus@servidor ~]# ab -n 100 -c 5 http://tudominio.com/
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking tudominio.com (be patient).....done

Server Software:        Apache
Server Hostname:        tudominio.com
Server Port:            80

Document Path:          /
Document Length:        125397 bytes

Concurrency Level:      5
Time taken for tests:   52.549041 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      12559900 bytes
HTML transferred:       12539700 bytes
Requests per second:    1.90 [#/sec] (mean)
Time per request:       2627.452 [ms] (mean)
Time per request:       525.490 [ms] (mean, across all concurrent requests)
Transfer rate:          233.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      195  232  45.7    211     417
Processing:  1687 2356 401.0   2257    3609
Waiting:      342  620 240.6    546    1756
Total:       1933 2589 400.7   2487    3815

Percentage of the requests served within a certain time (ms)
  50%   2487
  66%   2611
  75%   2798
  80%   2903
  90%   3090
  95%   3475
  98%   3791
  99%   3815
 100%   3815 (longest request)

Bien, pues a partir de esta información he hecho un pequeño estudio estadístico para valorar el cambio de rendimiento.  Se hicieron dos baterías de pruebas, primero con eAccelerator desactivado y después activado. En cada grupo se lanzaron diez tests utilizando “ab” con 100 peticiones. Vale, sí, la muestra es pequeña, pero veréis que suficiente para ver el cambio. Estos fueron los resultados.

eaceleratorstats.gif

La última fila establece los porcentajes de las pruebas con eAcclerator respecto de no utilizarlo. Interpretemos los resultados:

  • Tiempo total: utilizando el acelerador las pruebas se han ejecutado en el 73% del tiempo empleado al no utilizarlo. Eso supone un 27% menos de tiempo, una cifra nada despreciable.
  • Peticiones por segundo: con eAccelerator se ejecuta un 36% más de peticiones por segundo que sin él. Sobran más comentarios.
  • Tiempo mínimo: El tiempo mínimo empleado para atender una petición desciende un 22% al utilizar eAccelerator.
  • Tiempo máximo: este valor es sumamente llamativo, con eAccelerator se necesita un 42% menos de tiempo en el peor de los casos.

¿Necesitas más argumentos? 😛 . Otro día hablaremos de memcached.

Otras utilidades para un servidor de correo

A veces un servidor de correo puede sernos de muchísima utilidad si sabemos cómo manejarlo correctamente. En el artículo de hoy veremos como utilizar nuestro MTA para ejecutar automáticamente acciones cuando se recibe un determinado email o con los parámetros que definamos. Una vez conozcamos la teoría plantearemos dos casos prácticos como ejemplo.

Para comenzar necesitaremos un servidor Linux con Qmail como MTA. Supongo que cualquier otro servidor de correo servirá (Sendmail, Postfix), yo lo personalizo en Qmail porque es el que conozco y utilizo, pero estoy seguro de que con los demás se puede hacer lo mismo.

La teoría

Para entender cómo funciona la idea debemos entender primero cómo Qmail realiza la entrega de mensajes en los buzones locales. Es un tema sobre el que hay bastante literatura buscando en Google pero que puede no quedar muy claro en una lectura rápida. Es el famoso lío de los archivos .qmail.

Cada vez que se crea un usuario del sistema se debe crear, si va a recibir emails, un archivo .qmail-default en su directorio de usuario.

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-default
./Maildir/

Normalmente este archivo se crea automáticamente ya que al instalar qmail se habrá creado una copia de este archivo en el esqueleto de creación de usuarios /etc/skel:

[jiglesias@lerez ~]# ls -la /etc/skel
total 56
drwxr-xr-x  4 root root  4096 Jul 29 18:29 .
drwxr-xr-x 97 root root 12288 Dec 30 04:08 ..
-rw-r--r--  1 root root    33 Feb  1  2008 .bash_logout
-rw-r--r--  1 root root   176 Feb  1  2008 .bash_profile
-rw-r--r--  1 root root   124 Feb  1  2008 .bashrc
drwx------  5 root root  4096 Aug 29  2007 Maildir
-rw-r--r--  1 root root    12 Jan  2  2008 .qmail-default
-rw-r--r--  1 root root   658 Sep 12  2006 .zshrc

Para el que no lo sepa, el esqueleto son los archivos que se copiarán al directorio de usuario (con los permisos de éste) cada vez que se crea uno nuevo. Si quieres que todos tus usuarios tengan algún archivo automáticamente, éste es tu sitio. En nuestro caso vemos que además del .qmail-default está el directorio Maildir, el de entrega por defecto del correo en qmail. Teniendo un usuario este archivo y este directorio, podrá recibir correo.

Vale vale, vas muy deprisa. Todavía no has explicado para que sirve el .qmail-default ese.  Cierto. Los archivos .qmail indican las reglas de entrega de los mensajes en base a dos parámetros:

  • El nombre del archivo .qmail-xx hace referencia a la cuenta de correo sobre la que actúa.
  • El contenido indica qué hacer con el correo.

Supongamos un usuario (jiglesias)  que recibe el correo de dos cuentas distintas (jiglesias@… y osus@…).

Por defecto todo su correo irá a su buzón ya que es lo que indica el archivo .qmail-default. Queremos ahora que el comportamiento sea distinto dependiendo de la cuenta a la que vaya dirigido, creamos entonces los archivos .qmail para las direcciones:

  • .qmail-jiglesias : controla el correo que vaya a jiglesias@…
  • .qmail-osus : controla el correo que vaya a osus@…

Podemos incluso ir un poco más lejos con un archivo .qmail-jiglesias-default, y controlaríamos el correo que vaya a cualquier dirección del tipo jiglesias-XXXX@…, es decir, cualquier dirección que comience por jiglesias- será controlada por este archivo .qmail.

En el caso básico, que es el que veíamos, la entrega se realiza al buzón de correo del usuario (el directorio Maildir) pero podríamos hacer otras cosas en función del contenido del archivo .qmail encargado de procesar la entrega del correo:

Reenvío a otra cuenta:

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-jiglesias
[email protected]

Reenvío a un programa/script:

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-osus
|preline /usr/bin/programa

Combinación de los anteriores

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-jiglesias
./Maildir/
[email protected]
|preline /usr/bin/programa

El caso que nos interesa es el segundo, es decir, pasar la entrega del email a un script que se encargará de analizar el email y tomar decisiones.

Te habrá llamado la atención el |preline del archivo .qmail. Es el sistema que prepara un email para ser procesado y entregado a otro script añadiendo algunas cabeceras. La salida del script que reciba el email es importante ya que de ella depende el resultado de la entrega final de email, en concreto es importante saber que si queremos rechazar un email habrá que terminar el script con un exit (100), esto indicará a qmail que debe rechazar ese mensaje. Veremos más adelante la utilidad de esta salida.

La práctica

Una vez sabemos cómo pasar el control de un email recibido a un script, veamos como tratarlo. Lo haremos con un script PHP. En nuestro archivo .qmail haremos algo como:

[jiglesias@lerez ~]# more .qmail-jiglesias
|preline /usr/bin/php /home/jiglesias/prueba.php

Con esto hemos terminado el trabajo en el servidor de correo. Veamos ahora como parsear el email desde PHP.

Lo primero que debemos hacer es recoger el contenido del email desde el script a través de la entrada estándar,  después ya podemos procesar el email como una cadena de texto.

<?php
$email=file("php://stdin");
$email=implode("", $email);
?>

Con estas sencillas dos líneas de código tendremos en nuestro script el contenido del email. Ahora sólamente debemos procesarlo. Podemos hacerlo línea por línea por nuestra cuenta o apoyarnos en alguna librería. Yo utilizo Mail_mimeDecode de Pear. La ventaja de esta librería es que podemos obtener, además del texto del email, los archivos adjuntos.

<?php
$email=file("php://stdin");
$email=implode("", $email);
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['input'] = $email;
$structure = Mail_mimeDecode::decode($params);
$subject = trim($structure->headers['subject']);
$ddate = trim($structure->headers['date']);
$from = addslashes(trim($structure->headers['from']));
if(ereg("<(.*)>", $from, $p)) $from=$p[1];
if(ereg(""(.*)"", trim($structure->headers['from']), $pp))
    $nombre=$pp[1];
?>

Así podemos ya procesar el email y tomar las decisiones que consideremos oportunas. Podremos insertarlo en una base de datos, lanzar otros procesos automatizados, enviar avisos por SMS… lo que se nos ocurra.

Casos prácticos

La pregunta clave, después de ver la teoría, sería ¿para qué me sirve esto?.  Os propongo dos aplicaciones que yo he hecho.

Sistema de soporte

El típico sistema de tickets de soporte. En el asunto del email se arrastra el identificador del ticket, por ejemplo [#123445]. Tendremos que analizar el asunto y comprobar si aparece el patrón predefinido. Si no existe estamos ante un nuevo ticket e insertamos los datos en nuestra base de datos, en caso contrario es una respuesta a una incidencia anterior y ahí tendremos el identificador. Sencillo ¿no?. Podemos incluso adjuntar a nuestras indicendias archivos que puedan llegar en el email.

Envío de archivos desde el movil

El segundo ejemplo sería semejante al anterior técnicamente pero distinto conceptualmente. La mayoría de los móviles (salvo los de gama alta) no pueden enviar archivos desde los formularios wap (<input type=”file”>). La alternativa es que el usuario envie un email o un MMS (la mayoría de operadoras permiten el envío a direcciones de email) con sus archivos. Nuestro script procesará el contenido del mail recibido, decodificará los archivos y los tratará como sea oportuno.

Son dos sencillos ejemplos de cómo utilizar el email para automatizar tareas, pero, como he comentado, podríamos hacer todo lo que se nos ocurra, desde enviar un SMS de aviso hasta incluso reiniciar nuestro servidor o lanzar cualquier otra tarea.

Fotomatón, o haciendo fotos instantáneas online

La idea es sencilla, conectas con la webcam de tus usuarios, les haces una foto y la envías a una galería donde se pueden ver los caretos de tus visitantes. La implementación es casi tan sencilla como la teoría y el resultado es increíble y original.

El sistema es el mismo que utilizaba BloggerSnap, un servicio que tuvo un increíble éxito en su lanzamiento y que parece cerrado desde abril. Hace un año estaba por encima del puesto 40.000 en Alexa, seguramente estuvo incluso más alto. Imagino que sería muy complicado mantener el coste del ancho de banda con tanta popularidad. Hace un par de años estuvimos a punto de lanzar un servicio similar y lo desestimamos ya que no veíamos la manera de monetizarlo mientras que, a poco que tuviese éxito, los gastos se dispararían.

En un escenario normal dependerías de software que el usuario tendría que instalar para que se hiciese la foto y posteriormente te la enviase, con lo cual no conseguirías el objetivo de la instantaneidad, pero existe una solución que permite hacerlo todo mediante una aplicación online: Flash, en nuestro caso Flex 😛 .

La potencia de la plataforma Flash en el tratamiento de audio y vídeo va mucho más allá de lo que pueda parecer y permite crear aplicaciones multimedia avanzadas con participación del usuario de manera bastante rápida. Si además dispones de dinero suficiente, puedes combinarlo con Flash Media Server y los límites los pondrá tu imaginación. Otro día crearemos un videochat 😉 .

La clave es el acceso a la cámara y al micrófono del equipo que permite la plataforma Flash previa autorización del usuario, obviamente. Si lo autorizas, el player conectará con tu webcam y comenzará a mostrar el vídeo de la misma y, si la aplicación lo requiere, enviarlo a otros usuarios. Lo mismo se puede hacer con el audio.

En el taller de hoy haremos una pequeña aplicación para crear una galería de fotos instantáneas en web/blog. Los usuarios que lo deseen pueden hacerse la foto con su propia webcam y ésta quedará alojada en la galería. Simple pero llamativo.

La aplicación constará de dos partes:

  • Zona de capturas: una aplicación desarrollada en Flex que permitirá a los usuarios hacerse fotos sin más necesidad que tener una webcam.
  • Galería: una sencilla página html donde se listan todas las fotos que se han hecho los usuarios.

Capturando imágenes

Aunque pueda parecer algo muy complicado veremos a continuación que la facilidad de conectar con la webcam en Flex es impresionante.

Nuestra aplicación consta de dos únicos componentes, una imagen y un botón. La imagen nos aportará simplemente algo de estilo y el botón lo utilizaremos de disparador de nuestra cámara de fotos online.

Hay tres tareas bien diferenciadas:

  • Conectar con la webcam.
  • Capturar la foto.
  • Enviarla a nuestro servidor.

Conectar con la webcam

Con este sencillo código estaremos visualizando en nuestra aplicación la webcam que el usuario tenga conectada a su equipo.

private function insertWebcamVideo():void{
	var videoHolder:UIComponent = new UIComponent();
	if(Camera.names.length>0){
	   camera = Camera.getCamera();
	   if(camera==null || camera.width==-1 || camera.height==-1 || camera.fps==-1){
	   		Alert.show("Lo sentimos, no tienes cámara", "Error", 4, null, null, iconoAlerta);
	   		captura.enabled=false;
	   		camera=null;
	   }else{
		   camera.addEventListener(StatusEvent.STATUS, cerrar);
		   video = new Video(camera.width, camera.height);
		   video.attachCamera(camera);
		   videoHolder.addChild(video);
		   videoHolder.width=video.width;
		   videoHolder.height=video.height;
		   videoHolder.visible=true;
		   videoHolder.y=0;
		   caja.addChildAt(videoHolder, 0);
		   caja.removeChild(fotillo);
	   }
	}else{
   		Alert.show("Lo sentimos, no tienes cámara", "Error", 4, null, null, iconoAlerta);
   		captura.enabled=false;
	}
	//Security.showSettings(SecurityPanel.CAMERA);
}

Tan fácil como comprobar primero si tiene webcam (sino, obviamente, no puede hacerse fotos 😛 ) y posteriormente añadirla a un componente de vídeo que habremos incorporado a un UIComponent para mostrarlo en la aplicación. En este momento ya tenemos la cámara del usuario en vivo en la aplicación.

Capturar la foto

Teniendo el flujo de vídeo en la aplicación, queremos que cuando el usuario presione el botón “Capturar” se le haga la foto.

private function getSnapshot():void{
	var snapshot:BitmapData = new BitmapData(video.width, video.height, true);
	var m : Matrix = new Matrix();
	snapshot.draw(video, m);
	var jpegEnc:JPGEncoder=new JPGEncoder(75);
	var jpegDat:ByteArray = jpegEnc.encode(snapshot);
	var img_src:String = base64Encode(jpegDat);

	if(img_src.length>1600){
		//imagen correctamente capturada
	}else{
		Alert.show("La imagen capturada parece estar vacía", "Error", 4, null, null, iconoAlerta);
	}
}

Con este otro código conseguimos este efecto. Se obtiene una captura bit a bit del objeto de vídeo y se comprime en JPEG para enviarla al servidor. ¡Ya tenemos la foto hecha!

Enviar la foto

Utilizaremos un HTTPService para enviar por POST la foto que hemos realizado y que guardaremos en una base de datos. Añadimos a la función anterior el código para enviar la foto.

private function getSnapshot():void{
	var snapshot:BitmapData = new BitmapData(video.width, video.height, true);
	var m : Matrix = new Matrix();
	snapshot.draw(video, m);
	var jpegEnc:JPGEncoder=new JPGEncoder(75);
	var jpegDat:ByteArray = jpegEnc.encode(snapshot);
	var img_src:String = base64Encode(jpegDat);

	if(img_src.length>1600){
		var temp:Object=new Object();
		temp.imagen=img_src;
		httpService.send(temp);
		cargandoWindow = ventanaEspera(PopUpManager.createPopUp(this as DisplayObject, ventanaEspera, true));
	}else{
		Alert.show("La imagen capturada parece estar vacía", "Error", 4, null, null, iconoAlerta);
	}
}

<mx:httpservice id="httpService" showbusycursor="false" useproxy="false" method="POST" resultformat="text" url="upload.php" result="onResult()"
fault="onHTTPFault(event)" />

Ya está. El script upload.php de tu servidor recibirá por POST la variable “imagen” con el contenido de tu foto. Sólo habrá que guardarla en un archivo y habrás terminado la parta más ¿complicada? del proyecto.

if($_POST['imagen'] & strlen($_POST['imagen'])!=0 & strlen($_POST['imagen'])>1600){
        $bmd=base64_decode($_POST['imagen']);
        $im = fopen("fotos/foto.jpg",'w');
        fwrite($im,$bmd);
        fclose($im);
}

Antes de guardar la imagen en disco podrías añadir un registro a una base de datos que te permita generar después la galería. Esa parte queda a tu elección.

Generando la galería

Esta es la parte fácil, simplemente creas un script PHP (o en el lenguaje que prefieras) que liste las fotos que has ido guardando en la base de datos paginando como creas oportuno. Creo que sobran más comentarios para algo tan fácil.

Conclusiones

Hoy hemos visto cómo de una manera extremadamente rápida y sencilla se puede conectar con la webcam (y el micrófono) de los usuarios. Sólo hemos visto como hacer una fotografía instantánea de lo que se está viendo a través de la cámara, pero el sistema abre un inmenso mundo de posibilidades.

Descárgate aquí el código del proyecto.

Primeros pasos con Flash Media Server (2), autentificando usuarios del chat

Hace unos días veíamos como crear un sencillo chat con FMS.

Hoy, a petición popular, vamos a añadir la primera mejora a nuestra aplicación. Por defecto habíamos diseñado un chat público, cualquiera podía conectarse. Sin embargo, puede que deseemos que sólo nuestros usuarios registrados puedan utilizarlo. Necesitamos entonces que nuestros clientes se puedan autentificar contra nuestra base de datos pero no podemos dejar esa responsabilidad en manos de la aplicación cliente, cualquiera podría hacer otra que se conecte a nuestro servidor sin requerir autentificación.

FMS no puede conectarse con ningún tipo de bases de datos, no es su función, aunque cada vez hay más gente solicitando esta funcionalidad y se espera que en la próxima versión (FMS4) haya alguna novedad al respecto. Sea como fuera, de momento no hay métodos nativos para acceder a bases de datos así que sólo se puede recurrir a lo que un día fue Flash Remoting y hoy son los servicios RPC bajo AMF, el método estándar de acceso a datos desde aplicaciones Flash (con el permiso de los webservices SOAP). Con el tiempo fueron apareciendo versiones en los distintos lenguajes para utilizar el protocolo AMF fuera del amparo de Macromedia (ahora Adobe), pero sin duda uno de los pioneros fue amfphp, el que nos permite comunicar aplicaciones Flash con el servidor a través de PHP.

AMF es un formato para serialización de datos e invocación de métodos remotos y es independiente al lenguaje con el que trabaja el servidor, simplemente debe cumplir las especificaciones. FMS, como no podía ser de otra forma, puede comunicarse con nuestro servidor gracias a este protocolo AMF y esto es precisamente lo que vamos a utilizar para autentificar a nuestros usuarios:

dibujo1.jpg

El proceso es mucho más sencillo de lo que parece.

Comenzaremos por descargar amfphp desde la web oficial.  Lo descomprimes y subes la carpeta “amfphp” a tu servidor en la ruta que desees. Ya tienes lo que es el cerebro del sistema listo, para comprobarlo prueba la URL:

http://tuservidor/amfphp/gateway.php

Si todo va bien, prueba ahora el navegador de servicios, herramienta imprescindible para probar tus métodos remotos:

http://tuservidor/amfphp/browser/

Si no has visto ningún error es que todo va bien. Veamos ahora como crear tu servicio de autentificación.

Dentro de la carpeta amfphp que has subido a tu servidor encontrarás la carpeta services. Crea dentro de ella un nuevo directorio con el nombre de tu aplicación, por ejemplo chat. Creamos ahora dentro de esa carpeta las clases y métodos que se necesiten para crear el servicio de autentificación. Por ejemplo, en mi caso creo Chat.php:

<?
class Chat{
    var $conn;    function Chat(){
      $this->conn=mysql_connect("host", "usuario", "clave");
      mysql_select_db('db', $this->conn);
    }
function login($nick, $pass){
        $rs=mysql_query("select * from usuarios where nick='$nick' AND pass='$pass'");
        if(mysql_num_rows($rs)>0)
          return 1;
        else
          return 0;
    }
}
?>

Sencillo ¿no?. Nuestra clase tiene un método login que nos dice si la pareja nick/clave existe en nuestra base de datos. Obviamente esto es sólo un ejemplo, en tu aplicación haz la clase con todos los métodos y la lógica que necesites.

Si ahora vas de nuevo al Service Browser verás que te aparece la nueva aplicación chat con la clase que has creado. Si accedes a ella puedes probar el método login pasándole los parámetros necesarios. Más sencillo imposible.

Ya tenemos la lógica de autentificación lista. ¿Cómo le decimos ahora a FMS que la utilice? Modificamos el archivo main.asc que habíamos hecho en el capítulo anterior para que antes de aceptar la conexión de un usuario la autentifique.

application.onConnect = function(client, 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){
        var myServer = NetServices.createGatewayConnection("http://tudominio/amfphp/gateway.php");
        var servicio = myServer.getService("chat.Chat", this);
        var callserver=servicio.login(email, clave);

        this.login_Result=function(result){
            //trace("RESULTADO LOGIN"+result);
            if(result>0){
                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 = "*** Entra " + client.nick + " ***";
                this.chat_so.send("onMsgSend", msg);
                client.call("onUserid", null, client.uid, client.nick);
                this.uid++;
            }else{
                var err = new Object();
                err.message = "Email o contraseña incorrectos";
                application.rejectConnection(client, err);
            }
        }
    }else{
        var err = new Object();
        err.message = "Ya hay un usuario con ese nick";
        trace(err.message);
        application.rejectConnection(client, err);
    }
}

Es tan simple como añadir la comprobación del usuario y clave de nuestro cliente. Indicamos la url de nuestro gateway (amfphp) y llamamos al método login con los datos correspondientes. Lo más importante es la forma en que obtenemos los datos que devuelve el metodo. La llamada es asíncrona, no se paraliza la actividad esperando por un resultado sino que se continua la actividad esperando a recibir la respuesta. Para eso se supone que debe haber definida una función nombremetodoremoto_Result, en nuestro caso login_Result ya que login es el método remoto al que llamamos. Cuando se llame a esta función sabremos si el usuario existe en nuestra base de datos, con lo que aceptamos su conexión al chat, o no existe y la rechazamos.

Como habéis visto, es muy sencillo llamar a métodos remotos desde FMS de manera que aumenten las posibilidades de tu aplicación ya que puedes hacer uso de bases de datos y de sistemas avanzados y siempre de manera muy simple.

Yo te he dado las pistas de cómo hacerlo, el resto queda a tu imaginación.

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 😛 .

Consumiendo webservices SOAP desde PHP

Recientemente nos han llegado un par de proyectos en los que debemos consumir webservices SOAP para obtener datos. La verdad es que hacía bastante tiempo que no los veía en mi trabajo habitual. Hace algunos años eran muy habituales para casi cualquier cosa que implicase comunicación con fuentes externas, sin embargo los últimos años habían caído un poco en desuso para cosas sencillas puesto que complicaban bastante un trabajo que con una simple petición HTTP y un XML básico se podría resolver. Precisamente ésta ha sido siempre una de las mayores críticas al protocolo SOAP, el elevado consumo de ancho de banda para una sencilla petición.

Hasta ahora siempre había utilizado nusoap para realizar llamadas SOAP desde PHP, sin embargo  me encontré con un problema al acceder a un servicio de un importante medio de comunicación internacional. La llamada con nusoap no devolvía resultados mientras que desde el sistema de pruebas html todo funcionaba correctamente.

Después de darle mil vueltas y no encontrar ningún error (otras peticiones al mismo servicio sí que funcionaban) me dí cuenta que PHP ya tiene un conjunto de funciones SOAP nativas, con lo que no necesitaría nusoap. La duda era saber si funcionaría bien, como así fue. Tras añadir la extensión adecuada surge una pequeña incompatibilidad. No puedes usar nusoap y las funcionas nativas en la misma instalación de PHP. Dicho de otro modo, si activas la extensión SOAP, nusoap dejará de funcionar y comenzará a lanzar mensajes de error ya que muchas de las funciones que utiliza tienen el mismo nombre que las nativas, que serían entonces nombres reservados. Si tienes alguna aplicación que utilice nusoap en la misma máquina tendrás que migrarla también para que utilice las funciones nativas.

El ejemplo de hoy será una sencilla llamada a un servicio que nos devuelve un listado de noticias.

El proceso es muy sencillo, necesitas la url del webservice, el método al que vas a llamar y los parámetros a pasarle y, como en cualquier servicio SOAP, te devolverá un XML.

$servicio="http://dominio.com/noticias?wsdl"; //url del servicio
$parametros=array(); //parametros de la llamada
$parametros['idioma']="es";
$parametros['usuario']="manolo";
$parametros['clave']="tuclave";$client = new SoapClient($servicio, $parametros);
$result = $client->getNoticias($parametros);//llamamos al métdo que nos interesa con los parámetros

Con estas sencillas instrucciones ya tenemos en $result el XML resultado de la llamada al servicio. Como trabajar con el XML es un poco engorroso, lo convertimos a un array asociativo de manera que nos sea más sencillo procesar los datos, para ello utilizamos la función obj2array que indico a continuación.

$result = obj2array($result);
$noticias=$result['resultado']['noticias'];
$n=count($noticias);

//procesamos el resultado como con cualquier otro array
for($i=0; $i<$n; $i++){
    $noticia=$noticias[$i];
    $id=$noticia['id'];
    //aquí iría el resto de tu código donde procesas los datos recibidos
}

function obj2array($obj) {
  $out = array();
  foreach ($obj as $key => $val) {
    switch(true) {
        case is_object($val):
         $out[$key] = obj2array($val);
         break;
      case is_array($val):
         $out[$key] = obj2array($val);
         break;
      default:
        $out[$key] = $val;
    }
  }
  return $out;
}

En la segunda línea nos quedamos con los elementos del array que nos interesa procesar. Si no sabes qué devuelve tu webservice puedes hacer un var_dump($result) y verás todo el resultado. En nuestro caso, como es una secuencia de noticias, nos quedamos con el elemento que tiene esas noticias.

Como os habréis dado cuenta, no me he preocupado del control de errores al llamar al webservice.  Eso os lo dejo como ejercicio a vosotros, que es agosto y no me apetece 😛 . En el manual de PHP está toda la información.

Y eso es todo amigos, hoy ha sido un ejemplo sencillo pero muy útil cuando necesitas utilizar SOAP.

Regala lotería y personaliza tus participaciones y décimos

¿Regalas lotería de Navidad?
¿Cansado de no saber cómo ser original a la hora de enviar el número?

Coincidiendo con las semanas previas al Sorteo Extraordinario de Navidad del año pasado lanzamos TusDecimos.com, algo tarde pero a tiempo para que se enviasen varios miles de décimos personalizados.

Después de numerosas peticiones hoy mismo hemos cambiado el diseño del décimo por el de este año, con lo que ya puedes enviar tus participaciones a tus amigos y familiares. Ya se sabe que en vacaciones se suele comprar lotería y la gente está ansiosa por regalarla. Los décimos generados puedes descargarlos, enviarlos por email o incluso al móvil del destinatario, con lo que lo tendrá bien guardado y no lo perderá, además de resultar un modo muy original de enviar el número.

TusDecimos.com

La idea surgió ni más ni menos que porque todos los años teníamos el mismo problema que la mayoría al regalar lotería, ¿cómo hacerlo distinto?. Te tocaba escanear el décimo, retocarlo a mano modificando la cantidad que regalabas… Ahora todo es mucho más sencillo.

Detalles técnicos

Ya que esto es un blog de corte técnico (aunque también publique de vez en cuando temas personales :P ), explicaré como hicimos el generador de décimos automático.

Lo principal, obviamente, es comprar un décimo :P y escanearlo, mejor a 300ppp puesto que así lo tenemos a muy buena resolución. Después debes retocarlo un poco, ajustar bien los bordes y, sobre todo, eliminar las rayas cruzadas que aparecen en la zona de los números. Nosotros vamos recortando porciones del dibujo de fondo y pegándolas sobre los números hasta que todo el dibujo queda limpio. Hay que repetir el mismo paso en las zonas de la derecha de fracción, seríe y precio del décimo. Así tendremos un billete limpio para personalizar a nuestro gusto.

 Decimo de loteria de navidad personalizado

Ahora debes obtener, a través de tu programa de dibujo como Gimp, las coordenadas que vas a necesitar:

  • Posición izquierda superior del cuadro imaginario donde iría el número del décimo.
  • Posición izquierda superior del cuadro imaginario donde iría la serie.
  • Ancho máximo del cuadro imaginario donde va la serie.
  • Posición izquierda superior del cuadro imaginario donde iría la fracción.
  • Ancho máximo del cuadro imaginario donde va la fracción.
  • Posición izquierda superior del cuadro imaginario donde iría la cantidad que regalas.
  • Ancho máximo del cuadro imaginario donde va la  cantidad que regalas.

A la hora de generar los números valoramos distintos modos de hacerlo, pero el resultado nunca era del todo satisfactorio. Lo primero en lo que piensas es en utilizar las librerías GD para insertar el texto del número, pero si lo pruebas verás que el resultado es muy pobre además de muy complicado de ajustar al espacio que ocupan, principalmente porque no tienes la fuente original que utiliza el Organismo de Loterías y Apuestas del Estado.

Finalmente optamos por un modo algo más artesano. Se hicieron los 10 dígitos básicos a mano, de manera que se ajusta perfectamente el tamaño de cada dígito y puedes hacer unos números bastante parecidos a los originales.  La serie, fracción y cantidad que juegas se hacen con una fuente normal con la función imagettftext, simplemente escoge la que más te guste. Para centrar el texto en la zona donde va utiliza la función imagettfbbox, te devolverá el tamaño del texto al pintarlo en el décimo, simplemente desplaza la posición de inicio la mitad del espacio sobrante. Nuestro script es un poco más elabordado puesto que juega, además, con el tamaño de la fuente de manera que se ajuste siempre perfectamente al espacio disponible. En líneas generales seria algo como:

$size=73;//tamaño de la fuente
$fuente="tufuente.ttf"; //fuente para los numeros de serie y fraccion
$serie="3"; //serie que vas a ponerle
$anchoespacio=135; //tamaño maximo del cuadro imaginario del espacio para la serie
$xinicio=1060; //posicion izquierda del cuadro imaginario de la serie
$yinicio=235; //posicion superior del cuadro imaginario de la serie
$bbox = imagettfbbox($size, 0, $fuente, $serie."ª");
$tw = ($anchoespacio-($bbox[2] - $bbox[0]))/2;
imagettftext($src_img, $size, 0, $xinicio+$tw, $yinicio, $tc, $fuente, $serie."ª");

Esto lo repetiríamos para serie, fracción y cantidad.

Para los dígitos del décimo, mucho más simple.

$decimo="23456";
$len=strlen($decimo);
$numero=array();
for($i=0; $i<$len; $i++)
    $numero[]=$decimo[$i];

for($i=0; $i<count($numero); $i++){
    $number = imagecreatefromgif ("numeros/".$numero[$i].".gif");
    imagecopymerge($src_img, $number, 450+($i*110), 105, 0, 0, 110, 133, 100);
}

Vamos copiando cada dígito sobre la posición del décimo donde iría. Sencillo. Ya tienes tu décimo personalizado. Si quieres puedes escalarlo o hacer lo que prefieras con él.

Con esto y teniendo cuidado de que los décimos te coincidan cada año en el mismo punto al escanearlos, tienes la aplicación resuelta para siempre.

Recuerda, si vas a regalar lotería, hazlo desde TusDecimos.com.

Insertar en Sql-Server desde fuentes de datos con php en la línea de comandos

La aparentemente cosa más tonta del mundo me ha tenido todo el día liado. Y no es la primera vez. Anteriormente me había pasado al procesar RSS, hoy ha sido al consumir unos webservices (SOAP).

Nuestros sistemas trabajan con MsSql, Apache, PHP5 y Windows 2003 Server. De vez en cuando necesitamos crear tareas que lean desde determinadas fuentes de datos y los inserten en una base de datos. La última vez que me había ocurrido era leyendo un RSS. Si lo lanzaba a través de la línea de comandos con php.exe la codificación que se guardaba en la base de datos era errónea, todos los acentos, eñes y demás caracteres no ASCII se perdían. El caso es que al ejecutarlo a través de Apache todo funcionaba bien.

La otra vez, por falta de tiempo, lo dejé pasar y montamos la tarea a través de Apache con wget. Pero ahora no era la mejor solución, hay que procesar varios webservices y la tarea se puede prolongar durante mucho tiempo, con lo que tener un hilo de apache corriendo tanto tiempo no me parece lo más adecuado.

Después de mucho googlear he encontrado la solución. Resulta que al ejecutar la tarea desde la línea de comandos se produce una conversión automática de ANSI a OEM:

Any clients running Windows NT or Windows 95/98 are considered ANSI clients. Console-based applications, such as the isql utility, are considered OEM clients.

Por lo tanto nos cambia la codificación sin remedio. Para ello hay una solución, y es ejecutar nuestro script a través de un wrapper que lance el proceso sin ser bajo consola, digamos que engañando al sistema operativo. En tu instalación de php tendrás un php-win.exe que hace exactamente lo mismo que el cliente habitual pero sin lanzarlo en la línea de comandos, parecerá que no ha hecho nada pero si abres el administrador de tareas verás el proceso php-win.ese corriendo e insertando correctamente en la base de datos.

Alertas y acciones en tu servidor a través de SMS

Tal y como he prometido sigo en mi empeño de, como mínimo, dos artículos por semana, así que ahí va el segundo de esta 🙂 .

Hoy veremos cómo aprovechar un móvil que ya no utilicemos para recibir alertas y ejecutar acciones remotamente en nuestros servidores.

Monté un sistema de este tipo por primera vez allá por 2002 y desde entonces lo hago siempre que tengo nuevos servidores que manejar, sobre todo en aquellos que hacen de monitor (Cacti y/o Nagios) y backup.

La idea inicial era recibir alertas de problemas en el servidor sin tener que estar delante de la máquina. En aquél momento no había más opciones que el email, pero si no estabas delante de la máquina no recibirías nada. Posteriormente aparecieron las primeras pasarelas SMS por HTTP (con una llamada HTTP y los parámetros adecuados, envías un SMS), pero aún hoy tienen un inconveniente, si tu máquina se queda sin conectividad (problemas de red, router, ataques DOS..) nunca recibirás alertas y nunca te enterarás de que algo ocurre.

Se me ocurrió entonces que podría utilizar un Nokia 3210 que acababa de jubilar ya que además me había comprado el cable para conectarlo al ordenador por el puerto serie, pero me faltaba lo más importante, el software. Me puse a investigar y encontré lo que buscaba, era lógico, no iba a ser yo el primero al que se le ocurriese esa idea 😉 . La solución se llamaba Gnokii, un software que se comenzó a desarrollar ni más ni menos que a finales de 1998. Por lo que me consta, dejó de actualizarse allá por 2003, aunque probablemente siga funcionando perfectamente en la mayoría de casos, sin embargo hace ya tiempo que apareció un fork de Gnokii, Gammu, que es el que utilizo desde hace ya unos años y sobre el que nos centraremos en este artículo. Además he cambiado la forma de conectar el terminal al ordenador, ya no es por el puerto serie sino por bluetooth.

Podríamos dividir el artículo en cuatro pasos:

  1. Conectando el móvil con el servidor.
  2. Enviando mensajes.
  3. Sistema de mensajería avanzado.
  4. Ejecutando acciones.

Conectando el móvil con el servidor

Comenzaremos por configurar el servidor. No voy a explicar detenidamente los pasos puesto que, aunque hoy en día son sencillos, se salen del alcance del artículo. De todos modos configurar el bluetooth hace cuatro o cinco años era bastante más complicado, hoy todas las distribuciones Linux tienen los paquetes preparados.

Lo primero, obviamente, será tener una antena bluetooth en el servidor, con una USB será suficiente y funciona perfectamente, yo lo tengo así desde hace años.

El siguiente paso será instalar los paquetes necesarios para tener conectividad bluetooth, normalmente son dos, bluez-libs y bluez-utils. Si tu sistema operativo es medianamente moderno probablemente los tengas ya instalados, sino estarán en cualquier repositorio.

Como supondremos que estamos trabajando con servidores, lo más probable es que no tengas entorno gráfico, así que lo configuraremos todo a mano. Si tuvieses entorno gráfico hay paquetes gnome para hacer el emparejamiento automáticamente.

En mi Centos5.0 los archivos de configuración se encuentran en /etc/bluetooth, en el tuyo pueden variar. Tendremos que configurar tres archivos:

  • hcid.conf: configura los servicios que ofrecerá la antena bluetooth.
  • pin: pin de la SIM del móvil para no tener que introducirlo manualmente. Tambien puedes configurar la SIM para que no solicite pin.
  • rfcomm.conf: configura los canales de comunicación.

La verdad es que no recuerdo mucho las distintas opciones de hcid.conf, así que me limitare a copiar mi configuración para que tengáis un punto de inicio.

options {
        autoinit yes;
        security user;
        pairing multi;
        passkey "123";//aqui el pin de acceso a la antena del servidor
}
device {
        name "%h-%d";
        class 0x120104;
        iscan enable; pscan enable;
        lm accept;
        lp rswitch,hold,sniff,park;
}

El archivo pin es muy sencillo, simplemente debes poner el pin de la SIM de tu teléfono.

Finalmente configuramos el canal de conexión con el móvil. Para ello debemos averiguar la dirección bluetooth de tu móvil, sería algo así como la dirección MAC, de hecho tiene una estructura semejante. Para ello, inicia el servicio bluetooth, o reinícialo si lo tienes ya iniciado,

/etc/init.d/bluetooth restart

y ejecuta

hcitool scan

Esto te devolverá la lista de dispositivos bluetooth a tu alcance con el nombre y la dirección que tienen. Si obtienes algo como Device is not available: No such device, tu antena no está iniciada, quizás no se ha detectado correctamente, consulta el log de inicio con dmesg a ver qué ocurre cuando conectas la antena al USB.

Supondremos que todo ha ido bien y tienes la lista de dispositivos. Busca el del móvil que estás utilizando y apunta la dirección, la necesitarás añadir al siguiente fichero, rfcomm.conf.

rfcomm0 {
        bind yes;
        device 00:0E:07:E7:12:27;//aqui la dirección bluetooth de tu movil
        channel 1;
        comment "linea bluetooth";
}

Si todo ha ido bien tenemos el sistema bluetooth preparado y enlazado con el móvil.

Enviando mensajes

Instalamos el paquete Gammu. De nuevo, lo más probable es que haya un binario para tu sistema en los repositorios habituales, sino siempre puedes descargar el fuente y compilarlo tu mismo. Configurarlo es muy sencillo. Para empezar tenemos un sólo archivo:

/etc/gammurc
[gammu]
port=/dev/rfcomm0
connection = at19200
logfile = /var/log/smsdlog
logformat = textall
startinfo = yes

En la primera línea indicamos el canal que vamos a utilizar, que es el que configuramos en el paso anterior y añadimos un log para tener información.

Comprobamos si todo va bien. Para eso preguntamos a nuestro teléfono la hora que tiene su sistema, por ejemplo:

gammu --getdatetime

Nos devolverá la hora del teléfono y tendremos todo preparado. Ya podemos probar a enviar mensajes.

echo "probando el envio de SMS" | gammu --sendsms TEXT NUMERODESTINO

Con este sencillo comando podemos configurar cualquier aplicación que necesite enviar alertas para que nos haga llegar un SMS. Así de fácil.

Sistema de mensajería avanzado

Una vez teníamos el sistema anterior montado caímos en la cuenta de que podríamos utilzarlo para hacer más cosas de las que habíamos pensado, sólo necesitábamos saber cómo 🙂 .

Una de las máquinas que utilizábamos de monitor se encontraba en la oficina, tras una línea ADSL. El router de esta línea tenía algún problema que hacía que bajo ciertas circustancias se colgase y había que reiniciarlo, con lo que había que ir personalmente a la oficina para hacerlo. Se nos ocurrió entonces que, si ya teníamos un sistema de envío de SMS funcionando, ¿por qué no hacer que pueda recibir y ejecutar acciones en base a lo que reciba? Dicho y hecho. En vez de responder a POLI BULERIA 😉 , nuestro sistema respondería a ROUTER, aunque posteriormente fuimos ampliando las acciones, como REBOOT que permite hacer un reinicio limpio del servidor y todas las que posteriormente se te ocurran.

Investigando la ayuda de Gammu descubrimos que hay una forma de poner el sistema en modo demonio y que automáticamente recoja los SMS que lleguen al terminal y envíe los que se le encolen. Este procedimiento requiere modificar un poco el sistema, pero una vez configurado ganarás en robustez y efectividad.

La clave es la opción -smsd de gammu que lo pone automáticamente en modo demonio. Este servicio lo que hace es recoger los SMS que se reciban en el móvil y dejarlos como archivos de texto en un directorio específico mientras que coge los SMS que se quieren enviar y que previamente hemos dejado en otro directorio y los envía. Es decir, en vez de hacer los envíos directamente como veíamos antes, los dejas en archivos de texto y automáticamente el demonio los envia. Veamos primero la configuración necesaria. El archivo que maneja toda la configuración es /etc/smsdrc. De nuevo os copio mi configuración tal cual puesto que tiene mucho tiempo y no recuerdo los parámetros.

[gammu]
port=/dev/rfcomm0
connection = at19200
logfile = /var/log/smsdlog
startinfo = yes[smsd]
PIN = PINDETUSIM
logfile = /var/log/smsdlog
commtimeout = 1
sendtimeout = 10inboxpath = /var/spool/gammu/inbox/
outboxpath = /var/spool/gammu/outbox/
sentsmspath = /var/spool/gammu/sent/
errorsmspath = /var/spool/gammu/error/

No tiene mucha ciencia, el primer sector es igual que en el apartado anterior mientras que en el segundo configuramos el PIN de tu SIM y los directorios donde dejar y recoger los SMS tal como explicábamos antes.

Es importante puntializar el formato de los archivos de texto que dejaremos para enviar mensajes los que recibiremos.

  • Mensajes salientes: en outboxpath dejaremos los archivos con el nombre siguiento el patrón
    OUT<phone number>.txt,
    por ejemplo con el nombre OUT666777888.txt se enviaría al 666777888 el contenido del archivo de texto.
  • Mensajes entrantes: en inboxpath recibiremos los mensajes con el nombre del archivo siguiendo el patrón
    IN<date>_<time>_<serialno>_<phone number>_<sequence>.txt
    ,
    por ejemplo IN20021130_021531_00_+45409000931640979_00.txt. En este caso habríamos recibido el 30/11/2002 a las 02:15:31 un mensaje desde el +45409000931640979. Creo que sobran detalles adicionales.

Teniendo esto claro, podemos lanzar el demonio. Tan simple como

/usr/bin/gammu  --smsd FILES /etc/smsdrc &

Si algo va mal nos devolverá algún error, si no, tendremos nuestro sistema de envío y recepción funcionando. Para probarlo, comprueba la ruta inboxpath, si tenías SMS’s en tu teléfono empezarás a ver como entran los archivos de texto. También puedes probar a crear uno en el outboxpath siguiendo el patrón descrito, verás como lo recibes en el otro teléfono.

Hasta aquí tenemos el nuevo sistema de envío y recepción terminado. Deberás modificar tu sistema de alertas para crear los archivos de texto tal como hemos explicado y lo tendrías automáticamente funcionando. También puedes crear un sencillo script PHP que te permita enviar SMS de una manera cómoda, simplemente dejarás los mensajes en forma de archivos de texto en la cola del directorio y el demonio se encargará de enviarlos.

Ejecutando acciones remotas

Ahora deberemos hacer el script que compruebe los SMS recibidos y decida qué hacer con ellos. Os pondré un ejemplo básico para que tengáis una idea clara de cómo habría que hacerlo, es responsabilidad vuestra asegurar el acceso al sistema de acciones de manera que sólo determinados teléfonos puedan ejecutarlas. Yo, por comodidad, lo haré en PHP, pero podéis hacerlo como queráis, PERL, Python, bash, C,…

<?
$inbox="/var/spool/gammu/inbox/";
$outbox="/var/spool/gammu/outbox/";
$d = dir($inbox);
while (false !== ($entry = $d->read())) {
    if ($entry != "." && $entry != ".." && substr($entry, 0, 2)=="IN"){
        $contenido=file_get_contents($inbox.$entry)
        $palabras=explode(" ", trim($contenido));
        $mensaje=substr($entry, 2, strlen($entry));
        $datos=split("_", $mensaje);
        $fecha=$datos[0];
        $hora=$datos[1];
        $numero=$datos[3];
	//utiliza el numero para decidir si permites el reinicio desde ese cliente

	switch(trim(strtoupper($palabras[0]))){
            case 'ROUTER':
                system("/usr/bin/router.sh");
                $mensaje="El Router ha sido reiniciado";
                break;
            case 'REBOOT':
                system("reboot");
                $mensaje="El servidor se está reiniciando";
                break;
            default:
                $mensaje="La palabra clave enviada no se corresponde con ninguno de nuestros servicios.";
        }
        $serial=date("YmdHis");
        $arch=$outbox."OUTA_".$numero."_".$serial.".txt";
        $fp=fopen($arch, "w");
        fwrite($fp, $mensaje);
        fclose($fp);
        unlink($inbox.$entry);
    }
}
$d->close();
?>

El script es muy sencillo, buscamos los archivos del directorio INBOX, analizamos el nombre para ver el número de origen y separamos el contenido por palabras para decidir la acción. Fijáos que con este sistema es fácil crear acciones con parámetros, simplemente se añaden palabras y en el switch se opera como se necesite. En mi caso, cada acción recibida genera una respuesta en modo de SMS a enviar para que sepamos que la acción ha funcionado.

Y esto es todo amigos. Si has seguido bien las instrucciones tendrás un precioso sistema de envío y recepción de SMS desde tu servidor sin necesidad de tener conectividad a Internet. A través de Gammu puedes enviar también MMS, por si te apetece montar un sistema de seguridad que te envíe una captura de una webcam 😉 , por ejemplo. El límite está en tu imaginación y en tus necesidades.

Movilizando Joomla

Hace unos días me llegó desde la lista de correo de dev.mobi un interesante artículo sobre la creación de sitios para móviles con Joomla.

No es que me encante Joomla, de hecho probablemente haga un artículo crítico al respecto, pero creo que es muy interesante para determinados websites por su aparente sencillez. El problema que tenía es que quería una solución que me permitiese, a partir de un portal web, tener acceso desde el móvil a una versión adaptada. Gracias a este artículo encontré este plugin que prácticamente te lo da todo hecho.

Hay algo, sin embargo, que no tiene en cuenta el plugin: las imágenes. Las imágenes que insertas en tus artículos serán, normalmente, de un tamaño bastante elevado, sobre todo si hablamos de terminales móviles donde la media es de 174px de ancho de pantalla. La solución pasa por toquetear un poco el plugin haciendo que las imágenes de los articulos pasen a través de un redimensionador automático que crearemos nosotros. Os recomiendo los artículos que escribí acerca del escalado de gifs transparentes y animados (aquí y aquí) para tener una idea de como hacerlo. La solución llega en dos pasos

1) Parcheando el plugin

Este es el cambio que debemos hacer en el archivo pdabot.php del plugin. Busca al final de todo la función onAfterRenderer y haz que quede asi:

function onAfterRender()
{
	global $mainframe;
	//DESDE AQUI
	if($GLOBALS['ispda']==true){
		$body = JResponse::getBody();
		$body = preg_replace( '/<img src="(.*)"(.*)>/i', '<img src="/redimensionar.phtml?imagen=\1">', $body );
		$body = preg_replace( '/<img(.*)width="(.*)"(.*)>/i', '<img\1\3>', $body );
		$body = preg_replace( '/<img(.*)height="(.*)"(.*)>/i', '<img\1\3>', $body );
		JResponse::setBody($body);
	}
	//HASTA AQUI

Como ves, sustituimos el atributo src de todas las imágenes por nuestro script de autoescalado redimensionar.php al que le pasamos la url original de la imagen. Además aprovechamos para quitar los atributos de width y height porque en este punto no sabemos cuales serán los resultantes, si los dejásemos se vería a su tamaño original. Hemos terminado con el plugin.

2) Escalando las imágenes

Si has entendido todo el proceso hasta aquí estarás pensando, vale, pero nos falta un dato: ¿a qué tamaño escalamos las imágenes?. En efecto, no lo sabemos… todavía.

Y aquí viene W urfl en nuestra ayuda. Wurfl es una base de datos de características de terminales móviles que te permiten conocer datos como el ancho de pantalla simplemente pasándole el UserAgent del mismo. No voy a explicar el funcionamiento de Wurfl puesto que se sale del alcance de este artículo, pero en su web tienes todo lo necesario.

Crearemos entonces nuestro redimensionar.php donde simplemente buscamos mediante Wurfl el ancho de pantalla del terminal cliente y reescalamos la imagen al tamaño adecuado. Es recomendable no hacerlo al 100% para evitar que los scrolls verticales reduzcan el espacio útil y nos aparezca también el scroll horizontal (suele pasar en los Nokia). Yo suelo descontar 10px al ancho. Para imágenes de artículos puedes aplicar un escalado porcentual, pueden no quedar bien imágenes muy grandes, déjalas al 70% por ejemplo.

Conclusiones

Rápidamente y de un modo sencillo hemos adaptado para terminales móviles tu portal en Joomla, no se puede pedir más. No olvides diseñar la plantilla pda a tu gusto y necesidades.

En webs móviles es habitual utilizar una imagen como cabecera del portal. Puedes utilizar también tu redimensionador para adaptarla automáticamente al ancho de pantalla de los clientes.

Se podrían hacer muchas más cosas si integramos Wurfl directamente en el plugin, incluso podríamos hacer que el código generado estuviese adaptado a las capacidades del terminal del cliente (xHTML, iMode, WML) creando plantillas para cada lenguaje distinto. ¿Te atreves a hacerlo tu?