07 noviembre 2008

Crea tu propio servidor HTTP (y 4)

Vamos a terminar de ver las características principales del componente TIdHTTPServer viendo como validar a los usuarios del mismo utilizando las cookies del navegador.

Para el que no las conozca, las cookies son archivos temporales que una aplicación web puede crear en el navegador del cliente para guardar estados de sesión. En nuestro caso vamos a guardar el usuario y la contraseña del usuario que ha entrado.

REPROGRAMANDO EL EVENTO ONCOMMANDGET

En el evento OnCommandGet vamos a mostrar en la ventana del servidor las cookies que devuelve el navegador del cliente y también vamos a dar la posibilidad de entrar a la página principal del foro (foro.html) cuando los usuarios sean validados:

procedure TFServidorHTTP.ServidorCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
sDocumento: String;
i: Integer;
begin
if ARequestInfo.Cookies.Count > 0 then
begin
for i := 0 to ARequestInfo.Cookies.Count-1 do
Log.Lines.Add( 'cookie: ' + ARequestInfo.Cookies[i].CookieName + ': ' +
ARequestInfo.Cookies[i].Value );
end;

if ARequestInfo.Document = '/registrar' then
RegistrarUsuario( ARequestInfo, AResponseInfo );

if ARequestInfo.Document = '/entrar' then
Entrar( ARequestInfo, AResponseInfo );

if ARequestInfo.Document = '/enviarmensaje' then
EnviarMensaje( ARequestInfo, AResponseInfo );

if ARequestInfo.Document = '/foro.html' then
begin
Foro( ARequestInfo, AResponseInfo );
Exit;
end;

// ¿Va a entrar a la página principal?
if ARequestInfo.Document = '/' then
AResponseInfo.ServeFile( AContext, ExtractFilePath( Application.ExeName ) + 'index.html' )
else
begin
// Cargamos la página web que vamos a enviar
sDocumento := ExtractFilePath( Application.ExeName ) +
Copy( ARequestInfo.Document, 2, Length( ARequestInfo.Document ) );

// ¿Existe la página que ha solicitado?
if FileExists( sDocumento ) then
AResponseInfo.ServeFile( AContext, sDocumento )
else
// No hemos encontrado la página
AResponseInfo.ResponseNo := 404;
end;

AResponseInfo.CloseConnection := True;
end;

He añadido como novedades la posibilidad de ver las cookies que tiene el cliente en el navegador:

if ARequestInfo.Cookies.Count > 0 then
begin
for i := 0 to ARequestInfo.Cookies.Count-1 do
Log.Lines.Add( 'cookie: ' + ARequestInfo.Cookies[i].CookieName + ': ' +
ARequestInfo.Cookies[i].Value );
end;

Y suministrar la página del foro donde se van a escribir los mensajes:

if ARequestInfo.Document = '/foro.html' then
begin
Foro( ARequestInfo, AResponseInfo );
Exit;
end;

Ahora debemos modificar la página de entrada para crear las cookies.

CREANDO LAS COOKIES EN EL NAVEGADOR DEL USUARIO

Vamos a modificar nuestro procedimiento de entrar para que cuando el usuario sea validado cree en el navegador del cliente dos cookies para almacenar el usuario y su contraseña:

procedure TFServidorHTTP.Entrar( ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo );
var
sNombre, sPassword, sError, sUsuarios: String;
Usuarios: TStringList;
begin
sNombre := ARequestInfo.Params.Values['nombre'];
sPassword := ARequestInfo.Params.Values['password'];

// Abrimos la lista de usuarios
sUsuarios := ExtractFilePath( Application.ExeName ) + 'usuarios.txt';
Usuarios := TStringList.Create;

if FileExists( sUsuarios ) then
Usuarios.LoadFromFile( sUsuarios );

// Comprobamos si el usuario ya ha sido dado de alta en la lista
if Usuarios.Values[sNombre] = '' then
sError := '<h3>El usuario no existe.</h3>'
else
if Usuarios.Values[sNombre] <> sPassword then
sError := '<h3>La contraseña es incorrecta.</h3>';

if sError <> '' then
begin
AResponseInfo.ContentText := sError;
AResponseInfo.WriteContent;
end
else
begin
with AResponseInfo.Cookies.Add do
begin
CookieName := 'usuario';
Value := sNombre;
end;

with AResponseInfo.Cookies.Add do
begin
CookieName := 'password';
Value := sPassword;
end;

AResponseInfo.Redirect( 'foro.html' );
AResponseInfo.WriteContent;
end;

Usuarios.Free;
end;

Cuando el usuario ha sido validado le creamos las dos cookies y redireccionamos al cliente a la página del foro.

CREANDO LA PÁGINA WEB DEL FORO

La página web del foro va a ser la siguiente:


La página principal del foro contiene en su parte superior el nombre el usuario que ha entrado y la lista de mensajes que han enviado los usuarios. En la parte inferior muestro también el número de mensajes que hay en el foro y un pequeño formulario para escribir un nuevo mensaje.

Todo esto lo gestiono con un nuevo procedimiento llamado Foro que genera toda la página web:

procedure TFServidorHTTP.Foro( ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo );
var
i: Integer;
Mensajes, Usuarios: TStringList;
s, sUsuario, sPassword, sMensajes, sUsuarios: String;
begin
// Antes de entrar en el foro comprobamos si el usuario está registrado.
// Para ello leemos las cookies del navegador del cliente
i := ARequestInfo.Cookies.GetCookieIndex(0,'usuario');
if i > -1 then
sUsuario := ARequestInfo.Cookies[i].Value;

i := ARequestInfo.Cookies.GetCookieIndex(0,'password');
if i > -1 then
sPassword := ARequestInfo.Cookies[i].Value;

// Cargamos la lista de usuarios y verificamos su acceso
sUsuarios := ExtractFilePath( Application.ExeName ) + 'usuarios.txt';
Usuarios := TStringList.Create;
if FileExists( sUsuarios ) then
Usuarios.LoadFromFile( sUsuarios );

if Usuarios.IndexOf( sUsuario + '=' + sPassword ) = -1 then
begin
AResponseInfo.Redirect( 'index.html' );
AResponseInfo.WriteContent;
Usuarios.Free;
end
else
Usuarios.Free;

sMensajes := ExtractFilePath( Application.ExeName ) + 'mensajes.txt';
Mensajes := TStringList.Create;

if FileExists( sMensajes ) then
Mensajes.LoadFromFile( sMensajes );

s := '<h2>Listado de mensajes</h2><p>';

// Mostramos el usuario logeado

i := ARequestInfo.Cookies.GetCookieIndex(0,'usuario');
if i > -1 then
s := s + '<h2>Usuario: ' + ARequestInfo.Cookies[i].Value + '</h2>';

for i := 0 to Mensajes.Count - 1 do
s := s + Mensajes[i] + '<p>';

s := s + '<h4>Hay un total de ' + IntToStr( Mensajes.Count ) +
' mensajes</h4>';

s := s + '<p><h4>Escribir un nuevo mensaje</h4>';
s := s + '<form name="mensaje" action="enviarmensaje" method="post">';
s := s + '<label for="nombre">Título: </label><br>';
s := s + '<input name="titulo" size="40" type="text"><br>';
s := s + '<label for="nombre">Mensaje: </label><br>';
s := s + '<textarea name="mensaje" rows="3" cols="40"></textarea>
<p><br>';
s := s + '<input value="Enviar" type="submit"></form>';

Mensajes.Free;
AResponseInfo.ContentText := s;
end;

Antes de poder listar los mensajes del foro tenemos que validar al usuario leyendo las cookies y comprobando si esta en la lista de usuarios. Después muestro los mensajes que ha enviado cada usuario.

Aunque con una validación como esta es suficiente para controlar la entrada de usuarios, para crear un foro en condiciones habría que crear más cookies para identificadores de sesión, control del tiempo que lleva el usuario así como un poco de encriptación en las cookies para evitar el robo de las claves mediante sniffers.

También habría que guardar la información del foro en una base de datos fiable como Internase, Firebird, etc. Esto unido a unas buenas hojas de estilo CSS nos permitirá crear unas aplicaciones web pequeñas y potentes.

Y este sería el procedimiento EnviarMensaje:

procedure TFServidorHTTP.EnviarMensaje( ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo );
var
i: Integer;
sTitulo, sMensaje, sError, sMensajes, sUsuario: String;
Mensajes: TStringList;
begin
sTitulo := ARequestInfo.Params.Values['titulo'];
sMensaje := ARequestInfo.Params.Values['mensaje'];

i := ARequestInfo.Cookies.GetCookieIndex(0,'usuario');
if i > -1 then
sUsuario := ' ' + ARequestInfo.Cookies[i].Value;

if sTitulo = '' then
sError := '<h3>Debe introducir el título del mensaje</h3>';

if sMensaje = '' then
sError := '<h3>Debe introducir el mensaje</h3>';

// Abrimos la lista de mensajes
sMensajes := ExtractFilePath( Application.ExeName ) + 'mensajes.txt';
Mensajes := TStringList.Create;

if FileExists( sMensajes ) then
Mensajes.LoadFromFile( sMensajes );

if sError <> '' then
AResponseInfo.ContentText := sError
else
begin
Mensajes.Add( '<h4>' + DateTimeToStr( Now ) + sUsuario + '</h4><p>' +
UpperCase( sTitulo ) + ': ' + sMensaje );
Mensajes.SaveToFile( sMensajes );
AResponseInfo.Redirect( 'foro.html' );
end;

AResponseInfo.WriteContent;
Mensajes.Free;
end;

Este procedimiento es el utilizado por los usuarios para enviar un mensaje al foro.

Con esto termino estos artículos relacionados con el componente TIdHTTPServer aunque no descarto escribir más sobre el mismo si descubro más utilidades interesantes como puede ser por ejemplo la creación de páginas web seguras con SSL.

Pruebas realizadas en RAD Studio 2007.

Publicidad