Variable not found
Procesar peticiones a acciones inexistentes en ASP.NET MVC
Noviembre 29th, 2009 - [Enlace local]
Los controladores ASP.NET MVC que heredan de la clase Controller permiten procesar muy fácilmente las peticiones realizadas a acciones no definidas. Para ello, lo único que hay que hacer es sobrescribir el método HandleUnknowAction() e implementar la lógica que queremos que se ejecute en estos casos.
En el siguiente código, las peticiones realizadas a /Home/Index y /Home/About serán procesadas normalmente, pero /Home/BeberCerveza será procesada por HandleUnknowAction, cuya implementación mostrará la vista “Index” con un mensaje personalizado:
public class HomeController : Controller
{ public ActionResult Index() { ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View(); }
public ActionResult About() { return View(); }
protected override void HandleUnknownAction(string actionName)
{ ViewData["Message"] = "¿Estás intentando " + actionName + "?";
View("Index").ExecuteResult(this.ControllerContext);
}
}
Publicado en: Variable not found.
» Leer más, comentarios, etc...
Ingenieria de Software / Software Engineering
Project 2010 Beta
Noviembre 28th, 2009 - [Enlace local]
Ya post atrás había hablado de Project 2010, bien pues se ha liberado la versión Beta, la cual puedes descargar del siguiente sitio
http://www.microsoft.com/project/2010/en/us/default.aspx?cid=RXEML2010BETA
» Leer más, comentarios, etc...
HardBit
Estandares Web MX
Noviembre 28th, 2009 - [Enlace local]
Basta ya de tablas sobre tablas. Este post es para presentales el proyecto que hace poco iniciaron @juwe, @DiegoHerrera y @FullMental, llamado Estándares Web México
¿Pero que es?
Cito textualmente del sitio
EstandaresWeb.com.mx nace con la intención de promover en México el uso y enseñanza de los Estándares Web, buscando que se construyan más sitios que los cumplan, aumentando potencialmente la inversión realizada en ellos, a través de garantizar el acceso desde distintos tipos de dispositivos y plataformas, posicionamiento en Internet a través de optimización para motores de búsqueda -SEO, Search Engine Optimization-, accesibilidad, facilidad de mantenimiento y disminución de inversión en ancho de banda, entre otras características.
Creo que este tipo de iniciativas son muy interesantes, ya que es bien sabido que las aplicaciones y sitios web cada día toman mas relevancia en la vida diaria, así que, que mejor que tener conocimiento sobre estándares para tener una mejor experiencia como usuarios y saber como ofrecerla como desarrolladores/diseñadores.
» Leer más, comentarios, etc...
4 bits blog
Los modelos de django
Noviembre 27th, 2009 - [Enlace local]
En el anterior post de introducción a django, no se usaron bases de datos, así que en este se verá cómo trabaja django con las bases de datos, es algo realmente sencillo.
Los modelos
Al basarse django en el MVC, divide la aplicación en dos partes la vista (que ya se pudo ver en el anterior post) y el modelo. El modelo define los datos que utilizará la aplicación, como podrían ser los datos de un usuario (nombre, apellidos, dirección, …).
En el caso de django, los modelos serán clases de python que heredarán la clase Model y que utilizarán unos tipos de datos especiales para definir sus atributos. Más tarde, django, gracias a su ORM, transformará estas clases en tablas de la base de datos del proyecto.
Creando un modelo
Los modelos se definen en el archivo «models.py» de la aplicación de django que los vaya a utilizar. Cada modelo será una clase de python que heredará la clase django.db.models.Model, los atributos de las clases de cada modelo deberán ser instancias de unas clases que ofrece django para poder traducir estos atributos en campos de las tablas de la base de datos. Por ejemplo, para crear el modelo de un usuario:
# models.py
# -*- coding: utf-8 -*-
from django.db import models
class Usuario (models.Model):
"""Datos del usuario utilizados en la aplicación."""
nombre = models.CharField (max_length=40)
apellidos = models.CharField (max_length=60)
email = models.EmailField (unique=True)
def __unicode__ (self):
return '%s %s (%s)' % (self.nombre, self.apellidos, self.email)
Una breve explicación sobre el ejemplo anterior:
- Los atributos
nombreyapellidosserán dos campos de texto con una longitud máxima de 40 y de 60 caracteres respectivamente en la base de datos. - El atributo
emailserá un campo de texto cuyo valor deberá ser único y que además sólo aceptará direcciones de correo electrónico bien formadas. - El método
__unicode__es el equivalente a__str__en python, es decir, se usará cuando se quiera mostrar una instancia de la clase como una cadena de caracteres.
Para conocer los tipos existentes para los atributos de los modelos, siempre se puede consultar la lista de los tipos de los modelos de django.
Una vez se tengan los modelos creados, se deberán sincronizar con la base de datos utilizada.
Configurando la base de datos
Configurar la base de datos a utilizar en django es muy sencillo, sólo hay que editar el archivo «settings.py» del proyecto. Concretamente las siguientes variables:
DATABASE_ENGINEindica el gestor de base de datos utilizado, actualmente django sólo dispone de los siguientes módulos: postgresql_psycopg2, postgresql, mysql, sqlite3 u oracle.DATABASE_NAMEindica el nombre de la base de datos, menos en el caso de sqlite3 que indica la ruta al archivo de la base de datos.DATABASE_USERindica el usuario con acceso a la base de datos anterior.DATABASE_PASSWORDindica la contraseña del usuario anterior.DATABASE_HOSTindica la dirección del servidor de la base de datos, se debe dejar vacío para la máquina local.DATABASE_PORTindica el puerto del servidor de la base de datos, se debe dejar vacío para usar el puerto predeterminado.
Aviso: El usuario, la contraseña, la dirección y el puerto del servidor no se usan con sqlite3, ya que no son necesarios.
Una vez configurada la base de datos y creados los modelos necesarios, se podrá sincronizar los modelos de las aplicaciones del proyecto con la base de datos, sólo hay que ejecutar:
$ python manage.py syncdb
Nota: Hay que recordar que las aplicaciones del proyecto aparecen en la variable INSTALLED_APPS del archivo «settings.py», yo recomendaría (para estos ejemplos) quitar o comentar las que vienen de manera predeterminada.
Trabajando con la base de datos
Esta es una de las mejores cosas que tiene django, y es que para acceder (ya sea para consultar, insertar, actualizar o borrar una entrada) a la base de datos, gracias al ORM sólo se usan las clases definidas como modelos.
Para probar todo lo que se va a explicar sin tener que crearse vistas (que todavía no se han explicado en profundidad) siempre se puede ejecutar la consola de django:
$ python manage.py shell
Insertar, actualizar y borrar entradas
Tomando como ejemplo la clase Usuario anterior, se podría realizar:
from proyecto.aplicación.models import Usuario # Insertar un nuevo usuario usuario = Usuario (nombre='Pepe', apellidos='Garcia', email='pepe@garcia.es') usuario.save () # Actualizar los datos del usuario usuario.nombre = 'Jose' usuario.save () # Borrar el usuario usuario.delete ()
Como se puede ver es tan sencillo como trabajar con cualquier clase de python.
Los «QuerySets»
Los «QuerySets» son conjuntos de objetos de la base de datos, es decir, los resultados de una consulta a la base de datos en forma de objetos de python.
Las clases de los modelos disponen de una instancia del gestor de objetos de la base de datos Clase.objects, en esta instancia se definen los diferentes métodos para realizar las consultas sobre la base de datos, que devolverán un «QuerySet» que contendrá los resultados.
De este modo, tomando el ejemplo de la clase Usuario:
from proyecto.aplicación.models import Usuario
# Insertar varios usuarios
usuario = Usuario (nombre='Pepe', apellidos='Garcia', email='pepe@garcia.es')
usuario.save ()
usuario = Usuario (nombre='Maruja', apellidos='Perez', email='maruja@perez.es')
usuario.save ()
usuario = Usuario (nombre='Pedro', apellidos='Garcia', email='pedro@garcia.es')
usuario.save ()
# Conseguir todas las entradas de la base de datos
todos_los_usuarios = Usuario.objects.all ()
print 'Todos los usuarios %s' % todos_los_usuarios
# Conseguir los usuarios que se apelliden Garcia
garcias = Usuario.objects.filter (apellidos='Garcia')
print 'La familia Garcia %s' % garcias
# La familia Garcia ordenada por nombre
print 'La familia Garcia ordenada por nombre %s' % garcias.order_by ('name')
Como siempre esto no es más que una mota de polvo de lo que en realidad se puede hacer, para profundizar lo mejor es consultar la documentación de django sobre los «QuerySets».
» Leer más, comentarios, etc...
Picando Código
Konqueror en The Big Bang Theory
Noviembre 27th, 2009 - [Enlace local]
A falta de entradas en el blog, una imagen curiosa del último episodio de la tercera temporada de The Big Bang Theory: The Vengeance Formulation. Ya lo anunciaron en KDE News, Leonard Hofstadter usó en este episodio una remera de Konqueror:

Leonard - Remera de Konqueror
Es raro, porque generalmente las interfaces de las computadoras en la serie muestran alguna versión de Windows. En esta misma escena, la laptop de Sheldon Cooper está usando Windows 7 aparentemente. Capaz que usan doble booteo…
Ya había visto KDE en Héroes, y en el artículo enlazado a KDE News comentan de otras series: House, 24, Alias, Dexter y Shar.
The Big Bang Theory… me sigue resultando cada episodio mejor que el anterior.
» Leer más, comentarios, etc...
HardBit
Inno Setup Unicode con ISSkin
Noviembre 25th, 2009 - [Enlace local]
Los que usan Inno Setup, sabran que hay una actualización de Inno Setup 5.3.6 y tambien que existe una version Unicode de este generador de instaladores, para ponerle skins (pieles!!) a nuestro instalador pueden hacer uso de una extension gratuita que ofrece CodeJock llamada ISSkin, bien el punto de este post es que la version que ofrece en el sitio solo funciona con la version ANSI y no la Unicode de Inno Setup, asi que les dejo la DLL para que funcione en Unicode ISSkin Unicode
Es todo.
» Leer más, comentarios, etc...
Joan Garnet :: Arquitectura y desarrollo RIA
Herramientas para Mac(book)
Noviembre 25th, 2009 - [Enlace local]
Aprovechando que mi buen amigo Daniel Viedma se ha pillado un Mac y me ha pedido la tradicional lista de must have apps la voy a trasladar a este post para quién guste.
Me ahorro la lista de aplicaciones de Adobe, que las tengo todas en forma de Master Collection + Flex Builder 3 / Flash builder 4.
Aplicaciones
- TextWrangler (editor de textos)
- Adium (mensajeria instantanea)
- Cyberduck (ftp)
- Transmission (torrent)
- VLC (video)
- jDownloader (download manager)
- kdiff (tipo WinMerge)
- MAMP (MacApacheMysqlPhp)
- ZendServer (PHP server)
- MySQL GUI tools (de mysql.com)
- NeoOffice (OpenOffice optimizado para Mac)
- Path Finder (Tipo Total Commander – Finder replacement)
- ForkLift (Tipo Total Commander – Finder replacement)
- Visual Paradigm UML (uml)
- Charles (HTTP proxy)
Utilidades
- Quicksilver (app launcher tipo Launchy pero + featured)
- Stuffit expander (compresor / descompresor zip, rar, etc..)
- Tweetie (twitter)
- VirtualBox (virtualización S.O)
- Clipboard evolved (clipboard manager)
ManuBar apps
- Lab Tick (para controlar la iluminación de las teclas)
- iStat Menus (graphs de memoria, disco, red, ventialdores…)
- Caffeine (para controlar el screen dimming)
- smcFanControl (control RPM ventiladores. Si tienes SSD no te hará falta…)
Si me dejo alguna actualizaré la lista
…o si se ocurre algo que valga la pena mencionar podéis comentar :)
Sugerencias de otros usuarios
- Ernesto Quezada
- Forklift (similar a total commander y FTP a la vez)
- Little Snitch (firewall facil de controlar)
- Luis Adrián
- Smultron (Editor)
- TextMate (Editor)
- FilleZilla (Cliente y Servidor FTP)
- ScreenFlow (Capturador de Pantalla y Editor de Video)
- Paragon NTFS (Utilidad Compatibilidad con particiones NTFS)
- Parallels (Virtualización)
- World of goo (juego)
- Daniel Viedma
- Gimp (Editor de imágen)
- Javi
» Leer más, comentarios, etc...
MonoCaffe
Monocaffe Connections Manager 0.7
Noviembre 25th, 2009 - [Enlace local]
Monocaffe Connections Manager 0.7 está lista para ser descargada. Esta nueva versión contiene:
- Configuración visual de las consolas (transparencia, fuente, colores)
- Configuración de los clientes (binario y opciones por defecto)
- Permite modificar fácilmente las conexiones.
- Reordenado de las pestañas
- Ayuda en linea
- Instalación automatizada
- Paquete .deb para Ubuntu
- Visual configuration of consoles.
- Clients configurations (binary and default options).
- Easily modify connections data.
- Tabs rearrangement
- Online Help
- Automatic instalation by distutils
- Ubuntu package
ScreenShots!!!
» Leer más, comentarios, etc...
Fetishcode...Thinking in objects
Revisión de la demo de ADF 11g, FOD.
Noviembre 25th, 2009 - [Enlace local]
A
» Leer más, comentarios, etc...
Variable not found
Borrado de registros con jqGrid y ASP.NET MVC
Noviembre 24th, 2009 - [Enlace local]
En un post anterior dedicado a jqGrid y ASP.NET MVC vimos lo sencillo que resultaba implementar un potente grid para mostrar datos tabulares, permitiendo paginación, ordenación y redimensionado de columnas.
Pero, como ya comenté entonces, jqGrid es mucho más que eso. En este artículo estudiaremos la implementación de la funcionalidad de borrado de filas integrada en el propio componente, utilizando intercambio de datos Ajax con el lado servidor para actualizar el modelo.
Partiremos del ejemplo que desarrollamos anteriormente, y nos centraremos únicamente en introducir los cambios necesarios para introducir esta nueva capacidad. También, como en el caso anterior, encontraréis al final un enlace al proyecto de demostración, para Visual Web Developer Express 2008 SP1 y ASP.NET MVC 1.0.
1. Preparamos la infraestructura
Antes de nada, y es algo que es importante recordar cuando trabajemos con jqGrid, debemos pensar qué módulos de este componente necesitamos en nuestro proyecto.
En el post anterior descargamos exclusivamente los necesarios para implementar la visualización del grid; ahora, dado que vamos a utilizar más funcionalidades del componente, debemos seleccionar en la herramienta de descarga aquellos que nos harán falta, el base y los relativos a la edición de datos.
En teoría deberíamos seleccionar los módulos estrictamente necesarios para nuestros fines, pero en la práctica no es fácil adivinar cuál de ellos implementa justamente lo que estamos buscando. De hecho, es bastante frecuente encontrarse con errores de script cuando no acertamos con el módulo exacto, al que siguen bastantes iteraciones prueba-error hasta que conseguimos averiguar cuál debemos indicar en la descarga.
Por eso en este caso, seleccionaremos todos los módulos relativos a la edición; eso nos permitirá, además, seguir implementando funcionalidades como el alta o la modificación sin tener que volver a descargar componentes.
Aparte de los módulos a incluir en la descarga, el resto de los pasos de preparación de la infraestructura son idénticos a los descritos en los puntos 1 al 3 del post inicial:
- Copiar los archivos de script (jqGrid y el archivo de localización) en el proyecto.
- Descargar el tema visual de jQuery UI y añadirlo al proyecto.
- referenciar las librerías de scripts y estilos en la master (o vistas donde vayamos a usarlo).
2. El modelo
Vamos a ampliar la clase GestorDeAmigos anterior para que sea capaz de emular el almacenamiento en una base de datos, pero utilizando como soporte una colección en memoria que haremos persistir en una variable de aplicación. Además, aprovecharemos para añadirle un método Delete() que nos permita eliminar del almacén la persona cuyo “Id” le pasemos como parámetro.
public bool Delete(int id)
{ Amigo amigo = DatosAmigos.FirstOrDefault(a => a.Id == id);
if (amigo == null)
return false;
DatosAmigos.Remove(amigo);
return true;
}
3. La vista
En la vista debemos hacer muy pocos ajustes para permitir la eliminación de los datos. Básicamente, tendremos que habilitar un panel de botones en el grid, indicar que deseamos que aparezca el botón de eliminación, y configurar el comportamiento de éste, para lo cual invocaremos al método navGrid() después de la llamada de inicialización del grid que ya vimos el otro día:
<script type="text/javascript"> jQuery(document).ready(function() { jQuery("#list").jqGrid({ [...] // OMITIDO. Idéntico al del post anterior. });
$("#list").navGrid( null, { refresh: true, add: false, edit: false, del: true, search: false }, null, // parámetros para el alta
null, // parámetros para la edición
{ // parámetros para la eliminación url: '<%= Url.Action("Eliminar") %>', width: 500,
afterSubmit: function(r, d) { return [r.responseText=="", r.responseText];
}
}
);
});
Los parámetros que estamos pasando al método navGrid() son los siguientes:
- el primer parámetro debería ser
jQuery('#pager'), que es la referencia hacia el control de paginación que estamos utilizando. En este caso es un nulo porque esta referencia se incluyó en la inicialización de jqGrid.
- A continuación, creamos un objeto anónimo en el que establecemos a
truelas propiedadesdelyrefresh, que indica que queremos mostrar los botones de eliminación y recarga de datos. El resto de propiedades predefinidas,add,edit, ysearch, equivalentes a los botones de añadir, editar y buscar registros, respectivamente, las establecemos afalsecon objeto de que no aparezcan botones para invocarlas; ya las activaremos en otros posts ;-)
- el siguiente parámetro se trata de un objeto donde configuramos el comportamiento del botón de alta de registros. Dado que no vamos a implementarlo ahora, lo establecemos a nulo.
- el cuarto parámetro es lo mismo que el anterior, pero para configurar la edición, por lo que también se encuentra establecido a
null.
- a continuación, y por último, el objeto en cuyas propiedades definimos el comportamiento del botón de eliminación:
- La URL a la acción que se invocará en servidor, que la obtenemos utilizando el UrlHelper de MVC. En este caso, invocaremos a una acción llamada “Eliminar”, a la que el sistema enviará el Id del registro activo.
- El ancho del cuadro de diálogo de confirmación.
- En
afterSubmitimplementamos una función callback que jqGrid llamará cuando haya recibido el resultado de la petición Ajax. El primer parámetro que nos envía es el objetoXMLHttpRequestdonde encontraremos la respuesta obtenida desde el servidor; el segundo parámetro contiene los datos que han sido enviados a la petición.
El retorno de la función callback debe ser siempre un array con dos elementos. El primero es un booleano indicando si la eliminación ha tenido éxito, y el segundo es el mensaje de error, en caso de que se haya producido:return [ exito , "mensaje de error" ];
Es importante ahora resaltar una cosa: salvo los parámetros de entrada y el tipo de retorno descritos anteriormente, jqGrid no nos impone ninguna particularidad más respecto a cómo debemos implementar este método, el tipo de información que recibiremos desde el controlador o cómo la procesaremos al recibirla. Somos totalmente libres de elegir la forma en la que haremos las cosas.
En nuestro caso, vamos a hacer una implementación muy simple en base a la siguiente convención: el controlador retornará un string con una descripción del error en caso de que se produzca algún problema borrando el registro, y retornará un nulo cuando todo vaya bien. Esto nos permite implementar el callback utilizando la siguiente expresión:
- La URL a la acción que se invocará en servidor, que la obtenemos utilizando el UrlHelper de MVC. En este caso, invocaremos a una acción llamada “Eliminar”, a la que el sistema enviará el Id del registro activo.
return [r.responseText=="", r.responseText];
Si observáis, estamos llenando el array de retorno de tal forma que el primer parámetro será cierto si la respuesta obtenida está vacía (o sea, no hay error), y en el segundo parámetro introducimos la respuesta tal cual la hemos obtenido del servidor.
4. El controlador
Dado que la lógica la tenemos implementada en el modelo, y que la vista ya está preparada para ponerse en contacto con el controlador vía Ajax y para recibir el feedback sobre el éxito de la operación, sólo nos queda implementar muy rápidamente el método de acción:
public ActionResult Eliminar(int id)
{ if (!gestorDeAmigos.Delete(id)) return Content("Error eliminando el registro");
return null;
}
Podréis observar que si se produce un error, retornamos un ContentResult describiendo el problema; en otros casos, devolvemos un nulo.
Para probar el funcionamiento de una eliminación errónea podéis, por ejemplo, abrir dos navegadores contra la aplicación, borrar un registro desde uno de ellos e intentar borrarlo también desde el otro.
Y… ¡Voilá!
Una vez habiendo implementado el modelo, la vista y el controlador, sólo nos queda probar la aplicación, que veremos que funciona perfectamente ;-P

Resumiendo, en este post hemos seguido profundizando en las capacidades de jqGrid, implementando paso a paso la funcionalidad de eliminación de registros. Hemos podido observar también el escaso código (¡a pesar de la longitud del post!) que hay que añadir para disponer de esta funcionalidad, y de lo sencillo y reutilizable que resulta su implementación.
Descargar proyecto de demostración:
Publicado en: Variable not found.
» Leer más, comentarios, etc...
xailer.info (esp)
Los asistentes de Xailer 2
Noviembre 24th, 2009 - [Enlace local]
Estimados usuarios de Xailer,
La versión definitiva de Xailer 2 incorpora un gestor de asistentes que facilita enormemente la creación de formularios rutinarios y/o repetitivos. Un ejemplo típico de un formulario de este tipo sería aquel que creamos cada vez que queremos realizar la edición de todos los campos de una tabla en cualquier tipo de mantenimiento.
Primero vamos a ver lo fácil que resulta utilizar cualquier asistente, para luego adentrarnos en la modificación o creación de nuestros propios asistentes, que es igualmente fácil.
Para acceder a la opción de Asistentes utilizaremos la opción de menú Componentes->Asistentes y se nos mostrará el siguiente diálogo:

Observe que todos los asistentes están incluidos de alguna forma dentro de una DLL. Dicha DLL ha sido creada por el propio Xailer, y de hecho podrá incluso analizar su código fuente en el directorio \samples\Wizards. Por supuesto también podrá modificarla y reconstruirla para que los asistentes se adapten a su forma de trabajo.
Las DLL de los asistentes deben de estar ubicadas necesariamente en los directorio \Xailer\Template o \Xailer\User.
Una vez seleccionado un asistente en concreto entra en funcionamiento el código interno del mismo que o bien puede directamente realizar el trabajo requerido o es posible que le muestre algún diálogo para pedirle algún dato adicional.
Si selecciona el asistente DBF table assistant que permite crear un formulario de mantenimiento de tablas DBF, se le mostrará el siguiente diálogo:
/>
Simplemente indique como quiere que se comporte el asistente para cada tipo de datos que pueda tener el fichero DBF y pulse el botón Run y podrá observar como de forma instantánea el formulario es creado por usted
La creación o modificación de asistentes es igualmente sencilla, y si le echa un vistazo al ejemplo \samples\wizards\Basic se dará cuenta inmediatamente de ello. Básicamente todo el trabajo consiste en crear una clase descendente de TWizard que tan sólo necesita sobrecargar el método Run(). Eso es todo.
Lo más sencillo es ver un pequeño ejemplo:
-
CLASS TWzdSample FROM TWizard
-
-
METHOD Run()
-
-
END CLASS
-
-
METHOD Run() CLASS TWzdSample
-
-
LOCAL oForm
-
LOCAL oObj
-
-
oForm := ::AddForm()
-
-
oForm:SetBounds( 246, 223, 307, 137 )
-
oForm:SetClientSize( 299, 103 )
-
-
WITH OBJECT oObj := TLabel():New( oForm )
-
:SetBounds( 12, 12, 26, 14 )
-
:cText := "User:"
-
END
-
-
::AddComponent( oObj, oForm )
-
-
RETURN Nil
Este pequeño ejemplo lo único que hace es crear un nuevo formulario en el IDE y añadir un objeto TLabel. Observe las llamadas a AddForm() y AddComponent() que son métodos de la propia clase TWizard.
Para que el IDE sea capaz de reonocer ese código dentro de la DLL es necesario indicarle su existencia en un INIT PROCEDURE dentro de la propia DLL y utilizando la función RegisterWizard. Como suele ocurrir, un ejemplo es lo más descriptivo para entenderlo:
-
INIT PROCEDURE WzdInit()
-
-
RegisterWizard( "A sample",;
-
{|| TWzdSample():New() },;
-
"comments",;
-
cBitmap )
-
-
RETURN
A continuación os dejo el prototipado de la clase TWizard cuya documentación se encontrará en la ayuda del propio IDE para que veáis todos los miembros de dicha clase cuyo uso es prácticamente auto explicativo.
-
CLASS TWizard FROM TComponent
-
-
PROPERTY aForms
-
PROPERTY oActiveForm
-
-
METHOD SetActiveModule( cModule ) // –> Nil
-
METHOD AddForm( lShow ) // –> oForm
-
METHOD AddModule( cType ) // –> cModule
-
METHOD AddComponent( oObject, oParent ) // –> lSuccess
-
METHOD AddCode( cCode ) // –> lSuccess
-
METHOD AddMethod( cMethod, cParams, cBody, cReturn ) // –> lSuccess
-
METHOD AddFunction( cMethod, cParams, cBody, cReturn, lStatic ) // –> lSuccess
-
METHOD AddProcedure( cMethod, cParams, cBody, lStatic ) // –> lSuccess
-
METHOD AddProperty( cName, cClauses ) // –> lSuccess
-
METHOD AddEvent( cName, cParams ) // –> lSuccess
-
-
METHOD TabWidth() // –> nTabWidth
-
METHOD ProjectDir() // –> cDir
-
-
METHOD Run() VIRTUAL
-
-
ENDCLASS
Un cordial saludo,
[El equipo de Xailer]
» Leer más, comentarios, etc...
Cerebro en la Sombra » Técnico
De artículo a proyecto explicado paso a paso: acorta URLs con IraUrl.me
Noviembre 24th, 2009 - [Enlace local]
Hace un par de semanas me encontré con la necesidad de utilizar uno de esos sistemas que hay por ahí para acortar URL’s. Necesitaba enviar una dirección por SMS y tenía que ocupar la menor cantidad de caracteres posible por aquello de optimizar el texto del mensaje. Mientras lo utilizaba pensaba en lo ingenioso de utilizar un sistema de numeración base36 para reducir exponencialmente el número de caracteres de la redirección. Esto iba a ser, pues, un artículo sobre las ventajas de los sistemas de numeración distintos al decimal para determinados proyectos, pero se acabó convirtiendo en un proyecto completo. Cuando estaba comenzando la explicación teórica pensé, ¿por qué no hacerlo? ¿por qué no demostrar lo rápido y fácil que se puede montar algo en Internet hoy en día?
Así, tras unas 15 horas de trabajo os presento IraUrl.me, un acortador de URL’s al estilo de TinyUrl o Bit.ly. Me ha costado más escribir el artículo que hacerlo realidad, curioso ¿eh?. En realidad a medida que iba preparando la aplicación se me iban ocurriendo más cosas que sería interesante montar, por lo que las 8 horas iniciales, más o menos, se convirtieron en 15.
La teoría
Para el que no lo sepa, un acortador de URL se basa en encontrar un dominio lo más corto posible y crear redirecciones HTTP 301 a otras URL’s. El truco está en optimizar los parámetros que añadiremos a la URL para que sean lo más cortos posible, no queremos que éstos nos penalicen lo corto del dominio.
¿Cómo funcionan entonces estos acortadores de URL’s? Mucho más fácil de lo que parece y seguramente como a ti se te habría ocurrido. Simplemente tenemos una base de datos donde vamos añadiendo registros a una tabla a medida que se van creando nuevas URL’s cortas. Esta tabla tiene un campo autonumérico, la clave de la tabla, que para cada nueva URL nos devuelve un identificador único, con lo que cada dirección podría ser accesible de la manera habitual:
Esa es exactamente la idea, lo único que hacemos es cambiar el identificador en cuestión de base10 (la de nuestro sistema métrico decimal) a base36 o base62 en mi caso. Otros sistemas de numeración conocidos son el hexadecimal (base16) y base64.
Vale, ya has hablado en chino. ¿De qué va esto? Veamos.
Sobre bases de numeración
El sistema decimal utiliza diez dígitos (de ahí lo de decimal
) para formar todas las combinaciones de números posibles. Lo que ya conocemos, vamos. El binario utiliza dos dígitos (0 y 1), el hexadecimal 16 (0..9ABCDE), base36, como su nombre indica, treinta y seis (0..9a..z) y base62 utiliza los 62 dígitos que comprenden los números del 0 al 9 y las letras de la A a la Z en mayúsculas y minúsculas (0..9a..zA..Z). Veamos unos ejemplos:
| Binario | Decimal | Hexadecimal | Base36 | Base62 |
| 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 1 | 1 | 1 |
| 10 | 2 | 2 | 2 | 2 |
| 1010 | 10 | A | a | a |
| 1100100 | 100 | 64 | 2s | 1c |
| 1000000 | F4240 | lfls | 4c92 | |
| 10000000 | 989680 | 5yc1s | FXsk |
Se puede observar de un vistazo cómo a medida que aumenta el número, cuanto mayor sea la base que manejamos menos dígitos tendrá . Los números, a fin de cuentas, son combinaciones continuas entre todos los dígitos posibles.Así, en función de la base y del número de dígitos, el mayor número representable representable sería:
| Num. dígitos |
Decimal | Base62 |
| 1 | 10 | 62 |
| 2 | 100 | 3844 |
| 3 | 1000 | 238328 |
| 4 | 10000 | 14776336 |
| 5 | 100000 | 916132832 |
| 6 | 1000000 | 56800235584 |
| 7 | 10000000 | 3521614606208 |
| 8 | 100000000 | 218340105584896 |
| 9 | 1000000000 | 13537086546263552 |
O lo que es lo mismo, base(número de dígitos), 629 contra 109.Espero que se entienda la teoría. Como curiosidad:
Decimal: 10000000000000000000000
Base62: 36aHo5IWaicak
La pregunta ahora sería, ¿Por qué Base62 y no Base64, por ejemplo, mucho más conocida? Sencillo, porque además de los 62 caracteres de Base62, Base64 utiliza dos adicionales, generalmente + y / además del =, lo que convierten la cadena en no web safe, es decir, los caracteres especiales debieran traducirse para que su transporte no diese problemas, con lo que estaríamos perdiendo las ventajas de nuestro cifrado corto. Los 62 caracteres utilizados en Base62 son totalmente seguros, sólo letras (mayúsculas y minúsculas) y números.
Sabiendo ya cómo funciona el sistema, veremos cómo crear nuestra aplicación. Obviamente no contaré todo paso a paso ya que sino tardaría mucho más en escribir el artículo que en hacer la aplicación, me meteré sólo en las cosas que considere más importantes.
Para codificar/decodificar de base10 a base62 utilizaré estas librerías:
-
function dec2base($dec, $base, $digits = FALSE) {
-
if($base < 2 or $base > 256) {
-
}
-
$value = ”;
-
if(!$digits) {
-
$digits = digits($base);
-
}
-
while($dec > $base – 1) {
-
$value = $digits[$rest].$value;
-
}
-
return (string) $value;
-
}
-
-
function base2dec($value, $base, $digits = FALSE) {
-
if($base < 2 or $base > 256) {
-
}
-
if($base < 37) {
-
}
-
if(!$digits) {
-
$digits = digits($base);
-
}
-
$dec = ‘0′;
-
for($loop=0; $loop < $size; $loop++) {
-
}
-
return (string)$dec;
-
}
-
-
function digits($base) {
-
if($base < 64) {
-
} else {
-
return substr("\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#\x24%&’()*+,-./0123456789:;<=>\x3f@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", 0, $base);
-
}
-
}
-
-
function base_encode($value) {
-
return dec2base(base2dec($value, 256), 62);
-
}
-
-
function base_decode($value) {
-
return dec2base(base2dec($value, 62), 256);
-
}
Las dos últimas funciones son las que utilizaremos para las conversiones.
Paquetes y librerías utilizadas:
- Free CSS Templates: Para tener una bonita plantilla xhtml para nuestro proyecto
. - Maxmind GeoLite Country: Para la geolocalización de un usuario a través de su IP.
- Wurfl: Para identificar el navegador/terminal de un visitante por su User Agent. Yo lo complemento con Tera-Wurfl para mantener la información en una base de datos.
- Fusion Charts Free: Para los gráficos de estadísticas.
- Zero Clipboard: Para copiar al portapapeles la url corta generada sin que el usuario deba seleccionarla, solo con un click.
- JqueryUI: Para el componente de navegación con pestañas.
- Google Safebrowsing API: Para comprobar si una url es potencialmente peligrosa.
- Adodb (opcional): Para abstraer el acceso a la base de datos. Yo suelo utilizarla en todos mis proyectos pero se pueden utilizar las funciones nativas de PHP.
- PHPExcel: Para generar Excel y PDF.
Adicionalmente:
- Un dominio y hosting donde alojarlo (6€).
- PHP y MySQL (tampoco son obligatorios, puedes hacerlo con cualquier tecnología).
- 15 horas de tu tiempo
.
Estructura de la web
Cualquier proyecto web que se precie debe comenzarse describiendo qué queremos mostrar a nuestros visitantes, hay que recopilar todas las ideas, decidir las que interesan de verdad, estudiar cómo se van a disponer en el frontend y terminar con un mapa web que nos indique el flujo a seguir en el trabajo. Este será el nuestro:
Cuando un usuario vaya a una de nuestras url’s cortas en realidad estaremos reenviando la petición http internamente a un script encargado de hacer todo el proceso, link.php en mi caso.
Para ver las estadísticas de una URL me ha gustado el sistema de bit.ly, así que se lo copiamos
. Añadiendo un “+” al final de la URL corta, en vez de saltar a la dirección larga mostraremos las estadísticas. Esto lo haremos, como en el caso anterior, dirigiendo internamente a otro script, stats.php.
Si el identificador que pretendemos usar para saltar a la url larga o ver estadísticas no existe, reenviaremos a index.php para que muestre un mensaje de error tipo “La url no existe“.
El dominio
Obviamente tendremos que buscar un dominio lo más corto posible, la mayoría estarán ya ocupados, pero buscando y buscando en TLD’s extraños puedes encontrar algo. Yo he escogido un .me porque tiene un carácter menos que un .com
y no cuesta lo que un .es
.
La base de datos
Muy sencilla, dos tablas solamente, en una mantendremos las urls generadas y en otra las estadísticas de acceso a las mismas.
-
CREATE TABLE IF NOT EXISTS `urls` (
-
`id` bigint(20) NOT NULL auto_increment,
-
`url` varchar(500) NOT NULL,
-
`titulo` varchar(255) NOT NULL,
-
PRIMARY KEY (`id`)
-
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-
-
CREATE TABLE IF NOT EXISTS `stats` (
-
`id` bigint(20) NOT NULL auto_increment,
-
`idurl` bigint(20) NOT NULL,
-
`codpais` varchar(255) NOT NULL,
-
`referer` varchar(255) NOT NULL,
-
`hostreferer` varchar(255) NOT NULL,
-
`ua` varchar(255) NOT NULL,
-
`hora` datetime NOT NULL,
-
`pais` varchar(255) NOT NULL,
-
`marca` varchar(255) NOT NULL,
-
`modelo` varchar(255) NOT NULL,
-
PRIMARY KEY (`id`),
-
KEY `idurl` (`idurl`,`hora`)
-
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
La configuración de Apache
Como hemos comentado queremos que cuando alguien vaya a cualquier url de nuestro site del tipo
Se redirija internamente al script link.php que será el encargado de procesar la petición. De igual modo si alguien visita
le mostraremos las estadísticas de esa URL (si existen). Configuramos Apache para que tenga en cuenta todas estas particularidades, mod_rewrite será nuestro amigo para conseguirlo. En mi caso he hecho que si la llamada no es un script php, ni una imagen ni un archivo javascript ni un css ni tiene el signo “+“, se vaya a link.php. Si tiene el signo “+” se irá a stats.php.
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/(.*).php$
RewriteCond %{REQUEST_URI} !^/css\/(.*)$
RewriteCond %{REQUEST_URI} !^/js\/(.*)$
RewriteCond %{REQUEST_URI} !^/(.*)\+$
RewriteCond %{REQUEST_URI} !^/images\/(.*)$
RewriteRule ^(.+) /link.php?id=$1
RewriteCond %{REQUEST_URI} ^/(.*)\+$
RewriteRule ^(.+) /stats.php?id=$1
Imagen y diseño
Para el diseño, o te lo haces tú mismo o si eres un negado creativo como yo te descargas una plantilla superchula de freecsstamplates.org y la adaptas a tus necesidades, no voy a entrar en más detalles.
Crear urls cortas
El primer script de nuestra aplicación. Un sencillo formulario donde el usuario introduce la URL que quiere acortar y al hacer submit… la acortamos integrando el sistema con la comprobación de malware que explicaba hace unos días
.
-
$urlbase="";
-
$url=$_POST[‘url’];
-
-
try{
-
$url="http://".$url;
-
$class = new GoogleSafeBrowsing(‘ABQIAAAAQYvf-54bCBMuGY20SeONVxQ236Mc_IjryQBl-W_Repaw3fCykA’, true);
-
$nomalware=$class->lookupsFor($url);
-
-
if($nomalware){
-
$htmltitle="";
-
if($html!=""){
-
}
-
$query="select * from urls where url=’$url’";
-
$rs=$conn->Execute($query);
-
if($rs->recordcount()>0){
-
$id=$rs->fields[‘id’];
-
}else{
-
$query="insert into urls (url, titulo) VALUES (‘$url’, ‘$htmltitle’)";
-
$rs=$conn->Execute($query);
-
$id=$conn->insert_id();
-
}
-
$base=base_encode($id);
-
$urlbase="http://iraurl.me/".$base;
-
}else{
-
$err=4;
-
}
-
}catch(exception $e){
-
$err=3;
-
}
-
}
Hemos añadido una pequeña comprobación. Si la URL que se quiere añadir ya existe, devolvemos la misma URL corta, yo he tomado esa decisión, tú puedes hacer lo que quieras. Además obtenemos el título de la URL final para tener una referencia hacia la misma, cuestión de sencillez visual
.
Reenvío a urls largas
Ya tenemos nuestra URL corta, vamos ahora a reenviar las solicitudes a ella a la larga. Recordemos que nuestro Apache nos va a redirigir esa petición a link.php?id=XXXX. Nuestro script actualiza, además, las estadísticas de visitas de la url.
-
-
"php">if(isset($_GET[‘id’])){
-
$idb=$_GET[‘id’];
-
$id=base_decode($idb)+0;
-
try{
-
$query="select * from urls where id=$id";
-
$rs=$conn->Execute($query);
-
if($rs->recordcount()>0){
-
$url=$rs->fields[‘url’];
-
$referer=@$_SERVER[‘HTTP_REFERER’];
-
$ua=@$_SERVER[‘HTTP_USER_AGENT’];
-
$ip=@$_SERVER[‘REMOTE_ADDR’];
-
-
$hostreferer="";
-
$hostreferer = $matches[1];
-
-
$terminal=getMarcaModelo($_SERVER[‘HTTP_USER_AGENT’]);
-
$marca=$terminal[‘marca’];
-
$modelo=$terminal[‘modelo’];
-
-
$temp=getGeoCodeAndPais($ip);
-
$codpais=$temp[‘code’];
-
$pais=$temp[‘pais’];
-
-
$query="insert into stats (idurl, codpais, referer, ua, hora, pais, marca, modelo, hostreferer) VALUES
-
($id, ‘$codpais’, ‘$referer’, ‘$ua’, now(), ‘$pais’, ‘$marca’, ‘$modelo’, ‘$hostreferer’)";
-
$rs2=$conn->Execute($query);
-
-
exit;
-
}else{
-
exit;
-
}
-
}catch(exception $e){
-
exit;
-
}
-
}
Como veis, si la URL no existe redirigimos al usuario a index.php con un mensaje de error. Necesitaremos dos funciones adicionales, las que nos devuelven información del país de origen de una IP y los datos del terminal del usuario (móvi o web). No entraré en detalles sobre la instalación de Maxmind GeoLite Country o de Wurfl/Tera-Wurfl.
-
function getGeoCodeAndPais($ip){
-
$gi = geoip_open("/usr/share/GeoIP/GeoIP.dat",GEOIP_STANDARD);
-
$codpais=geoip_country_code_by_addr($gi, $ip);
-
$pais=geoip_country_name_by_addr($gi, $ip);
-
geoip_close($gi);
-
}
-
-
function getCapabilities($ua){
-
$wurflObj = new TeraWurfl();
-
$matched = $wurflObj->GetDeviceCapabilitiesFromAgent($ua);
-
$movil = $wurflObj->capabilities;
-
-
return $movil;
-
}
Estadísticas
La teoría es la misma. Si existe la URL cargamos los datos, si no redirigimos a la home. En nuestra caso utilizamos el componente de pestañas de JqueryUI para organizar los distintos tipos de datos que permitiremos ver y añadiremos los botones para exportar a Excel y PDF.
-
-
$id=base_decode($idb)+0;
-
$query="select * from urls where id=$id";
-
$rs=$conn->Execute($query);
-
if($rs->recordcount()>0){
-
$urlbase="http://iraurl.me/".$idb;
-
$url=$rs->fields['url'];
-
$id=$rs->fields['id'];
-
$htmltitulo=$rs->fields['titulo'];
-
if($htmltitulo=="")
-
$htmltitulo=$url;
-
$query="select count(*) as nregs from stats where idurl=$id";
-
$rs=$conn->Execute($query);
-
$clicks=$rs->fields['nregs'];
-
}else{
-
exit;
-
}
Muy sencillo.
Lo complicado en este caso es mostrar las gráficas con FusionCharts. Para cada una debemos añadir algo de código html:
El script xml.php será el que devuelva los datos en el formato adecuado para FusionCharts. Por ejemplo:
-
$query="select DAY(hora) as dia, MONTH(hora) as mes, YEAR(hora) as ano, count(*) as nclicks
-
from stats
-
where idurl=$id
-
group by ano, mes, dia
-
order by hora";
-
$rs=$conn->Execute($query);
-
-
$xml='
' ; -
while($r=$rs->fetchrow()){
-
$xml.='
.$r['dia']."/".$r['mes']."/".$r['ano'].'" value="'.$r['nclicks'].'" color="#A1A1A1" />'; -
}
-
$xml.='';
Os doy sólo un ejemplo, el resto lo montáis por vuestra cuenta
.
Descifrar urls cortas
Todos los sistemas de acortar URL's funcionan tal y como cuento en este artículo, haciendo un HTTP/301 redirect hacia la url original.
A partir de la URL corta podemos saber cual es la URL original simplemente siguiendo las redirecciones que hace. Muy sencillo con PHP y que, además nos sirve para, integrándola en nuestra API de malware, prevenir posibles problemas con la URL final.
-
function get_web_page( $url )
-
{
-
'user_agent' => 'spider',
-
'max_redirects' => 10,
-
'timeout' => 120,
-
) );
-
-
if ( $page != false )
-
$result['content'] = $page;
-
return null; // Bad url, timeout
-
-
// Save the header
-
$result['header'] = $http_response_header;
-
-
// Get the *last* HTTP status code
-
for ( $i = $nLines-1; $i >= 0; $i-- )
-
{
-
$line = $http_response_header[$i];
-
{
-
$result['http_code'] = $response[1];
-
break;
-
}
-
}
-
-
return $result;
-
}
-
-
$url="";
-
$url=$_POST['url'];
-
$datos=get_web_page( $url );
-
if($datos){
-
$headers=$datos['header'];
-
foreach($headers as $head){
-
$urls[]=$temp[1];
-
}
-
}
-
$htmltitle="";
-
}
-
}
Ya está, en $urls tendremos la lista de urls que van saltando hasta llegar a la final.
Api
Hoy en día todo tiene que tener Api. Para las estadísticas es muy sencillo, el propio XML que generamos para consumir con FusionCharts nos permite que clientes externos se alimenten del mismo. Para crear URL's cortas remotamente, simplemente creamos un archivo api.php:
-
if(isset($_GET['url'])){
-
$url=urldecode($_GET['url']);
-
try{
-
$htmltitle="";
-
if(substr($url, 0, 4)!="http")
-
$url="http://".$url;
-
$html=file_get_contents($url);
-
if($html!=""){
-
preg_match('/(.*)<\/title>/is', $html, $matches);
-
if(is_array($matches) && count($matches>0))
-
$htmltitle=trim($matches[1]);
-
}
-
$query="select * from urls where url='$url'";
-
$rs=$conn->Execute($query);
-
if($rs->recordcount()>0){
-
$id=$rs->fields['id'];
-
}else{
-
$query="insert into urls (url, titulo) VALUES ('$url', '$htmltitle')";
-
$rs=$conn->Execute($query);
-
$id=$conn->insert_id();
-
}
-
$base=base_encode($id);
-
$urlbase="http://iraurl.me/".$base;
-
echo $urlbase;
-
}catch(exception $e){
-
echo "ERROR";
-
}
-
}
Eso es todo. No olvides integrarlo también con el sistema de malware.
Conclusiones
Bueno, y todo este rollo ¿para qué?. Pues muy sencillo, para que veais que hoy en día la tecnología está al alcance de todos, es sencillo y rápido crear un proyecto en Internet, hay de todo por todas las esquinas, la tecnología no es lo importante, lo que verdaderamente cuenta es cómo mueves ese producto tecnológico para rentabilizarlo y obtener un beneficio de él.
Ya tengo mi proyecto superchulo funcionando, sólo me ha costado unas 15 horas de trabajo. Le he puesto un poco de Adsense por aquí y por allí. ¿Y ahora qué? ¿A esperar a que la gente entre y me haga millonario?
Es mucho más complicado que eso como todos sabéis, primero tienes que tener una masa de usuarios elevada que le dé movimiento al proyecto y después tienes que conseguir que la mayoría de ellos sean gente normal, no gente técnica, usuarios avanzados que no pagamos por nada ni pinchamos en publicidad
.
Hoy en día, en Internet, como en cualquier negocio, las técnicas de marketing y venta son mucho más importantes que la tecnología en sí misma, es duro reconocerlo, pero es así. De nada sirve que tengas el mejor producto del mundo mundial si no consigues que la gente lo utilice y se deje dinero, así de claro. Si tienes los conocimientos adecuados para mover el negocio, no te preocupes, la tecnología te la aporta cualquier partner por un módico precio, pero poner en manos de otro toda la estrategia de ventas de tu negocio no está tan claro ¿no?.
Espero que os sirva de algo el artículo. He querido mostrar, fundamentalmente, cómo utilizando algunas librerías que puedes obtener sin coste puedes hacer algo realmente útil y funcional con muy poco esfuerzo. Seguro que sacáis alguna idea.
Perdón por el rollo
, al final me ha costado mucho más escribir el artículo que implementarlo.
-
-
"php">Location: http://iraurl.me/
» Leer más, comentarios, etc...
Cerebro en la Sombra » Técnico
HTPC (Home Theater PC)
Noviembre 24th, 2009 - [Enlace local]
Si hace unas semanas os hablaba del DVD grabador de mis padres, hoy voy a enseñaros uno de mis juguetes, la evolución natural de esos cacharros, el HTPC.
Los HTPC son ordenadores cuya principal función es servir de plataforma de ocio y entretenimiento: televisión, dvd, reproductor de vídeos, música, fotos… todo lo hace él solito y con una gran ventaja: en cualquier momento puedes actualizar las partes de hardware que se hayan quedado obsoletas y tener tu aparato actualizado permanentemente.
El mío tiene ahora dos años y medio y en líneas generales es esto:
- Caja Thermaltake Mozart SX
- 2Gb de memoria RAM
- Micro AMD 64 X2 Dual Core 3600+ y placa base Gigabyte (no recuerdo el modelo)
- Disco duro 200gb IDE (sistema) y 500gb Serial Ata (librería multimedia) y grabadora de DVD
- Sintonizadora dual TDT Lifeview LV32T
- Tarjeta gráfica Pointofview Geforce 8400GS PCIE 512MB
- Windows 7
- DVBLink for IPTV para integrar los canales de Imagenio en Windows Media Center
El aparato se complementa con un receptor A/V Denon AVR-2802, 7 cajas acústicas (JBL e Infinity), un subwoofer Yamaha, un televisor LCD Samsung de 32” y un mando a distancia Logitech Harmony 555.
La caja
Es el punto más crítico e importante de un aparato de este tipo ya que normalmente querremos colocarlo en el salón de casa y no nos gustaría que desentonase, que se viese la típica caja de ordenador. Desde el principio tenía claro que buscaba una caja que no fuese excesivamente grande, a ser posible del tamaño de un DVD o similar. La mayoría de modelos son, en cuanto a tamaño, como una minitorre de ordenador tradicional en horizontal, muy altos, cosa que no me convencía. Buscando encontré la que terminé comprando, Thermaltake Mozart SX, buen precio, mando a distancia, buena disipación, dos ventiladores incorporados… me pareció perfecta y que era lo que yo buscaba, sin embargo no es oro todo lo que reluce.
Seguramente el sitio más completo donde puedes buscar componentes de este tipo es Sistemas Ibertrónica.
Los componentes
Cuando comencé a montar el aparato me di cuenta de que la escasa altura de la caja penalizaba las opciones de ampliación del aparato con tarjetas PCI ya que no se pueden poner tal cual, son más grandes que la caja. Para solucionarlo viene de serie con un adaptador que expande la ranura PCIe y dos PCI de la placa en horizontal tal y como podéis ver en la imagen de abajo. En general debiera ser más que suficiente, pero no tiene por qué ser así. Otro detalle importante a tener en cuenta es que la placa hay que escogerla cuidadosamente ya que, para poder utilizar el adaptador que os he comentado, la ranura PCIe tiene que estar situada en la posición exacta donde se sitúa el adaptador, de otro modo no podremos pincharla.
En mi caso tengo pinchadas la tarjeta gráfica y la sintonizadora de TV como podéis ver. En la ranura de abajo de todo tengo el adaptador de la placa para sacar la salida de audio digital y no puedo poner nada más que eso, la ranura PCI en sí queda totalmente inutilizada ya que la tarjeta gráfica es más “gorda” de lo habitual y se come el espacio disponible. En mi caso no he tenido problemas, todo lo que necesitaba está ahí, pero si necesitas algo más no tendrás donde ponerlo. De hecho una de las razones de poner la sintonizadora dual fue precisamente la imposibilidad de poner dos sencillas.
La gráfica se conecta al televisor a través de un cable DVI-HDMI con lo que la visualización es increíblemente perfecta. Hasta hace un par de meses tenía otra gráfica, pero se estropeó
, en realidad la estropeé yo. Traía uno de esos pequeños ventiladores que traen las gráficas y al poco tiempo comenzó a hacer un ruido muy molesto, con lo que decidí desconectarlo y que aguantase con el disipador lo que pudiese. Al final fueron más de dos años, no está mal. Para la nueva tenía claro que la quería silenciosa, sin ventilador, pensáoslo bien antes de comprar una con ventilador, los de estas tarjetas suelen ser de muy baja calidad.
Aquí os muestro una panorámica del equipo montado, queda todo muy limpio y accesible, la verdad. Una de las ventajas que tiene es que la fuente de alimentación es de tamaño normal, no tienes que preocuparte si se estropea. En aparatos tipo barebone tienes un grave problema si se estropea la fuente, y tarde o temprano se estropeará. El disco IDE que tengo es porque ya lo tenía
, tengo ganas de quitar el cable IDE amarillo que se ve y que abulta muchísimo y sustituirlo por los sencillos Serial ATA, pero mientras funcione tampoco lo vamos a jubilar
.
He pensado más de una vez en ponerle un lector Blu Ray y aprovechar la calidad de imagen que ofrece, pero después pienso, ¿para qué si no alquilo una película desde 2001?.
El software
Hasta hace un par de semanas estaba con Windows XP Media Center Edition 2005, pero lo actualicé a Windows7 viendo las buenas referencias y creedme, es un lujazo verlo funcionar ahora, el buen rendimiento que tiene se une a las prestaciones del nuevo Media Center dando como resultado una plataforma de ocio multimedia que roza la perfección, aunque siempre hay cosas que te gustaría cambiar
. En su día probé otros programas media center, el mejor y más completo es, sin duda, Media Portal, también probé soluciones bajo Linux, MythTV y Freevo, pero no acabaron de convencerme, quizás les de otra oportunidad un día de estos.
Hace mucho tiempo que quiero integrar Imagenio en el Media Center y tenerlo todo centralizado, pero no había manera de hacerlo hasta que apareció DVBLink for IPTV, un pequeño pero gran software que crea sintonizadoras virtuales para importar en Media Center los canales de televisión IP de forma que quedan integrados como cualquier otro canal de TV, pudiendo programar las grabaciones o verlos con un sólo click en el mando. DVBLink for IPTV cuesta unos 20 euros, un precio más que asequible para las ventajas que aporta, al menos yo lo veo así.
La prueba de fuego que le hice es grabar tres programas simultáneamente (uno en cada sintonizador TDT y otro desde Imagenio) y, mientras tanto, reproducir una película. Todo perfecto, no se puede pedir más.
El mando a distancia
Posiblemente el elemento más importante y el que más desapercibido pasa de todo el sistema si no quieres tener varios mandos a la vez entre tus manos.
Hasta hace un par de meses tenía un One-for-All Kamaleon que compré allá por 2002, pero también se estropeó
, así que me decidí por un Logitech Harmony 555, muy buena relación calidad-precio y, sobre todo, con soporte directamente para el mando de las cajas Thermaltake.
La verdad es que la curva de aprendizaje del mando y el tiempo de puesta en marcha completa son muy elevados, una vez tienes claro como funciona llega el momento de ir ajustando cada botón para que haga lo que tu quieres. Aunque tiene el clásico modo de funcionamiento de todos los mandos basado en dispositivos, es decir, funcionar como cada mando por separado, el funcionamiento principal se basa en actividades, lo que despista mucho al principio. Por ejemplo:
- Actividad Ver TV: Encender la TV, ponerla en modo TDT, mantener el resto de aparatos apagados.
- Actividad Media Center: Encenter la TV, ponerla en modo HDMI, encender el receptor A/V, ponerlo en modo DVD, encender el Media Center.
De esta manera con un sólo botón preparas el ambiente que vas a utilizar. El modo de actividades permite, además, combinar en el teclado del mando funciones de distintos aparatos, por ejemplo, que el volumen maneje el receptor A/V, otro botón el cambio de fuente en la TV, los de play/stop el Media Center, etc. En el modo dispositivo, cada mando simula ser el mando original completo sin posibilidad de intercalar funciones de otros mandos.
Conclusiones
Como casi todo en la vida, tiene sus pros y sus contras.
A favor, sin duda, la versatilidad, la sencillez de manejo y la centralización de todo tu ocio multimedia en un sólo aparato…
En contra… quieras o no, es un ordenador con todos los inconvenientes que conlleva también eso para la gente no técnica. Si estás cansado de que familiares y amigos te llamen cada vez que tienen un problema con su ordenador, ni se te ocurra recomendarles que se pongan un cacharro de estos. Son muy apetecibles para todo el mundo pero dado que son tan abiertos a poder hacer casi de todo, la cantidad de problemas que pueden surgir son muy elevados. Sólo aptos para usuarios muy avanzados.
En cuando al hardware, lo más importante en este tipo de aparatos es la caja, yo he contado mi experiencia. Quizás hoy me hubiese comprado una más normal en cuanto a tamaño de manera que pudiese utilizar las tarjetas PCI en su forma habitual, no lo sé, pero aún así estoy muy contento con la que tengo, es amplia y está muy bien refrigerada. Con este tipo de cajas hay que tener mucho ojo con la placa que compras, no sirve cualquiera ya que hay que hacer coincidir la ranura PCIe con el hueco adecuado para el adaptador horizontal. De igual modo cuando veas cajas mira una que venga con el display y el mando incorporados, comprarlos después te saldrá más caro y posiblemente no se integren a la perfección.
Para los demás componentes no creo que haya que irse a componentes de última generación, ya veis mi equipo, normalito y se mueve a la perfección.
Finalmente gástate algo de dinero en un buen mando universal que te permita tener todo unificado en un solo aparato, a la larga lo agradecerás, no tengo más que decir, creo que no haya que pensar mucho para darse cuenta de las ventajas
.
» Leer más, comentarios, etc...
Fetishcode...Thinking in objects
JSF y su valor añadido
Noviembre 24th, 2009 - [Enlace local]
A
» Leer más, comentarios, etc...
Buayacorp
Archivos de WordPress útiles y efectivos
Noviembre 24th, 2009 - [Enlace local]
Cómo crear una página de archivos de WordPress útil y efectiva.
» Leer más, comentarios, etc...
Variable not found
Hojas con estilo usando .less
Noviembre 23rd, 2009 - [Enlace local]
Seguro que más de una vez has sufrido de lo lindo al tocar un archivo de hoja de estilos. Suelen estar mal organizados, ser muy extensos, difíciles de mantener, hay declaraciones duplicadas por todas partes… Y seguro que siempre te has preguntado si hay otra forma menos complicada de conseguir lo mismo.
K. Scott Allen nos habla en su post Keeping CSS Files DRY de .Less (dot less), un interesante componente portado del mundo Ruby capaz de “compilar” hojas de estilo que utiliza una notación extendida y mucho más potente del estándar CSS, permitiendo las siguientes características:
- Uso de variables. Less permite declarar y utilizar variables para los valores que utilizaremos frecuentemente en nuestra hoja de estilos, así:
@nice-blue: #5B83AD;#header { color: @light-blue; }#footer { color: @light-blue; } - Operaciones. Podemos utilizar expresiones para obtener valores, por ejemplo:
@base: 5%;
@filler: @base * 2;
@other: @base + @filler;
color: #888 / 4;
background-color: @base-color + #111;
height: 100% / 2 + @filler;
Fijaos en un detalle de lo más interesante: Less es capaz de distinguir cuándo está operando con colores y cuándo con unidades, ofreciendo el resultado correcto en cada caso.
- Mezclas (mixins), otra potente capacidad de Less que seguro que puede venirnos bastante bien, que consiste en incluir dentro de un conjunto de reglas dentro de otro, sólo haciendo referencia a su selector:
.bordered {border-top: dotted 1px black;
border-bottom: solid 2px black;
}
#menu a {color: #111;
.bordered;}
.post a {color: red;
.bordered;}
- Reglas anidadas, que nos permiten definir reglas siguiendo una estructura más legible y fácilmente reconocible por su paralelismo con la estructura del documento:
#header {color: black;
.navigation {font-size: 12px;
}
.logo {width: 300px;
:hover { text-decoration: none }}
}
- Visibilidad de variables. Las variables pueden ser declaradas de forma global (como en el primer ejemplo) o asociadas a algún selector, lo que les aporta ámbito de visibilidad de forma muy similar a los lenguajes tradicionales:
@var: red;
#page {@var: white;
#header {color: @var; // color será “white”
}
}
- Accesores. Less permite utilizar valores de propiedades y variables como contenido para otras propiedades de forma muy natural:
#defaults {@width: 960px;
@color: black;
}
.article { color: #294366; }
.comment {width: #defaults[@width];
color: .article['color'];
}
Fijaos que el bloque #defaults no tiene por qué corresponder con un elemento del documento a formatear, se trata sólo de una forma de agrupar variables y reglas, consiguiendo un efecto empaquetador muy similar a los espacios de nombres.
- Comentarios en línea, al más puro estilo C++ ó C#:
// Get in line!@var: white;
- Importación de archivos, permitiendo acceder a reglas y variables definidas en otros archivos:
@import "library"; // Si la extensión es .less, se puede omitir
@import "typo.css";
¿Y cómo funciona esto? Muy sencillo. En primer lugar, escribimos la hoja de estilos en un archivo .less, en el que podemos utilizar las características descritas anteriormente, y lo incluimos en las páginas donde nos interese, como lo hacemos siempre:
<link href="/Content/estilos.less" rel="stylesheet" type="text/css" />
Es interesante tener en cuenta que Less es compatible con CSS, lo que quiere decir que si simplemente renombramos el .css a .less, ya tendremos una hoja de partida.
A continuación, debemos introducir en el web.config una declaración que asocie la extensión .css al compilador Less:
<httpHandlers>
<add validate="false" path="*.less" verb="*"
type="dotless.Core.LessCssHttpHandler, dotless.Core" />
</httpHandlers>
Cuando llega una petición, el handler se encargará de compilar el archivo .less, generar el .css correspondiente y enviarlo minimizado al cliente, cacheando el resultado para posteriores usos, si se lo indicamos en la configuración, una sección opcional del web.config.
.Less está diseñado para ASP.NET 3.5 o superior, y por supuesto, es válido tanto para Webforms como para ASP.NET MVC. Se distribuye bajo licencia Apache 2.0, y está aún en fase beta, por lo que habrá que estar atento a la evolución de este interesantísimo proyecto.
Enlaces:
Publicado en: Variable not found.
» Leer más, comentarios, etc...
4 bits blog
Cómo proteger las contraseñas de los usuarios
Noviembre 22nd, 2009 - [Enlace local]
Voy a recuperar otro de mis documentos perdidos en Google Docs (el anterior fue el de las autotools), esta vez se trata de un pequeño manual sobre cómo proteger las contraseñas de lo usuarios de cualquier aplicación que se programe, el manual es una traducción al español del artículo How to encrypt user passwords de Daniel Fernandez.
1. Visión general
Casi todas las aplicaciones web modernas necesitan, de un modo u otro, cifrar las contraseñas de sus usuarios. Se podría decir que, desde el momento en que la aplicación tiene usuarios, y los usuarios se identifican usando una contraseña, esas contraseñas se deben guardar usando algún tipo de cifrado.
Hay muchas razones básicas para esto: las bases de datos pueden estar comprometidas, y por tanto también las comunicaciones. Pero la razón más importante es que se tiene que pensar que las contraseñas de los usuarios son datos personales. Sus contraseñas son sus claves para su privacidad, por tanto son personales, y nadie tiene el derecho de conocerlas. Y se debe cumplir esto si se quiere ganar la confianza de los usuarios.
2. El algoritmo
Ahora que se quieren cifrar las contraseñas, se debe saber cómo. Aquí entra la primera regla:
I. Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o hash).
Esto se debe a que, excepto para algunos escenarios específicos, no hay ninguna razón para que una contraseña sea descifrada. Si se cifran las contraseñas usando cifrados basados en claves (una técnica de dos sentidos) y un atacante consigue saber la clave de cifrado, se revelarán todas las contraseñas. Si no se tiene una clave para el cifrado, el riesgo desaparece, y el atacante tendrá que confiar en el uso de técnicas de fuerza bruta o similares.
Pero, ¿qué ocurre si un usuario pierde su contraseña? ¿No podrá recuperarla? La respuesta es NO. No sólo no se puede recuperar, si no que ni siquiera existe una manera de leer/conocer/ver las contraseñas de los usuarios, no importa si se es el administrador del sistema. Si un usuario pierde su contraseña, sólo se podrá reiniciarla a un nuevo valor y enviárselo por algún canal seguro al usuario, y pidiéndole que la cambie lo más pronto que pueda.
Ahora que está claro que las funciones resumen son necesarias en las contraseñas, ¿cuál algoritmo se debería usar? Hay bastantes, y dependen de las necesidades de cada uno. Los más usados son:
- Algoritmo MD5.
- Familia SHA: SHA-1 y la variantes de SHA-2 (SHA-224, SHA-256, SHA-384 y SHA-512)
En la mayoría de los casos, MD5 o SHA-1 serán elecciones adecuadas para los resúmenes de contraseñas, aunque aplicar estos algoritmos no será suficiente, como se verá a continuación.
Cuando se dice que se deberían usar funciones resumen para los cifrados de las contraseñas, y dado que estas funciones resumen son técnicas de un sentido, la siguiente pregunta es: «Si no se pueden descifrar las contraseñas, ¿cómo se comprueba que un usuario tiene una contraseña correcta?»
Hay una respuesta muy simple a esta cuestión, que será la segunda regla:
II. La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.
Lo que significa que, una vez los usuarios han introducido sus contraseñas en la identificación, se realizará la función resumen sobre la contraseña introducida con el mismo algoritmo que se ha usado con las contraseñas guardadas, y se compararán ambos resultados. Como estas funciones garantizan que dos entradas iguales obtienen el mismo resultado (lo que no es cierto en el sentido contrario), si los resultados coinciden se considerará que la contraseña introducida por el usuario es correcta.
3. Mejorando la seguridad de los resúmenes
Todos los algoritmos de funciones resumen mencionados anteriormente comparten una característica: son públicos. Son algoritmos muy conocidos y ampliamente implementados, por lo que cualquiera puede usarlos.
Si cualquiera puede usar el mismo algoritmo que la aplicación, y por alguna razón los atacantes pueden ver la base de datos de los resúmenes de las contraseñas, ¿cómo se puede estar seguro que los atacantes no serán capaces de conseguir alguna contraseña de los usuarios probando todas las posibilidades hasta que encuentren algún resumen que coincida con uno de los guardados?
La respuesta es que no se puede. Pero se puede hacer que todo eso sea una tarea enorme y con un gran tiempo de realización, de modo que no resulte útil, a menos que puedan esperar toda la eternidad. Para lograr esto, se van a explicar dos conceptos: «salt» y el contador de iteración.
3.1. «Salt»
«Salt» es una secuencia de bytes que se añaden a la contraseña antes de conseguir su resumen. Esto hace que estos resúmenes sean diferentes a los que deberían ser usando sólo la contraseña, y como resultado se consigue protección contra los ataques de diccionario. Se pueden seguir dos estrategias para usar «salt»:
- Usar un «salt» fijo, una secuencia de bytes que se utilizarán para resumir cualquier contraseña. Se puede guardar oculto el «salt» y considerarlo un valor añadido de seguridad, pero esto puede hacer que el sistema se vuelva más vulnerable a ataques de cumpleaños y, en general, a ataques dirigidos contra la base de datos de contraseñas.
- Usar un «salt» variable, que normalmente es una opción segura (especialmente si es aleatorio). Este se genera independientemente para cada contraseña a resumir, y permite que cada contraseña guardada esté desacoplada de las demás, creando una gran protección general y un alta seguridad contra ataques dirigidos contra la base de datos de contraseñas.
En la práctica, un «salt» aleatorio (o variable) es la mejor idea porque, aunque al generarse aleatoriamente tendrá que guardarse junto al resumen de la contraseña (por lo que cualquiera podrá recuperarlo) y esto hará que sea trivial para un atacante conocerlo, permitirá que cada contraseña de usuario sea independiente del resto, por lo que tendrán que atacarse una por una.
Se tiene que pensar que, si se usa un «salt» fijo y el atacante consigue saberlo, la seguridad de toda la base de datos de contraseñas será nula. Y hay muchos métodos para que el atacante pueda conocer este «salt», como aplicar fuerza bruta en sus contraseñas o en contraseñas que tenga de algún usuario válido. En un escenario de un «salt» fijo, una contraseña débil podría hacer que el sistema de contraseñas fuera débil.
Sin embargo, si se quiere mantener una parte del «salt» en secreto, se puede usar una técnica que mezcle las otras dos, usando un «salt» compuesto de una parte fija y otra aleatoria, guardando los bytes aleatorios junto al resumen de la contraseña.
El tamaño mínimo de un «salt» es de 8 bytes. Si se usa una opción mixta, al menos 8 bytes deberían ser aleatorios.
Así, se puede definir la tercera regla:
III. Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.
3.2. El contador de iteración
El contador de iteración es el número de veces que la función hash que realiza el resumen se aplica a sus propios resultados.
Esto significa que, una vez se ha seleccionado un «salt» y concatenado con la contraseña, se deberá aplicar la función hash (por ejemplo, MD5), conseguir el resultado, y pasarlo de nuevo como entrada a la misma función, y hacer lo mismo las veces que indique el contador de iteración.
El número mínimo recomendado de iteraciones es 1.000, y esto proporcionará una buena seguridad extra. Hay que pensar que, cuando se crea un único resumen de una contraseña para un nuevo usuario, la diferencia entre aplicar la función hash una vez o 1.000 veces no será un problema, tal vez doscientos milisegundos, pero los atacantes deberán tener que generar una enorme cantidad de intentos de resumen mediante fuerza bruta y, para un atacante, la diferencia entre aplicar la función hash una vez y aplicarla mil veces para cada intento será un gran problema computacional.
Así, se obtiene la cuarta regla:
IV. Iterar la función hash al menos 1.000 veces.
4. Secuencia de traducción de caracteres a secuencia de bytes
Antes de poder guardar correctamente los resúmenes de las contraseñas, hay algo que se debería tener en cuenta, que es la diferencia entre cadenas de caracteres y secuencias de bytes: dos cadenas de caracteres se pueden representar con diferentes secuencias de bytes dependiendo de la codificación empleada (ISO-8859-1, UTF-8, …)
Esto afecta, ya que las contraseñas suelen ser cadenas de caracteres, pero las funciones hash trabajan a nivel de byte.
4.1. Problemas codificando la contraseña introducida
En el supuesto de que un nuevo usuario se identifique con una contraseña que no contiene caracteres ASCII, y que la aplicación de identificación, que se está ejecutándo en Windows 2000 en Europa Occidental, convierta la cadena de caracteres a bytes sin elegir una codificación específica para la operación, e incluso usando la que haya de forma predeterminada, en Windows suele ser ISO-8859-1. De modo que el resumen se realice sobre esa secuencia de bytes y quede almacenado.
Entonces, este usuario vuelve de nuevo y esta vez no visita la aplicación de identificación anterior, en vez de eso usa cualquier otra aplicación con la misma base de datos de contraseñas pero en Linux. El usuario introduce correctamente su contraseña y … ¡no coincide!
¿Por qué? Porque la mayoría de los sistemas Linux, usan UTF-8 como codificación predeterminada, y las contraseñas que ha introducido el usuario en ambos casos se han codificado en diferentes secuencias de bytes, afectando a la correcta concordancia de las contraseñas.
Cómo se puede resolver esto, pues utilizando una codificación fija para las codificaciones de las cadena de caracteres a secuencia de bytes. Así se enuncia la quinta regla:
V. Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.
Si se usa Java, donde las cadenas de caracteres (clases String) se codifican independientemente (aunque usan de fondo UTF-16), no habrá que preocuparse de si la aplicación usa ISO-8859-1 o alguna otra codificación en lugar de UTF-8 para su interfaz de usuario, al igual que no será necesario que la codificación para los resúmenes de las contraseñas coincidan con las de la interfaz de usuario. Sólo será necesario que la codificación sea una fija, y UTF-8 proporciona un buen equilibrio entre tamaño y conjunto de caracteres.
4.2. Problemas codificando en el almacenamiento del resumen
Normalmente se quiere gestionar y almacenar el resumen de contraseñas como una cadena de caracteres, pero la función hash dará como resultado una secuencia de bytes que no necesariamente representará un carácter válido en alguna codificación. Por esto, la secuencia de bytes del resumen no se puede codificar en una cadena de caracteres, habiendo peligro de pérdidas de datos si se realizase.
Aquí es donde entra al rescate la codificación BASE64. Codificando la secuencia de bytes del resumen en BASE64, se asegura que la secuencia de bytes de salida representa una cadena de caracteres US-ASCII válida. Por lo que se podrá codificar una secuencia de bytes en BASE64 en una cadena de caracteres US-ASCII.
VI. Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.
5. Resumen de las reglas para el cifrado de contraseñas
La lista completa de reglas a aplicar:
- Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o hash).
- La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.
- Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.
- Iterar la función hash al menos 1.000 veces.
- Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.
- Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.
6. Realizándolo en Java
La forma más sencilla de cifrar contraseñas en Java, mediante las técnicas explicadas es usar Jasypt.
Si no se puede usar jasypt, o si por alguna razón se quiere desarrollar alguna característica de cifrado propia, se necesitarán:
- La clase
java.security.MessageDigestpara crear resúmenes. Esta clase permite especificar el algoritmo que se va a usar. - La clase
java.security.SecureRandompara generar «salt» aleatorios de una manera segura, usando algoritmos comoSHA1PRNG. - El método
java.lang.String.getBytes (String charsetName), para obtener una secuencia de bytes desde una cadena de caracteres, especificando una codificación fija (UTF-8). - La clase
org.apache.commons.codec.binary.Base64, parte de la biblioteca Apache Commons-Codec, para realizar codificaciones BASE64 en la salida de la función hash.
También, se puede utilizar el código fuente para las clases org.jasypt.digest.StandardByteDigester y org.jasypt.digest.StandardStringDigester, disponible en el repositorio de jasypt.
7. Defensa frente a los típicos ataques
7.1. Ataques de fuerza bruta
Realizado sobre: Una única contraseña.
Descripción: El atacante intenta conseguir la contraseña del usuario generando todas las contraseñas posibles, resumiéndolas y probando si coinciden con el resumen de la contraseña del usuario.
Defensa: La iteración de la función hash un número de veces, como 1.000 (mínimo recomendado), el coste de la creación del resumen de la contraseña en el tiempo de identificación del usuario no es significante, pero el coste de un ataque de fuerza bruta por parte de un atacante que genera millones de resúmenes será muy grande. Hay que recordar que una de las mejores maneras de proteger los datos cifrados es haciendo que el coste de romper la seguridad sea más alto de lo que cuesta el esfuerzo.
7.2. Ataques de diccionario
Realizado sobre: Cada contraseña o una base de datos de contraseñas
Descripción: El atacante intenta conseguir la contraseña de un usuario comparando sus resúmenes contra un conjunto de resúmenes de contraseñas más probables, normalmente se generan desde una lista de palabras de un diccionario. Este ataque explota una debilidad actual de las aplicaciones, ya que muchos usuarios usan palabras del diccionario como contraseña.
Defensa: Añadiendo un «salt» aleatorio, la debilidad de las contraseñas basadas en diccionario se reduce, y la posibilidad de de que el resumen aparezca en un conjunto de resúmenes previamente creados por el atacante es mínima.
7.3. Ataques de cumpleaños
Realizado sobre: La base de datos de las contraseñas.
Descripción: Este ataque explota la paradoja del cumpleaños, la cual en resumen consiste en, teniendo un gran conjunto de resúmenes de contraseñas, las posibilidades de generar una contraseña cuyo resumen coincida con al menos uno de los resúmenes en el conjunto es mucho más alto que lo esperado. Y estas posibilidades aumentan bastante cuando el tamaño del conjunto (el número de usuarios) aumenta.
Defensa: Añadiendo un «salt» aleatorio las posibilidades de que un ataque de cumpleaños tenga éxito son mínimas, porque el atacante tendría que atacar cada contraseña por separado, y no el conjunto de contraseñas, para encontrar una coincidencia. Esto es porque se tendría que encontrar una contraseña que crease el mismo resumen que el del atacante usando el mismo «salt» que se usó para el resumen, y que es diferente para cada contraseña haciendo que el ataque pase a ser de fuerza bruta.
Espero que este pequeño manual os sea de utilidad, a mí me sirvió para mi PFC.
» Leer más, comentarios, etc...
Ingenieria de Software / Software Engineering
Microsoft Project Admin Blog
Noviembre 21st, 2009 - [Enlace local]
Con gusto recibo el post donde se anuncia el nuevo blog para administradores de Project Server y para muestra un excelente post el cual menciona los requerimientos de instalación.
» Leer más, comentarios, etc...
MonoCaffe
Monocaffe Connections Manager 0.6
Noviembre 20th, 2009 - [Enlace local]
Monocaffe Connections Manager 0.6 está lista para ser descargada. Además de arreglar algunos fallos he añadido una caracteristica genial, un línea de comandos en cluster. Marcando diferentes pestañas y luego escribiendo en la parte inferior, se verá reflejado en todas las pestañas marcadas. De ésta forma podemos trabajar en varias maquinas a la vez.
Finalmente, me gustaria añadir una version hecha en Qt para KDE, pero prefiero continuar afinando las versiones de consola y de GTK, por lo que si alguien está interesado, que me deje un comentario aquí o en launchpad.
Pronto haré una entrada sobre todo el proceso para poder montar un paquete en launchpad para Ubuntu, siempre que me acuerde de todos los pasos, porque es lioso de cojones.
» Leer más, comentarios, etc...
Joan Garnet :: Arquitectura y desarrollo RIA
Upgrade de wordpress
Noviembre 20th, 2009 - [Enlace local]
Acabo de actualizar a WordPress 2.8.6, si alguien encuentra alguna anomalía en el funcionamiento del blog que por favor me avise. Por el momento no he detectado nada extraño…
Gracias!
» Leer más, comentarios, etc...
4 bits blog
Primeros pasos con django
Noviembre 20th, 2009 - [Enlace local]
Hace tiempo estuve aprendiendo a programar en Python porque me apeteció y porque me tocó lidiar en el trabajo con una aplicación web, la cual realicé en django.
¿Qué es django?
Django es un entorno de desarrollo (lo que en inglés se denomina framework) para la creación rápida de aplicaciones web, basándose en el MVC (Modelo-Vista-Controlador).
Entre todas las posibilidades que ofrece están que permite trabajar con las bases de datos como si no existieran, la creación de URL bonitas sin tener que tocar nada del servidor web, el uso de plantillas en (X)HTML para la creación de las diferentes páginas, y muchas cosas más, todas ellas estupendas y supongo que todas ellas también las tendrán otros entornos similares que utilicen otros lenguajes de programación.
Iniciando un nuevo proyecto
Lo primero que se debe hacer en django es crear lo que se denomina un proyecto, éste a su vez contendrá aplicaciones que serán las que contengan el código utilizado en la aplicación web. Por poner un ejemplo: el proyecto sería un CMS y las aplicaciones las diferentes funcionalidades de dicho CMS.
Para ello se deberá ejecutar:
$ django-admin.py startproject nombre_del_proyecto
Esto creará un directorio, llamado «nombre_del_proyecto», con los archivos básicos de todo proyecto de django:
__init__.pyes el archivo de inicialización de cualquier paquete de python.manage.pypermitirá la gestión del proyecto de django, como: crear las aplicaciones del proyecto, crear las bases de datos, ejecutar un servidor web para probar el proyecto, …settings.pycontiene la configuración del proyecto, básicamente contiene un montón de variables que utilizará django para la configuración.urls.pycontiene los patrones de las direcciones de las diferentes vistas del proyecto de django.
Lo malo es que sólo con esto no se va a poder hacer nada más que cambiar unos cuantos valores de la configuración, para que un proyecto haga algo deberá tener al menos una aplicación que contendrá el código necesario.
Iniciando una nueva aplicación
Para crear una aplicación se debe usar el archivo «manage.py» del proyecto del siguiente modo:
$ manage.py startapp nombre_de_la_aplicación
Esto creará un directorio, llamado «nombre_de_la_aplicación», con los archivos básicos de toda aplicación de django:
__init__.pyigual que antes, es el archivo de inicialización de cualquier paquete de python.models.pycontendrá los modelos de la aplicación, estos modelos son clases de python que se traducirán en tablas en la base de datos.tests.pycontendrá tests de la aplicación. Nota: Este archivo sólo se crea a partir de la versión 1.1 de django.views.pycontendrá las vistas de la aplicación, estas vistas son funciones de python que realizarán las tareas necesarias para mostrar el resultado como una página web.
Para que esta aplicación esté disponible en el proyecto, hay que añadirla a la lista de aplicaciones del proyecto que está en el archivo «settings.py».
INSTALLED_APPS = (
'hello_world.hello_app',
)
Nota: Cuando se crea un nuevo proyecto django crea una lista de aplicaciones predeterminada, que en este caso daría lo mismo borrarlas.
Creando un «hola mundo»
Lo primero será crear un proyecto («hello_world») y una aplicación («hello_app») para dicho proyecto, tal y como se ha mencionado antes. Una vez hecho, se deberá crear una vista para la página web.
Creando una vista
Para la creación de una vista, se debe editar el archivo «views.py» de la aplicación creada del siguiente modo:
# views.py
# -*- coding: utf-8 -*-
# Views used in hello world application.
from django.shortcuts import get_object_or_404, render_to_response
def hello:
"""Hello view function."""
return render_to_response ('hello.htm', { 'hello_msg' : '¡Hola mundo!' })
La función render_to_response usa una plantilla (que ahora se explicará) para crear la página web de la vista.
Creando una plantilla
Django dispone de un lenguaje propio para la creación de plantillas, básicamente son archivos HTML con unas etiquetas especiales, que se marcan con llaves dobles, que django se encargará de rellenar.
De este modo, crear una plantilla es tan sencillo como crear un archivo html en un directorio llamado «templates» dentro del directorio del proyecto o de la aplicación. En este caso valdría crear un archivo llamado «hello.htm» (es el que se usa en la función hello) en el directorio «templates» de la aplicación «hello_app».
{{ hello_msg }}
{{ hello_msg }}
Esta es una plantilla muy sencilla, el lenguaje de las plantillas de django permite realizar muchas más cosas, como herencia entre plantillas, definir bloques, bucles, …
Definiendo una dirección para la vista
Una vez creada la vista y su plantilla, habrá que definir una dirección para poder acceder a la vista. Como se mencionó al principio, para eso está el archivo «urls.py» del proyecto. Este archivo contiene una lista de direcciones a las que se les asignará unas vistas, de modo que cuando se abra esa dirección la vista asignada se encargue de crear su correspondiente página web.
En este caso, el archivo «urls.py» quedaría del siguiente modo:
# urls.py
# -*- coding: utf-8 -*-
# Django URL patterns for hello world project.
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'', 'hello_world.hello_app.views.hello'),
)
Como se puede ver, lo único que se hace es añadir un patrón (una expresión regular) con una vista asociada. En este caso se trata de una dirección vacía (r''), la raíz del dominio, a la que se asocia la función hello que se definió en las vistas de la aplicación.
Probando la aplicación web
Una vez llegado a este punto, ya se puede probar la aplicación web, para ello no hace falta tener ningún servidor web instalado, ya que django dispone de uno para realizar pruebas. Para ejecutarlo, desde el directorio del proyecto:
$ python manage.py runserver
Y aquí concluye esta introducción básica, pero larga, próximamente más. Si os ha gustado y queréis ir aprendiendo por vuestra cuenta, siempre podéis leer la documentación oficial de django.
» Leer más, comentarios, etc...
Arragonán
Testeando respuestas JSON con Grails
Noviembre 20th, 2009 - [Enlace local]
Aunque en la documentación de Grails aparece un ejemplo de como escribir un test para una acción que recibe una petición en formato XML o JSON, no lo hace de como escribir un test de una acción que genera el JSON, que tampoco es que sea precisamente complicado.
Si lo único que quisieramos probar es que, en una cadena JSON, se está devolviendo algún valor que estuvieramos esperando, podríamos utilizar el contenido que devuelve la response como una cadena(con contentAsString) y hacer alguna comprobación sobre el texto:
def response = controller.response.contentAsString
assertTrue response.contains('"name":"Gustavo Poyet"')
assertTrue response.contains('"goals":63')
Pero claro, este test es un muy ligero y no prueba demasiado. Si quisieramos probar más a fondo la respuesta(estructura, un elemento concreto de un array, etc), podríamos usar contains sobre la cadena devuelta, convertir tipos… vamos, que sería más que posible que el código del test terminara siendo infumable. Pero está disponible out of the box el converter JSON de Grails, que permite testear más a fondo fácilmente y con un código mucho más claro.
Es muy posible que si alguien se ve en la necesidad de escribir un test para una acción que devuelve un JSON, ya conozca el converter, por que es una de las alternativas que hay para hacerlo:
render object as JSON
Pues esa misma clase, tiene el paso contrario, pasar una cadena a un objeto groovy/java(JSONElement). De esta forma ya nos ahorraremos trabajo sucio y el código quedará bastante claro:
def response = controller.response.contentAsString
def responseJSON = grails.converters.JSON.parse(response)
assertNotNull responseJSON.players
assertEquals 5, responseJSON.players.size()
assertEquals 63, responseJSON.players[0].goals
assertEquals "Gustavo Poyet", responseJSON.players[0].name
» Leer más, comentarios, etc...
Fetishcode...Thinking in objects
Nueva demo de ADF Faces 11g
Noviembre 19th, 2009 - [Enlace local]
A
» Leer más, comentarios, etc...
Variable not found
Publicado ASP.NET MVC 2 Beta
Noviembre 18th, 2009 - [Enlace local]
Hace sólo unas horas Haack ha anunciado en su blog la publicación de la versión beta del framework ASP.NET MVC 2, que tenemos a nuestra disposición tanto a nivel de código fuente como de instalador binario.
Echando un vistazo rápido a las release notes, las novedades son:
- introducción de los nuevos métodos
Html.RenderAction()yHtml.Action(), el primero de ellos para escribir directamente la salida resultado de ejecutar la acción dada, y el segundo para obtenerla como cadena. - helpers para la generación de controles de edición en las vistas, basados en expresiones lambda o, en otras palabras, con tipado fuerte:
ValidationMessageFor(),TextAreaFor(),TextBoxFor(),HiddenFor()yDropDownListFor(). - mejoras en TempDataDictionary, destinadas a controlar de forma más pormenorizada la persistencia de los datos.
- se incluyen las librerías de validación en cliente, MicrosoftAjax.js y MicrosoftMvcAjax.js. Para utilizar este automatismo, sólo hay que incluir ambos script en la vista (o master) e invocar al método de activación:
<% Html.EnableClientValidation(); %>. Visual Studio incorpora ahora una opción “Add Area” en el menú contextual, así como un cuadro de diálogo para crearlas.- es posible crear controladores de ejecución asíncrona heredando de la clase
AsyncController. Esto permite crear acciones que se ejecuten en segundo plano, sin bloquear el hilo principal. - nueva plantilla de proyectos en blanco, es decir, sin toda la parafernalia de las plantillas actuales (páginas de ejemplo, autenticación, etc.).
- capacidad para registrar múltiples proveedores de validación, permitiéndonos adoptar otras soluciones o frameworks, o crear nuestras propias opciones.
- separación del proveedor de datos, el componente utilizado para obtener datos del contexto, en subcomponentes especializados en función del origen de la información (formularios, ruta, cadena de petición, y archivos). También existe la posibilidad de crear proveedores personalizados.
- Y otras mejoras, de menor calado, citadas de forma concisa en las notas de la revisión, como:
- adición de nuevos métodos y propiedades a la clase
ModelMetadata. - cambios en la plantilla por defecto de las vistas para generar los controles utilizando los nuevos helpers.
- helpers con soporte para datos binarios en el modelo, generando automáticamente campos ocultos codificados en Base64.
- etc.
Y ojo, que esta beta sólo funciona con Visual Studio 2008 SP1, no podemos probarla con la última beta de Visual Studio 2010. Eso sí, puede convivir en nuestros equipos con MVC 1.0 sin interferencias.
El siguiente paso será la publicación de la RC, que está prevista “antes de finalizar el año”, aunque no se planea incluir funcionalidades nuevas, simplemente corregir bugs y pequeñas mejoras que puedan derivar de esta fase de pruebas y del feedback de los usuarios. Por tanto, en líneas generales, lo que nos trae esta beta será prácticamente lo que vendrá en la versión 2 :-)
Enlaces:
- Instalación de ASP.NET MVC 2 beta
- Código fuente de ASP.NET MVC 2 beta y ensamblado futures, en Codeplex
- Documento de notas de la revisión
Publicado en: Variable not found.
» Leer más, comentarios, etc...
Buayacorp
Chuleta para hacer temas en Drupal
Noviembre 17th, 2009 - [Enlace local]
Un interesante cheatsheet o chuleta para hacer temas en Drupal.





