Monthly Archives: May 2010

Cambiar la ubicación de los botones de ventana en Ubuntu 10.04

Introducción.

Estoy bastante cómodo con la nueva ubicación de los botones de ventana (cerrar, minimizar y maximizar) que introdujo en abril de este año la nueva versión de GNU/Linux Ubuntu, la cual es similar a la utilizada por Mac OSX.  Sin embargo muchas personas prefieren los controles como estaban antes.  En mi caso algunos temas que he probado me modifican la posición y el orden de los botones y cuando regreso al tema personalizado quedan mal, así que me es necesario volver a ponerlos en su disposición habitual.

A continuación se detalla el procedimiento necesario para determinar el lado de la ventana en que deben aparecer los botones así como el orden de los mismos.

Procedimiento.

Opción #1: configuración estándar

Ejecutar desde una terminal (shell) o desde el diálogo de ejecución de aplicationes (ALT+F2) la siguiente instrucción.

$ /usr/bin/gconf-editor

Navegar a través del árbol de opciones y ubicar la siguiente ruta.

/apps/metacity/general

Configuración de los botones de ventana

Editar el contenido de la llave button_layout según el resultado que se desee.

  • Botones del lado izquierdo: close,minimize,maximize:menu
  • Botones del lado derecho: menu:maximize,minimize,close

Adicionalmente es posible determinar el orden de los botones con el simple reordenamiento de los mismos en la cadena de texto, así como incluír o no el ícono de menú en la ventana.

Las modificaciones a esta llave se pueden apreciar inmediatamente después de alterar su valor.

Opción #2: script de configuración.

Existe un método un tanto mas sencillo y menos flexible para realizar este cambio en la configuración de la apariencia de las ventanas y se basa en un script de terceros.  Para utilizarlo es necesario seguir estos pasos.

$ wget http://gtk-apps.org/CONTENT/content-files/123360-123360-Change%20Window%20Buttons.sh

$ chmod +x 123360-123360-Change\ Window\ Buttons.sh

$ ./123360-123360-Change\ Window\ Buttons.sh

Seleccionar la opción que corresponda con la disposición deseada.

Script para modificar la disposición de los botones de ventana

Enlaces.

Solventando el desconcertante problema del clic izquierdo en Flash bajo Ubuntu 10.04

Introducción.

Este desconcertante problema me empezó a suceder desde que actualicé mis equipos a la versión 9.10 de GNU/Linux Ubuntu, debido a este bug las aplicaciones desarrolladas en Flash perciben correctamente los eventos del ratón con la excepción del clic izquierdo, haciendo imposible en la mayoría de los casos utilizar la aplicación.  Esto se puede apreciar muy bien en sitios como YouTube donde no es posible presionar el botón de Play para iniciar la reproducción del video.

Una "solución" parcial.

Hasta hace poco la única solución que le había encontrado a este problema tan molesto era el hacer un clic derecho sobre el botón o área que deseaba activar y con el menú contextual desplegado hacer un clic izquierdo sobre el mismo lugar.  Con esto y por alguna extraña razón Flash recibe exitosamente el evento del ratón.  Esta aproximación funciona bien pero después del tercer uso se hace terriblemente dispendiosa.

El problema.

Todo parece indicar que el problema surge debido a cambios importantes en el GDK (GIMP Drawing Kit) de la librería de gráficos GTK (The Gimp ToolKit) sobre la cual se ha desarrollado GNOME.  Con esta actualización, probablemente desde la versión 2.18, se ha implementado algo llamado client-side windows que hace que las ventanas GDK se comporten diferente en contravía de lo que se conocía anteriormente.  En ese órden de ideas, el plugin de Flash debería ser actualizado según los nuevos supuestos para permitirle funcionar con estas versiones nuevas de GTK.

El motivo del problema resultó ser el mismo que hace unos meses encontré utilizando Eclipse.

Las posibles soluciones.

Según el registro del bug 410407 en el LaunchPad de Ubuntu, existen tres posibles soluciones al problema, que no eliminan las causas por si mismos pero que las solventan y permiten utilizar normalmente las aplicaciones basadas en Flash.

  1. Deshabilitar Compiz.
  2. Remover los plugins instalados de Flash (como flashplugin-nonfree y flashplugin-installer)  e instalar los provistos directamente por Adobe.
  3. Manipular la variable GDK_NATIVE_WINDOWS para forzar a GDK crear ventanas X11.

En mi opinión, la solución mas práctica es la número 3.  Ya que con esta solución no se pierden los efectos del Compiz ni es necesario descargar e instalar nuevos paquetes.

Implementación de la solución #3.

Desde una terminal (shell) o la ventana de ejecución de programas (ALT+F2) invoque la siguiente instrucción (sin el símbolo $ por supuesto).

$ gksudo gedit /usr/lib/nspluginwrapper/i386/linux/npviewer

Agregue la siguiente línea justo antes de la última, es decir, está nueva línea deberá convertirse en la antepenúltima línea del script.

export GDK_NATIVE_WINDOWS=1

El contenido resultante de este archivo en mi máquina especificamente es el siguiente.

#!/bin/sh
TARGET_OS=linux
TARGET_ARCH=i386
export GDK_NATIVE_WINDOWS=1
. /usr/lib/nspluginwrapper/noarch/npviewer

Reinicie el equipo o al menos su entorno grafico para tener en cuenta este nuevo valor de configuración del GDK.

Enlaces.

Utilizar un módem HSDPA USB Huawei E1556 en GNU/Linux Ubuntu 10.04

La situación.

El módem venía funcionando correctamente con la versión 9.10 de Ubuntu, sin embargo después de la instalación de la nueva versión (10.04) algo parece haber cambiado y el dispositivo sólo es identificado como un medio de almacenamiento (el controlador para Windows).

El problema.

Según el bug #521578 registrado en el LaunchPad de Ubuntu, el problema parece haber surgido debido a un movimiento de funcionalidades entre los paquetes udev (modem-modeswitch) y usb-modeswitch los cuales son responsables de garantizar que estos dispositivos USB no sean descubiertos únicamente como medios de almacenamiento con los drivers de Windows sino que sean identificados también como módems de banda ancha.  El paquete usb-modeswitch ahora con esta funcionalidad, no se instala por defecto en Ubuntu Lucid Lynx (10.04).

Este problema afecta a varios tipos de módems de la marca Huawei.

La solución.

La solución radica en instalar los paquetes requeridos.

$ sudo aptitude update && sudo aptitude install usb-modeswitch usb-modeswitch-data

Desconectar el módem USB y volver a conectarlo.  Este deberá ser reconocido normalmente a partir de ese momento.

Enlaces.

Reemplazar OpenJDK con SunJDK en Ubuntu 10.04 debido a problemas con Processing

Introducción.

Este fin de semana estuve haciendo unas pruebas realmente sencillas en Processing 1.1 en mi portátil que utiliza GNU/Linux Ubuntu 10.04.  Previamente le había instalado OpenJDK sin embargo empecé a obtener comportamientos extraños durante las pruebas como el hecho de reiniciarse el sistema de ventanas frecuentemente.

Debido a que mis programas en Processing eran muy sencillos no tenía sentido que fueran estos, mas aún sabiendo que se terminan ejecutando en la Máquina Virtual de Java  y por ende no debería haber motivos para que interfirieran con otros programas externos.  Terminé dudando de la implementación de la máquina virtual que estaba utilizando.

A continuación se detalla el procedimiento para desinstalar OpenJDK e instalar SunJDK (ú Oracle) con el cual se solucionaron los extraños problemas que estaba obteniendo.

Procedimiento.

Agregar el repositorio donde están los paquetes del SunJDK.

$ sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"

Actualizar la base de datos del aptitude.

$ sudo aptitude update

Remover los paquetes del OpenJDK.

$ sudo aptitude remove –purge openjdk-6-jre

$ sudo aptitude remove openjdk-6-doc openjdk-6-jre-headless openjdk-6-jre-lib

Instalar los paquetes del SunJDK.

$ sudo aptitude install sun-java6-jre sun-java6-jdk sun-java6-plugin sun-java6-fonts

Instalar Processing en Linux Ubuntu 10.04

Introducción.

Processing es un lenguaje y un ambiente de desarrollo opensource diseñado para quienes trabajan con imágenes, animaciones e interacción.  Es utilizado por estudiantes, artistas, diseñadores, investigadores y entusiastas para aprendizaje, creación de prototipos y producción.  Fue creado inicialmente para la enseñanza de los fundamentos de la programación de computadores en un contexto visual, para servir como software para la creación de gráficos (sketches) y como una herramienta de producción profesional.

Es libre de descargar y se encuentra disponible para GNU/Linux, Mac OS X y Windows.

El proyecto fue iniciado por Ben Fry y Casey Reas y ha evolucionado a partir de las ideas exploradas en el grupo de Aesthetics & Computation del MIT Media Lab.

Tomado de http://processing.org/.

Instalación.

Requisitos previos.

Processing se fundamenta en Java, tanto su ambiente de desarrollo como su compilador e intérprete (el cual es finalmente la JVM).  A pesar de que incluye su propia distribución del Java Runtime Edition considero ventajoso el tener instalado previamente el Java Development Kit, ya sea el oficial de Oracle o el de OpenJDK.

Las instrucciones para hacer esto se pueden encontrar en el siguiente enlace.

Instalación estándar.

Esta instalación es útil en cualquier distribución de Linux ya que se basa en la distribución TGZ de Processing.

El primer paso es descargar la distribución mas reciente del lenguaje, en este caso la versión 1.1.

$ wget http://processing.org/download/processing-1.1.tgz

Verifique cual es la última versión disponible en la página de descargas.

Se crea un directorio para almacenar el software y se translada allí.

$ sudo mkdir -p /usr/local/processing

$ sudo mv processing-1.1.tgz /usr/local/processing

$ cd /usr/local/processing

Se descomprime el paquete recién descargado con la distribución del lenguaje.

$ sudo tar zxvf processing-1.1.tgz

Se renombra apropiadamente para permitir la coexistencia de varias versiones de ser necesario.

$ sudo mv processing-1.1 1.1

Se verifican los permisos de ejecución del ambiente de desarrollo.

$ sudo chmod +x /usr/local/processing/1.1/processing

Para ejecutar el ambiente de desarrollo y empezar a programar con Processing es necesario invocarlo desde la línea de comando o crear un acceso directo (lanzador) a él.

$ /usr/local/processing/1.1/processing

Instalación para Ubuntu.

Los sistemas operativos herederos de Debian, como Ubuntu, pueden realizar la instalación del lenguaje de una manera resumida.

Descargar la última versión disponible en formato DEB de la siguiente ubicación.

Realice la instalación del paquete recién descargado.

$ sudo dpkg -i processing_1.1-1_all.deb

Los pasos descritos en la sección anterior serán automáticamente implementados y además se creará un ícono en el menú de GNOME bajo la sección de Programming.

Enlaces.

tar zxvf processing-1.1.tgz

Integración de una autenticación externa con los foros de Simple Machines 1.1.11

Introducción.

La situación es la siguiente, se cuenta con un CMS desarrollado en PHP bajo el framework de CodeIgniter con su propio sistema de autenticación en el que se almacenan los usuarios de la siguiente manera.

CREATE TABLE IF NOT EXISTS `core_usuario` (
`id_usuario` int(11) unsigned NOT NULL auto_increment,
`estado` enum('activo','inactivo') NOT NULL,
`_username` varbinary(16) NOT NULL,
`_password` varbinary(32) NOT NULL,
`nombres` varchar(50) NOT NULL,
`apellidos` varchar(50) NOT NULL,
`fecha_nacimiento` date default NULL,
`tipo_documento` enum('cc','ce','ti','nit') NOT NULL,
`documento_identidad` varchar(12) NOT NULL,
`genero` enum('m','f') NOT NULL,
`correo` varchar(255) NOT NULL,
`pagina` varchar(255) default NULL,
`observaciones` varchar(255) default NULL,
`fecha_creacion` datetime NOT NULL,
`fecha_actualizacion` datetime NOT NULL,
PRIMARY KEY  (`id_usuario`),
);

En el sitio se ha instalado un foro basado en la versión 1.1.11 de Simple Machines el cual comparte la base de datos con el CMS.  Se necesita encontrar la forma de unificar los nombres de los usuarios y las contraseñas para tener una única identificación para la autenticación en los dos sistemas.

Para hacer esto se utiliza el API de SSI de SMF de la siguiente manera.

Hooks de integración.

SMF provee una facilidad para alterar el ciclo de flujo del software mediante la manipulación de hooks que se agregan a etapas específicas del programa para modificar su comportamiento por defecto.  En este caso nos interesa crear un hook sobre el punto integrate_validate_login ya que ese es el punto exacto en que SMF va a realizar la validación de los usuarios durante su autenticación.

Para implementar esto se crea un archivo llamado jimhook.php (el nombre es arbitrario) con el siguiente contenido inicial.

<?php
define('SMF_INTEGRATION_SETTINGS', serialize(array(
    'integrate_validate_login' => 'user_validate',
)));
require_once('SSI.php');
function user_validate()
{
    // Implementación de la lógica de la nueva autenticación.
}
?>

Estos hooks deben asociarse al sistema de foros agregando la siguiente línea al inicio del archivo index.php.

include_once("jimhook.php");

Conexión a la base de datos.

Como se mencionó inicialmente el CMS fue desarrollado en CodeIngniter y en este caso, comparte la base de datos con los foros.  El primer paso es obtener la información de conexión a la base de datos desde los archivos de configuración del CMS.

define('BASEPATH', 1);
include('../pt/system/application/config/database.php');

Teniendo esta información se realiza una conexión global a la base de datos utilizando las funciones de PDO.

$pdo = null;
try
{
    $pdo = new PDO("mysql:host={$db['default']['hostname']};dbname={$db['default']['database']}",
                   $db['default']['username'],
                   $db['default']['password'],
                   array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
    );
}
catch (PDOException $e)
{
    print "Error!: " . $e -> getMessage() . "<br/>";
    die();
}

Funciones de apoyo del CMS.

Se crean algunas funciones básicas que servirán de soporte para el nuevo procedimiento de autenticación.

function existeUsuarioEnPlataforma($usuario)
{
    global $pdo;
    $stmt = $pdo -> prepare("SELECT id_usuario FROM `usuarios` WHERE user=?");
    $results = $stmt -> execute(array($usuario));
    return ($stmt -> rowCount() > 0);
}

Verifica si el usuario identificado por el nombre de usuario ($usuario) existe o no registrado en el CMS.  Retorna true o false según se encuentren o no efectivamente registrados.

function autenticarUsuarioEnPlataforma($usuario, $contrasena)
{
    global $pdo;
    $stmt = $pdo -> prepare("SELECT id_usuario FROM `usuarios` WHERE user=? AND pass=?");
    $results = $stmt -> execute(array($usuario, MD5($contrasena)));
    return ($stmt -> rowCount() > 0);
}

Verifica que el usuario identificado con el nombre de usuario ($usuario) tenga efectivamente a $contraseña como clave de usuario.  Retorna true en caso de coincidir, false de lo contrario.

Nótese como el CMS almacena sus contraseñas en MD5 motivo por el cual es necesario realizar esta conversión antes de realizar la comparación en la consulta SQL.

Funciones de apoyo de los foros.

De manera análoga, estas funciones facilitan la manipulación de la información en las tablas del sistema de foros, las cuales tienen configurado el prefijo foros_ en sus tablas.

function existeUsuarioEnForos($usuario)
{
    global $pdo;
    $stmt = $pdo -> prepare("SELECT ID_MEMBER FROM foros_members WHERE memberName = ?");
    $results = $stmt -> execute(array($usuario));
    if($stmt -> rowCount() == 0)
        return false;
    $fuente = $stmt -> fetch();
    return $fuente['ID_MEMBER'];
}

Verifica si el usuario identificado por el nombre de usuario ($usuario) existe o no registrado en el foro .  Retorna el identificador del usuario si existe o false de lo contrario.

function traerUsuario($usuario, $contrasena)
{
    global $pdo, $sourcedir, $modSettings;
    if(!existeUsuarioEnPlataforma($usuario))
        return false;
    $stmt = $pdo -> prepare("SELECT * FROM `usuarios` WHERE user=?");
    $results = $stmt -> execute(array($usuario));
    $fuente = $stmt -> fetch();
    require_once($sourcedir . '/Subs-Members.php');
    $regOptions = array('username' => $fuente['user'],
                        'password' => $contrasena,
                        'password_check' => $contrasena,
                        'email' => $fuente['email'],
                        'posts' => '0',
                        'ID_GROUP' => '0',
                        'ID_POST_GROUP' => '4');
    $memberID = registerMember($regOptions);
    return ($memberID > 0);
}

Obtiene la información del usuario de plataforma identificado por $usuario y $contrasena, y lo agrega en la base de datos del foro a través de su API interno.  A este nivel no se realiza ninguna verificación de autenticación.

function activarUsuarioForo($usuario, $estado='1')
{
    global $pdo;
    $stmt = $pdo -> prepare("UPDATE foros_members SET is_activated = ? WHERE memberName = ?");
    $control = $stmt -> execute(array($estado, $usuario));
    return $control;
}

Activa al usuario del foro recién creado, el cual por defecto siempre queda en espera de confirmación (is_activated = 3).

function actualizarContrasenaForo($usuario, $contrasena)
{
    global $pdo;
    $passwd       = sha1(strtolower($usuario) . $contrasena);
    $passwordSalt = substr(md5(mt_rand()), 0, 4);
    $stmt = $pdo -> prepare("UPDATE foros_members SET passwd = ?, passwordSalt = ? WHERE memberName = ?");
    $control = $stmt -> execute(array($passwd, $passwordSalt, $usuario));
    return $control;
}

Actualiza la $contrasena del $usuario en el sistema de foros.  Nótese como la $contrasena se recibe de manera plana y se almacena en la base de datos de SMF en SHA1.

Acerca del comportamiento del login de SMF.

Algo que se debe tener en cuenta acerca del comportamiento del login de SMF es que desde el cliente (navegador del usuario) se envía la contraseña ya en su propia representación SHA1 así que si la contraseña con que se va a comparar no está en la misma representación, como en este caso, el CMS la almacena en MD5, se tiene entonces un problema.

Para solventar esto, el sistema de foros permite solicitar nuevamente la contraseña al usuario (retry) y en este segundo intento la envía totalmente plana para su manipulación libre.  En este punto es donde la aprovechamos para verificar la autenticación del usuario contra la información del CMS, crear el usuario en el foro si no existe y actualizar la contraseña en el foro previniendo que haya sido actualizada antes en el CMS.

Implementación del nuevo proceso de autenticación.

function user_validate()
{
    $user = $_REQUEST['user'];
    $id   = 0;
    // Verificar un usuario proveniente de plataforma.
    if(existeUsuarioEnPlataforma($user))
    {
        $id = existeUsuarioEnForos($user);
        // Nueva lógica de autenticación.
    }
    // El usuario no existe en plataforma, verificar los nativos del foro.
    return false;
}

La base de la nueva autenticación es entonces la definición de la función user_validate que corresponde con la especificada inicialmente durante la configuración de los hooks.

En ella se verifica inicialmente si el usuario que intenta acceder al sistema (login) se encuentra presente o no en la base de datos de usuarios del CMS.  En caso de no estar presente el control de la autenticación se libera para que el sistema verifique sus usuarios propios, esto permite definir también usuarios internos del foro que no estén presentes en el CMS.

El paso siguiente consiste en determinar si el usuario existe previamente o no en el foro, es decir, no es su primer ingreso al mismo.  A partir de esta respuesta se toma uno de los siguientes caminos.

  • Si no está disponible la contraseña plana (primer login) se solicita nuevamente (segundo login).
  • Si el usuario no existe en el foro entonces …
    • Se verifica que el nombre de usuario y la contraseña coincidan.
    • Se crea el usuario en el foro.
    • Se activa al nuevo usuario.
  • Si el usuario ya existía previamente en el foro entonces …
    • Se verifica que el nombre de usuario y la contraseña coincidan.
    • Se actualiza la contraseña en el foro por si ha sido actualizada previamente en el CMS.

La implementación de estas acciones se detalla a continuación.  En el caso en que el usuario no exista previamente en el foro se ejecuta la siguiente lógica del negocio.

if($id === false)
{
    // Esta disponible la contrasena (2do. login).
    if(isset($_REQUEST['passwrd']) && !empty($_REQUEST['passwrd']))
    {
        $auth = autenticarUsuarioEnPlataforma($user, $_REQUEST['passwrd']);
        if($auth)                           // La informacion del usuario es valida.
        {
            $id = traerUsuario($user, $_REQUEST['passwrd']);
            activarUsuarioForo($user);
            return false;                   // La autenticacion tiene EXITO.
        }
        else                                // La informacion del usuario NO es valida.
            return true;                    // La autenticacion FALLA.
    }
    else                                    // No esta disponible la contrasena (1er. login).
        return "retry";                     // Vuelva a solicitar el login para confirmar.
}

En el caso en que el usuario si exista previamente en el foro, el procedimiento es mas simple y se ejecuta la siguiente lógica del negocio.

// El usuario existe en el foro.
if($id !== false)
{
    // Esta disponible la contrasena (2do. login) -> actualizar la contraseña en el foro.
    if(isset($_REQUEST['passwrd']) && !empty($_REQUEST['passwrd']))
    {
        $auth = autenticarUsuarioEnPlataforma($user, $_REQUEST['passwrd']);
        if($auth)                           // La informacion del usuario es valida.
        {
            actualizarContrasenaForo($user, $_REQUEST['passwrd']);
            return false;                   // La autenticacion tiene EXITO.
        }
        else                                // La informacion del usuario NO es valida.
            return true;                    // La autenticacion FALLA.
    }
    else                                    // No esta disponible la contrasena (1er. login).
        return "retry";                     // Vuelva a solicitar el login para confirmar.
}

Conclusiones.

Hasta ahora, con poca experiencia en su uso, SMF parece ser un sistema de foros útil y práctico.  El proceso de determinar esta unificación de la autenticación fue largo y doloroso debido a la poca documentación que pude encontrar acerca del API, la cual en su mayor parte se encuentra diseminada en los foros de su sitio web.

En términos de su código parece tener un nivel decente de flexibilidad, el concepto de hooks permite realizar modificaciones interesantes al flujo normal del software, sin embargo es difícil formalizarlo al no contar con información suficiente de su lógica de funcionamiento.  Algunas partes del software, en especial los temas, adolecen de separación MVC por lo que se hace necesario editar funciones, entre comillas, que retornan el código HTML que se desea modificar siendo esto molesto, difícil y propenso a errores, sobretodo si se compara con tener archivos PHP separados con la lógica y HTML con la presentación como sería una mejor opción.

Finalmente, después de varios días de luchas y de pruebas, la unificación de la autenticación está funcionando y se ve bien, de todas maneras es mi primer acercamiento a este software por lo que no puedo garantizar que sea la mejor aproximación.  Como siempre, estoy abierto a sugerencias constructivas.

Enlaces.