quarta-feira, 11 de novembro de 2009

Uso de hash (MD5) em banco de Dados é seguro?

Vou começar esse documento perguntado:
Se você necessitasse desenvolver um software que exigisse manter um controle de usuários (login, permissões, ...), como você garantiria a segurança da senha armazenada no banco de dados?

Não tenho a menor dúvida que há uma infinidade de soluções para essa pergunta. Mas o que eu gostaria de tratar nesse post é uma das possíveis soluções bastante difundida e utilizada.
Estou referindo-me ao uso de algoritmos hash, mais especificamente o MD5. Nesse caso, em vez de gravar a senha tal como ela é, grava-se o respectivo hash.

Exemplo

Vamos pensar na estrtura de uma tabela para armazenar a lista de usuários desse sistema.
CREATE TABLE "usuario"
(
    "fullname"  VARCHAR(85) NOT NULL,
    "username"  VARCHAR(20) NOT NULL,
    "passwd"    CHAR(32)
);

OBS: Eu estipulei o tamanho de 32 bytes para o field "passwd" porque o código hash retornado pelo algoritmo MD5 costuma ser uma string com 32 caracteres hexadecimais.

Agora, quando em seu sistema você for cadastrando novos usuários, provavelmente utilizaria um SQL semelhante a este:
INSERT INTO "usuario" (fullname, username, passwd) VALUES ('...', 'mylogin', MD5('minha senha'));

ou talvez calcularia o hash da senha na própria aplicação, persistindo o valor final:
INSERT INTO "usuario" (fullname, username, passwd) VALUES ('Fabius Lovato', 'lovato', 'e10adc3949ba59abbe56e057f20f883e');

Então você faz uma consulta em sua tabela "usuarios" e observa que os registros não exibem uma senha "entendível" e, logo, conclui que mesmo que alguém conseguisse obter esses códigos hash não seria nada fácil convertê-los nas senhas originais.
Aí, acreditando estar seguro, você passa a utilizar esse código como identificador de determinadas transações trafegadas por meios não seguros ou, pior ainda, persiste esse valor em um arquivo texto qualquer.

Talvez não seja novidade para você, mas há grandes bases de dados dedicados que mantém códigos hash para palavras simples e compostas que são usualmente utilizadas. E além disso, essas bases de dados estão disponíveis para o acesso público.
Duvida? Então faça um teste muito simples: Vá até o site de busca do google (http://www.google.com.br/) e cole o código hash do nosso exemplo acima (e10adc3949ba59abbe56e057f20f883e).
Provavelmente você encontrará a resposta estampada nos primeiros links da pesquisa.

Bom, possivelmente você deve estar argumentando que essa é uma senha muito comum e muitos outros artigos a utilizariam com exemplo. Então vamos tentar novamente, mas com o hash a8832562d37247d41a7679612a104997.

Entendeu agora porque o uso de algoritmos de hash não é tão seguros assim?

Links interessantes
Um site muito interessante onde você tem a disposição uma gigantesca base de códigos hash é o md5(); que pode ser acessado pelo link http://md5.rednoize.com/
Na data de publicação desse post, uma nota no rodapé do site informava "Search in 57,035,795 md5/sha1 hashes. 20,612,544 searches answered since feb 2005".
Certamente esses valores são superiores hoje. Inclusive se você esperar alguns segundos e fizer um refresh no site (CTRL + F5), notará que o número de registros dessa base está continuamente crescendo.
Esse site também permite que você gere o código hash de um texto qualquer. Para isso basta digitar um texto que não caracterize um hash md5 válido (32 caracteres hexadecimais).

Se você for web design ou trabalham com aplicações com comunicação/acesso remoto, sugiro a leitura do artigo "Cuidado com sua senha - saiba como funciona a autenticação em sites na web".

Se tiver interesse em saber mais sobre criptografia MD5, um bom artigo está disponível no link http://www.devmedia.com.br/articles/viewcomp.asp?comp=2944.

Se você acha que é só, a lista abaixo ainda não são todos os links que você tem a disposição para localizar um código hash md5. Além disso, alguns desses sites trabalham também com códigos hash de outros algoritmos (sha1, lm e ntlm)
http://tools.benramsey.com/md5/
http://md5.gromweb.com/
http://www.thepanicroom.org/index.php?view=md5
http://shm.hard-core.pl/md5/
http://www.mmkey.com/md5/home.php
http://md5.overclock.ch/
http://www.cmd5.org/
http://www.md5encryption.com/
http://www.md5decrypt.com/
http://www.md5decrypter.com/
http://www.milw0rm.com/md5/
http://plain-text.info/
http://schwett.com/md5/
http://www.tmto.org/
http://www.xmd5.org/
http://passcracking.ru/
http://www.hashchecker.com/
http://gdataonline.com/
Esse último tem uma base de mais de 1 bilhão de hash!

Conclusão
Não importa quão seguro é o algoritmo de hash utilizado, se a senha que você utiliza é intuitiva, fácil e/ou possui poucos caracteres, então temo que ela não seja tão segura quanto você imagina.

sábado, 31 de outubro de 2009

ZeosLib for Delphi 2010

Este sem dúvida é um dos projetos open source e freeware destinado aos desenvolvedores Delphi que mais eu respeito.
ZeosLib é uma biblioteca com um conjunto de ferramentas que oferece conexão nativa com vários de tipos de banco de dados (MySQL, ADO, PostgresSQL, Oracle, SQLite, Firebird, Interbase, MSSQL, Sybase).
Além de ser open source e freeware, uma das grandes vantagens em utilizar o ZeosLib é a flexibilidade e facilidade durante a migração de um banco de dados para outro, justamente pelos mesmos componentes serem utilizados para todos os tipos banco de dados que são suportados.

O objetivo desse artigo não é descrever sobre os recursos e funcionalidades do ZeosLib, mas sim anunciar que hoje essa biblioteca é compatível com o Delphi 2010. Para ser mais específico, já faz alguns meses que essa versão já é suportada pelo Delphi 2010, mas há pouco tempo alcançou um grau de estabilidade aceitável.
A última release estável é a versão 6.6.5 que foi publicada em 25/maio/2009 e oferece suporte até o Delphi 2007.
Apesar de algumas versões não oficiais serem lançadas posteriormente, usuário do Delphi 2009 e, hoje, Delphi 2010 têm ansiosamente aguardado por atualizações dessa biblioteca.
O suporte oficial da biblioteca ao Delphi 2009/2010 só está previsto para a versão 7.0 do ZeosLib que não tem um data definida para ser lançado e, acredito, ainda deve demorar um pouco. Entretanto já está disponível uma versão beta que, apesar de ainda requerer um bom trabalho, é suportado pelo Delphi 2010 e está em fase de teste (svn://zeos.firmos.at/zeos/branches/testing), mas totalmente funcional.


Download


A comunidade ZeosLib mantém o repositório svn (svn://zeos.firmos.at/zeos) onde você pode encontrar todas as versões já lançadas e as versões que estão em desenvolvimento.
Como alguma ferramenta svn client (ex. Tortoise) faça checkout do link svn://zeos.firmos.at/zeos/branches/testing para um pasta qualquer.

No final você deverá ter uma estrutura de diretórios igual ao da imagem ao lado.
Apesar de úteis (não sei se essa é a melhor classificação), apenas os diretórios packages e src são realmente relevantes (considerando que o objetivo é a instalação do pacote no RAD Studio 2010). Sendo assim, manter os demais diretórios é opcional.


Instalando o ZeosLib
A biblioteca ZeosLib é muito organizada e o processo de instalação é simples.


1. No IDE do Delphi, abra o projeto ZeosDbo.groupproj;
2. Compile todos os projetos (menu Project -> Build All Projects);
3. Instale o pacote de componentes selecionando a opção "Install" (no menu de contexto) do pacote "ZComponentDesign140.bpl";


Dependendo de como o RAD Studio está configurado, é possível que o IDE apresente uma mensagem de erro quando o segundo pacote (ZPlain140.bpl) for buildado;

[DCC Fatal Error] ZPlain.dpk(30): E2202 Required package 'ZCore' not found

Essa mensagem informa que o pacote ZCore (ZCore140.bpl) não foi encontrado.
Apesar ser clara, a mensagem não é muito intuitiva principalmente depois de ter sido buildado o primeiro pacote (ZCore140.bpl) e, consequentemente, o arquivo existir.
Na verdade o arquivo foi criado e está localizado em C:\Documents and Settings\All Users\Documents\RAD Studio\7.0\Bpl (o diretório depende da versão do sistema operacional e o idioma do mesmo), apenas não está sendo visível pelo RAD Studio.
Para corrigir, basta incluir $(BDSCOMMONDIR)\Bpl no "Library path", como mostra a imagem abaixo.



Após isso você poderá observar a aba "Zeos Access" na Tool Palette.


Usando o Zeos DBO
Quando ainda usava o Delphi 2009, baixei uma versão super-mega-alfa, baseada na release 6.6.5, que dizia ser suportada pelo Delphi 2009. No entanto, não consegui nem mesmo realizar uma conexão com o PostgresSQL.
Com o objetivo de validar o quão estável está essa versão, resolvi fazer alguns testes com os componentes usando uma base de dados no PostgresSQL 8.4.1.

Após configurar o componente de conexão (TZConnection) com os dados para acessar uma base de dados no PostgresSQL, tentei ativar a conexão.
Nesse momento o componente apresentou a mensagem de erro abaixo.



Um detalhe que pode ser corrigido facilmente apenas acrescentando o diretório bin da instalação do postgres (provavelmente C:\Program Files\PostgreSQL\8.4\bin) no system path. Você pode também copiar os arquivos: Comerr32.dll; krb5_32.dll; libconv-2.dll; libeay32.dll; libintl-2.dll; ssleay32.dll para a pasta system32 do Windows!

Acrescentado o diretório no system path, só foi necessário reiniciar o Delphi e tudo correu muito bem.



E por falar em open source, o que acha de ajudar essa versão do ZeosLib a deixar de ser beta?

quarta-feira, 14 de outubro de 2009

Alternativas freeware

Até não muito tempo atrás o mundo open source não era tão expressivo quanto hoje e, muitas vezes, associado a baixa qualidade. No entanto, hoje podemos vislumbrar projetos open source que competem de igual para igual com soluções pagas.
Dedico esse artigo para citar e descrever sobre alguns pacotes de componentes open source e freeware que são excelentes alternativas para produtos comerciais.


The Fastcode Challanges
The Fastcode Challanges é um projeto, ou melhor, é um desafio público de quem escreve o código mais eficiente para substituir funções e procedimentos da RTL e VCL do Delphi. Atualmente a biblioteca conta com mais de 300 funções publicadas para download e geralmente implementadas em assembly inline.
O projeto é realmente sério, tanto que algumas funções foram adotadas pela Borland substituindo as versões originais.


 Synapse TCP/IP Library
Synapse é uma biblioteca de comunicação TCP/IP muito light que preza a simplicidade e é um excelente alternativa para o pessado Indy.
Sua abordagem é um pouco diferente, utiliza comunicação síncrona (blocking). Também oferece comunicação assíncrona, mas de forma limitada.
Uma característica curiosa é a retrocompatibilidade com o Delphi 3. A versão atual (release .39) tem suporte até o Delphi 2009, sendo esse último experimental.
Observando a frequência de atualização dessa biblioteca, pressumo que muito em breve será lançado uma release com suporte ao Delphi 2010.


DCPCrypt
DCPCrypt é um bliblioteca open source que trás vários componentes que implementam algortimos de criptografia e hash.
É compatível com o Delphi 4, 5, 6, 7, 2005, 2006, 2007, 2009. Apesar de não possuir pacotes de instalação exclusivos, também é compatível com a versão 2010 do Delphi.

Algorimos de Criptografia
  • Blowfish
  • Cast 128
  • Cast 256
  • DES, 3DES
  • Ice, Thin Ice, Ice2
  • IDEA
  • Mars
  • Misty1
  • RC2, RC4, RC5, RC6
  • Rijndael (the new AES)
  • Serpent
  • Tea
  • Twofish

  Algoritmos de Hash
  • Haval
  • MD4
  • MD5
  • RipeMD-128
  • RipeMD-160
  • SHA-1
  • SHA-256, SHA-384, SHA-512
  • Tiger

Se você usa o Delphi 2010, pode utilizar os pacotes de instalação do 2009 ou, então, baixar uma versão customizada do DCPCrypt 2.0 b3 (http://rapidshare.com/files/293117004/DCPCrypt_2b2_D2010.7z).
Eu particularmente já tive a oportunidade de usa essa fantástica biblioteca e recomendo.


Delphi Fast ZLib
Uma biblioteca para compressão e descompressão de arquivos no formato zib muito mais rápida do que a que é fornecida pelo Delphi e, além disso, não requer DLL externa.
Esse bliblioteca possui várias otimizações, incluindo código para processadores P6 e alinhamento de 64 bits. Tais otimizações são automaticamente utilizadas conforme as características do hardware.

O exemplo abaixo foi extraído do site official (http://www.dellapasqua.com/delphizlib/) e demonstra como utilizar a biblioteca.
procedure TForm1.Button1Click(Sender: TObject);
var
  InputStream, OutputStream: TFileStream;
  DeCompressionStream: TZDecompressionStream;
  CompressionStream: TZCompressionStream;
  InputFileName, OutputFileName: string;
begin
  //compress
  InputFileName := 'c:\image.png';
  OutputFilename := 'c:\image.png.bzip';
  InputStream := TFileStream.Create(InputFileName, fmOpenRead);
  OutputStream := TFileStream.Create(OutputFileName, fmCreate);
  CompressionStream := TZCompressionStream.Create(OutputStream, zcFastest);
  CompressionStream.CopyFrom(InputStream, InputStream.Size);
  CompressionStream.Free;
  OutputStream.Free;
  InputStream.Free;

  // decompress
  InputFileName := 'c:\image.png.bzip';
  OutputFilename := 'c:\image2.png'; //rename to original into final code
  InputStream := TFileStream.Create(InputFileName, fmOpenRead);
  OutputStream := TFileStream.Create(OutputFileName, fmCreate);
  DecompressionStream := TZDecompressionStream.Create(InputStream);
  OutputStream.CopyFrom(DecompressionStream, 0);
  DecompressionStream.Free;
  OutputStream.Free;
  InputStream.Free;
end;


NewAC Delphi Components
NewAC é um pacote de componente destinados a tarefas de processamento de áudio.
Oferece suporte a vários tipos de arquivos de áudio (wav, Ogg Vorbis, FLAC, Monkey Audio, WavPack, MP3, Windows WMA, True Audio (TTA), OptimFROG, TAK, Musepack, DTS 5.1, AC-3).

Uma rápida descrição das caractrísticas desse pacote são:
  • Audio recording and playback using DirectSound
  • Audio recording and playback using low latency ASIO drivers
  • CD-ripping
  • Supported formats: wav, Ogg Vorbis, FLAC, Monkey Audio, WavPack, MP3, Windows WMA, DTS, AC-3, TTA, OptimFROG, TAK, Musepack
  • Sound mixing, concatenation, cutting and insertion of audio fragments.
  • Audio resampling
  • 8, 16, 24, 32 bit sound support
  • Multi-channel sound
  • Id3v*, Ape, Vorbis tags support
  • Ready for Delphi 2009
  • License: NewAC is free for both open source and commercial use

No link http://symmetrica.net/newac/formats.htm você pode ter uma noção melhor dos formatos de arquivos em comparação com as funcionalidades suportadas.


Professional Screen Camera Component
Como o próprio nome sugere, esse é um excelente componente para que deseja fazer captura do desktop em gravar em avi.
Algumas das suas características são:
  • Save desktop screen to avi file by arbitrary region.
  • Select region from 5 way. (SelObject, FreeHand, FixedMoving, FixedStable, FullScreen).
  • Auto selecting best video codecs in your system. (DivX, XviD, MP4, WMV and Standard AVIs).
  • Capability recording audio from selected input device.
  • Full support recording from single monitor and/or multi monitor.
  • Change arbitrary recording frame rate. (Up to 1000 fps — super fast machines).
  • Capability recording with set timer.
  • Change on time video priority at recording mode.
  • Capability preview before recording or with recording.
  • On time show recording status.
  • On time drawing objects, texts, images and… over video canvas. (Overlay option).
  • On time filter effective (GrayScale Drawing, Reverse Color, Rotation, Brightness, Contrast, Color Adjusting, Saturation, Noise).

 Link para acesso: http://delphitutorial.info/professional-screen-camera-component-v4710.html

quarta-feira, 7 de outubro de 2009

SafeMM - Debug Memory Manager para Delphi e C++

Já faz um tempinho que a Embarcadero publicou o SafeMM. De lá para cá eu posterguei inúmeras vezes a realização de testes para entender melhor a proposta desse gerenciador de memória.

Definição
O SafeMM é um gerenciador de memória "prova de conceito" que tem a finalidade de identificar alguns tipos de AV (Access Violation) que normalmente não são caputrados pelo gerenciador de memória padrão do Delphi.

Vamos para um exemplo.



procedure TForm1.Create(Sender: TObject);
const
  DEFAULT_OPTIONS: array[0..2] of string = ('Opção 1', 'Opção 2', 'Opção 3');
var
  i: integer;
  lMyStrList: TStringList;
begin
  lMyStrList := TStringList.Create;
  try
    for i := low(DEFAULT_OPTIONS) to high(DEFAULT_OPTIONS) do
      lMyStrList.Add(DEFAULT_OPTIONS[i]);

    // ... outras instruções
    Self.cbbAction.Items.Assign(lMyStrList);
    // ... outras instruções

    {$IFDEF DEBUG}
    ShowMessage('String list count: ' + IntToStr(lMyStrList.Count));
    {$ENDIF}
  finally
    lMyStrList.Free;
  end;

  Self.cbbAction.ItemIndex := lMyStrList.Count - 1;
  ShowMessage('String list count: ' + IntToStr(lMyStrList.Count));
end;

No exemplo acima uma lista de strings é criada e preenchida com as entradas de um array constante; essa lista é, então, atribuída para o componente "cbbAction"; por último, é posto em seleção o último item da lista.
Um trecho de código bastante comum, mas há uma falha nas duas últimas linhas. A variável "lMyStrList", naquele ponto do código, já está com sua área de memória liberada.

Se você compilar esse exemplo no Delphi (não sei se o comportamento é idêntico para todas as versões - utilizei o Delphi 2010), o segundo ShowMessage retorna o valor 0 (zero) quando tenta acessar o método Count do objeto lMyStrList, mas nenhuma exceção ou falha ocorre. No entanto prever o comportamento da instrução "lMyStrList.Count - 1;" é classe impossível, já que a memória foi liberada e não há qualquer garantia de que os dados anteriormente pertencentes ao objeto ainda estejam intactos na memória.

Apesar do exemplo aqui ser simples, essa falha na lógica de programação pode resultar em um bug complicado e consumir um tempo razoável para rastreá-lo.

Usando o SafeMM
É justamente para situações como essa que o SafeMM se propõem.
Se você utilizar esse gerenciador de memória qualquer instrução de leitura ou escrita à uma área de memória que não deveria estar acessível resultará em um AV (Access Violation).

A sua instalação é muito simples:
Step 1. Acesso o site da Embarcadero (http://cc.embarcadero.com/Item/27241) e faça o download do SafeMM.
Apesar de o código fonte ser livre, é necessário que você seja um usuário registrado. Caso não seja, registre-se e faça o download (a Embarcadero agradece);
Step 2. Descompacte o arquivo em uma pasta qualquer (apenas os arquivos 'SafeMM.pas' e 'SafeMMInstall.pas' são serão utilizados, os demais pode se apagados se assim você desejar);
Step 3. No Delphi, abra o seu projeto e adicione os arquivos 'SafeMM.pas' e 'SafeMMInstall.pas'; build e já está pronto para depurar;

OBS: Eu testei em um projeto relativamente grande, com um executável principal de muitos módulos (.bpl). Só foi necessário acrescentar a referência desses dois arquivos no projeto principal.

Exemplo de um projeto incluindo o SafeMM.
program Project2;

uses
  {$IFDEF DEBUG}
  SafeMM,
  SafeMMInstall,
  {$ENDIF}
  Forms,
  fuPrincipal in 'fuPrincipal.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Eu utilizei um a diretiva {$IFDEF DEBUG} para garantir que o SafeMM não fosse incluído quando o projeto for buildado para release, ou seja, para a versão final.

Como funciona
O código fonte desse gerenciador de memória é bem pequeno e recomendo dedicar um tempinho para compreender seu funcionamento mais detalhado.
Vou explanar num alto nível como funciona e como é feito para detectar esse tipo de acesso indevido.

O SafeMM substitui as rotinas de gerenciamento de memória (GetMem, FreeMem, ReallocMem e AllocMem) por rotinas próprias (SafeGetMem, SafeFreeMem, SafeAllocMem e SafeReallocMem, respectivamente). Essas rotinas utilizaram listas auxiliares para armazenar registros com informações extras para cada bloco de memória que você for alocado durante a execução do código fonte.
Por exemplo, a linha de código do exemplo acima "lMyStrList := TStrings.Create" necessita alocar memória (que é feito pelo internamente pela rotina SafeGetMem). Nesse momento, uma entrada nessa lista auxiliar é adicionada com diversas informações sobre esse bloco de memória e, entre elas, uma se destaca: "Magic".
A propriedade "Magic", como o próprio nome sugere, é usada para armazenar um código mágico (123123 - decimal). E é utilizada, juntamente com outras variáveis, para manter a integridade desses blocos de informações.
Quando o objeto local lMyStrList é desalocado, o SafeMM realiza várias operações internas e, por último, bloqueia o acesso à região de memória onde o TStringList estava alocado (através da rotina VirtualProtect). Assim, quando você tentar acessar novamente a variável lMyStrList, um AV (Access Violation) é levantado fazendo com que a depuração seja interrompida na linha exata onde ocorreu o acesso indevido.

Simples e funcional!

Considerações
O SafeMM não tem o propósito de substituir o gerenciador de memória padrão do Delphi, ele foi concebido para o processo de depuração.
Um aplicativo normal pode chegar a ter a quantidade memória alocada multiplicada em até 4 vezes. A versão atual é 0.3 e é a mesma desde 29 de março de 2007.

segunda-feira, 5 de outubro de 2009

Assertivas - Boas práticas de programação

Olá a todos.

Há alguns dias atrás eu estava corrigindo um daqueles bugs que só ocorrem muito esporadicamente e que você mal sabe como reproduzi-lo, menos ainda o motivo dele estar ocorrendo.
Provavelmente vocês concordarão que na maioria das vezes esse tipo de bug é ocasionado por um descuido durante a programação. E que a correção desse tipo de falha costuma ser um detalhe em uma única linha de código.
Quem nunca gastou horas de depuração para descobrir que usou a variável errada ou o teste errado dentro de, por exemplo, uma condição "if"?
Por essa razão resolvi revisar muitas daquelas boas práticas de programação que costumamos aprender, mas que deixamos de utilizá-las por achar que somos super-programadores ou por qualquer outro motivo.
O uso de assertivas é um assunto bastante velho e não é um recurso exclusivo do Delphi, muito pelo contrário, quase todas as linguagens que tive a oportunidade de aprender oferecem suporte a esse recurso tão simples e útil.

Definição
As assertivas são validações lógicas de uma condição julgada necessária para o correto funcionamento do algoritmo no momento em que são verificadas. A condição testada pela assertiva representa uma pressuposição assumida e instrui o sistema a notificar sempre que essa condição não for satisfeita, garantindo que não haja um comportamento imprevisto.
Asserções no Delphi são implementadas através da rotina Assert. Apesar do IDE informar que esse procedimento faz parte da unit System, você não encontrará seu código fonte porque é uma função interna do compilador.
A sintaxe de uma assertiva, em Delphi, é a seguinte:
Assert(Condition: Boolean; [Message: string]);

O parâmetro "Condition" é uma expressão booleana que representa a condição a ser satisfeita. O parâmetro "Message" é opcional e pode ser utilizado para especificar uma mensagem customizada exibida quando a condição não for satisfeita.
O funcionamento do Assert é bastante simples. A condição especificada pelo parâmetro Condition é validado e, se o resultado for True, o fluxo de execução continua normalmente. Caso contrário, se a condição resultar em False, uma exceção do tipo EAssertFailed será levantada apresentando a mensagem de texto especificada pelo parâmetro "Message".
Caso você não especifique o parâmetro Message, o Delphi irá utilizar por padrão o texto ‘Assertion failure'.
Um exemplo pode ser observado na imagem abaixo:



Por si só a mensagem padrão exibida já é suficientemente útil. Para a maioria das falhas detectada com as assertivas, sabendo-se o local exato no código fonte onde a assertiva encontra-se e obtendo a stack tracing do depurador, é possível identificar e corrigir o bug.

Relevância
Mesmo sendo seu uso muito simples é importante ficar atendo para a finalidade na qual as assertivas se propõem e, assim, evitar que sejam usadas erroneamente.
Tenha em mente que o Assert é uma prática de programação defensiva que instrumenta o código fonte e o instrui a identificar falhas e comportamentos não originalmente previstos e, assim, detectar bugs em seus estágios iniciais.
Apesar de ser na prática bastante usada para validar as entradas de métodos/rotinas, sua proposta não é essa e sim validar condições consideradas obrigatórias e que, quando não satisfeitas, identifica uma falha no sistema.
Muitas vezes a validação de entradas e/ou saídas das rotinas carrega uma pressuposição e, conseqüentemente, passível de uso de assertivas (e recomendável). No entanto, essa sutil diferença de conceito pode fazer a diferença entre incrementar o grau de corretude do seu código fonte ou acrescentar mais bugs.

Versão Interna x Versão de Produção
Uma característica peculiar das assertivas é a possibilidade de "anular" a geração de código binário.
Essa característica é muito útil quando o sistema é buildado e posto em produção, ou seja, quando a intenção é distribuir o software.
Considerando que a finalidade das assertivas é identificar pressuposições não satisfeitas, quando a versão de sistema é considerada estável e, portanto, passível de ser distribuída, a validação dessas condições não são mais relevantes do ponto de vista dos usuários (clientes) e, portanto, não há necessidade que seja gerado código binário.
O Delphi possui um item de configuração específico que permite habilita/desabilita a compilação das assertivas.
Acesso o menu Projet -> Options -> aba Compiler -> grupo Debugging -> opção "Assertions", conforme mostra a imagem abaixo:



Também existe uma alternativa que pode ser utilizada para forçar a compilação das assertivas ou, então, para desabilitá-las em trechos específicos de código fonte.
Nesse caso há as diretivas de compilação {$ASSERTIONS ON / OFF} e {$C + / -}. Com elas você pode delimitar uma região do código fonte que habilitarão ou não a geração de código para as assertivas.

Use Case 1:
Esse é um exemplo muito simples e não tem uma funcionalidade prática, mas servirá para demonstrar o uso desse recurso fantástico.

function TfrmPrincipal.GetCount: Integer;
begin
  Assert(Self.FItems <> nil);

  Result := Self.FItems.Count;
end;

Observe que nossa assertiva está pressupondo que a propriedade FItems não pode ser nil.
É fácil prever que se não houvesse essa assertiva e a propriedade FItems não fosse inicializada, quando tal método fosse chamado o sistema certamente falharia. O que caracteriza uma brecha para um bug se instalar e que pode ser facilmente detectado com o acréscimo do Assert.
Entretanto dizer se essa assertiva está ou não empregada corretamente irá depender da finalidade do método e da sua visibilidade (private, protected, public).
Nos próximos exemplos essa questão será abordada.

Use Case 2:
Vamos imaginar um programa simples que disponibiliza ao cliente dois campos de edição para valores numéricos e, ao pressionar um botão, é apresentado um dialog com o valor da divisão entre os valores desses dois campos.
Sabemos que a divisão por zero ocasiona uma exceção (EDivByZero) e necessita ser tratado.

function Dividir(const Dividento, Divisor: Integer): Double;
begin
  Assert(Divisor <> 0);

  Result := Dividento / Divisor;
end;

procedure TfrmPrincipal.btnResultadoClick(Sender: TObject);
var
  lResultado: Double;
begin
  lResultado := Dividir(sedtDividento.Value, sedtDivisor.Value);

  ShowMessage('Resultado da divisão: ' + FloatToStr(lResultado));
end;

Quando o botão é pressionado, o evento “btnResultadoClick” é gerado. Este chama a rotina “Dividir” repassando os valores dos dois campos e apresentando um resultado em um dialog.
No método Dividir há uma assertiva que pressupõem que o valor do parâmetro Divisor não seja 0 (zero).
Esse é um exemplo do emprego incorreto do Assert.
No momento em que a compilação de assertivas for desabilitada, a possibilidade de falha seria alta, principalmente porque os valores da divisão são fornecidos diretamente pelo cliente.
A visibilidade da rotina “Dividir” vai além de simplesmente ser usada internamente. Além disso, as assertivas não devem ser utilizadas para validar regras de negócio e, portanto, seu emprego no exemplo acima deve ser revisto.

Use Case 3:
O exemplo abaixo apresenta uma situação bastante problemática e que certamente resultará em dores de cabeça no momento em que o sistema for posto em produção.

procedure TfrmPrincipal.AddItem(Item: TObject);
begin
  Assert(Self.FItems.Add(Item) >= 0);
end;

O método “AddItem” tem a finalidade de acrescentar um objeto, passado por parâmetro, a uma lista interna. E há uma pressuposição de que esse item sempre será acrescentado à lista.
Mas aí eu questiono: O que acontecerá se a compilação dos Assertions for desabilitada?
A resposta é muito simples, o sistema falhará. E pior, a falha pode ocorrer em um ponto do código que aparentemente não apresenta relação com o verdadeiro causador do bug.
Então você houve aquela famosa frase: “Sim, mas eu testei e estava funcionando perfeitamente!”

Use Case 4:
Na maioria das vezes o Assert é utilizado no início de métodos/rotinas que exigem a obrigatoriedade de parâmetros, faixa de valores específicos, recursos alocados, variáveis ou objetos inicializados ou qualquer outra condição necessária para o correto funcionamento desse método/rotina. Mas como dito no início desse artigo, o uso de Assert é uma prática de programação defensiva.
Imaginemos uma situação onde o sistema realize um cálculo relativamente complexo e que, baseando-se nos parâmetros fornecidos, o resultado nunca deverá ultrapassar uma faixa de valores específica.

procedure TfrmPrincipal.btnResultadoClick(Sender: TObject);
var
  lResultado: Integer;
begin
  lResultado := Calcular(Param1, Param2, Param3, Param4);
  Assert(lResultado <= 80, 'Resultado fora da faixa permitida.');

  // ...
end;

A assertiva aqui é de grande valia e possibilita identificar uma situação falha, seja ela porque os valores dos parâmetros fornecidos não estavam corretos ou porque a função “Calcular” não está realizando o cálculo de forma correta. Independente do motivo, o Assert garante que a inconsistência não seja levada adiante na execução do sistema.

Use Case 5:
Esse próximo exemplo é um pouco semelhante ao caso acima, mas é uma situação comum.
Imagine que o sistema mantenha uma lista dos componentes relacionados com uma funcionalidade qualquer. A referência desses componentes são armazenadas em um objeto TList e em um dado momento é necessário obter o índice de um desses componente dentro da lista.

procedure TfrmPrincipal.btnResultadoClick(Sender: TObject);
var
  lResultado: Integer;
begin
  lResultado := Self.FItems.IndexOf(Self.FSelected);
  Assert(lResultado >= 0, 'Componente não encontrado.');

  // ...
end;

Aqui há a pressuposição de que o componente selecionado deve obrigatoriamente ter usa referência armazenada em um dos itens da lista. Caso isso não ocorra, há o indício de um bug.
Novamente temos um exemplo de programação defensiva que pode evitar dores de cabeça.

Use Case 6:
A situação abaixo não é muito comum e, particularmente, nunca li qualquer relato, mas é uma prática que costumo utilizar quando estou implementando eventos de um formulário.
Vamos supor que um dado sistema deva possuir uma tela de cadastro de clientes, e uma das informações requeridas pelo cadastro é o estado civil.
Então, durante a fase de construção do formulário, você acrescente dois componentes TRadioButtom. Um para representar o estado civil ‘Solteiro’ e o outro para representar o estado civil ‘Casado’.
O sistema determina que se o cliente não for solteiro, alguns campos devem ser habilitados e preenchidos durante o cadastro.
Então você resolve implementar essa regra de negócio da seguinte forma:

procedure TfrmPrincipal.rdbEstadoCivilClick(Sender: TObject); begin
  if Sender = rdbCasado then
  begin
    tabConjugue.Visible := True;
    // Outras tarefas
  end
  else
    tabConjugue.Visible := False;
end;

Ou seja, você compartilha o mesmo evento OnClick com os dois componentes TRadioButtom, tornando visível uma parte do formulário caso o componente rdbCasado esteja marcado.
Depois de um tempo você julga necessário acrescentar outro estado civil (Viúvo) e, então, insere um novo TRadioButtom e associa o mesmo método ao evento OnClick deste novo componente.
Se você esquecer-se de modificar o código prevendo essa nova condição, o sistema não irá falhar e, dependendo do caso, pode ser facilmente detectado em testes funcionais. Entretanto, o uso de assertivas aqui pode economizar um tempo considerável e tornar a vida da equipe de testes mais tranqüila.

procedure TfrmPrincipal.rdbEstadoCivilClick(Sender: TObject); begin
  Assert((Sender = rdbCasado) or (Sender = rdbSolteiro));

  if Sender = rdbCasado then
  begin
    tabConjugue.Visible := True;
    // Outras tarefas
  end
  else
    tabConjugue.Visible := False;
end;

Aqui o Assert está pressupondo que esse evento só será gerado pelos componentes “rdbCasado” e “rdbSolteiro”.
Assim, se outro componente fosse associado com esse método, a assertiva irá lembrar-te de que algo está faltando.

Use Case 7:
A criação de componentes ou de classes persistentes (que herdam de TPersistent) exige a implementação do método Assign, cuja finalidade é copiar os dados de um outro objeto de mesmo tipo (ou não) para as respectivas propriedades.
Durante a fase de criação dessas classes é muito comum ocorrer constantes modificações na lista de propriedades, o que exige a atualização do método Assign. No entanto, muitas vezes você não atualiza o método Assign imediatamente após ter acrescentado novas propriedades, deixando para um momento mais apropriado.
Então outras tarefas surgem e os métodos de importação/exportação ficam desatualizados.
Vamos analisar o exemplo abaixo:

TTeste = class(TPersistent)   private
    FID: integer;
    FNome: string;

  public
    property ID:   integer read FID;
    property Nome: string  read FNome write FNome;

    procedure AssignTo(Dest: TPersistent); override;
end;

implementation

procedure TTeste.AssignTo(Dest: TPersistent);
begin
  Assert(Dest <> nil);
  Assert(Dest.inheritsFrom(TTeste));
  Assert(Dest.InstanceSize = 16, ‘A classe TTeste foi modificada, revise o método Tteste.AssignTo()!’);

  TTeste(Dest).FID   := Self.FID;
  TTeste(Dest).FNome := Self.FNome;
end;

Observe o Assert que está em cor vermelha. Se uma nova propriedade fosse acrescentada à classe, o Assert notificaria que o método Assign está desatualizado (logicamente quando ele fosse chamado).
É claro que fazer uma validação utilizando o InstanceSize da classe não irá identificar todos os tipos de mudanças. Você poderia, por exemplo, modificar o tipo da propriedade “FID” para ShortInt e acrescentar outra propriedade, também ShortInt, que e o tamanho da classe permaneceria os mesmos 16 bytes.
Mesmo assim, a assertiva é de grande ajuda. Principalmente em caso onde há dependência de classes.
Métodos de exportação e importação também são exemplos semelhantes onde esse mesmo caso se aplica.

Customizando o Assert
Até agora vimos diversas situações que podemos empregar o uso do Assert para identificar falhas ou nos ajudar a revisar trechos de código fonte que são dependentes de alguma recurso (classe, record, ...).
Para a maioria das necessidades dos desenvolvedores/testers, o uso padrão do comado Assert já é suficiente, mas este comando ainda guarda um recurso muito interessante, que é a sua customização.
Sempre que um asserção é levantada, o Delphi executa a rotina referenciada pela variável AssertErrorProc.
A variável AssertErrorProc está localizada na unit System e é, na verdade, um ponteiro para uma função com a seguinte assinatura:

procedure (const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);

Isso significa que você pode escrever o seu próprio tratador de Assertivas bastando. escrever uma rotina com a mesma assinatura e atribui-la para a variável global AssertErrorProc.

Exemplo:
procedure MeuTratador(const Message, Filename: string; LineNumber: integer; ErrorAddr: Pointer);
begin
   // Seu código fonte.
end;

AssertErrorProc := MeuTratador;

Talvez você se pergunte: “Mas para que eu necessitaria implementar um tratador de assertiva?”
Há algumas situações que podem ser muito úteis como por exemplo o envio de um email de notificação para o time desenvolvimento; manter um log de falhas em arquivo; automatizar alguma ação relacionada a testes; abrir um ocorrência de falhas em um sistema externo;...

quinta-feira, 24 de setembro de 2009

Windows 7 + VisualSVN Server + OpenSSL + TortoiseSVN + Trac (Parte 4)

1. Overview

Trac é uma simples ferramenta, open source e de interface web para controle de mudanças em projetos de desenvolvimento de software. O objetivo do software é ajudar o desenvolvedor a rastrear essas mudanças, entender o porquê de cada uma e qual o seu impacto no projeto como um todo.

A finalidade desse documento não é falar sobre os recursos e funcionalidades da ferramenta (onde maiores detalhes podem ser encontrados no link http://pt.wikipedia.org/wiki/Trac), mas descrever o passo-a-passo necessário para instalar essa magnífica ferramenta e integrá-la com o repositório do subversion e ao VisualSVN Server.

No site official do VisualSVN (http://www.visualsvn.com/server/trac) é disponibilizado uma versão pré-configurada do Trac juntamente como o passo-a-passo necessário para integrá-lo ao VisualSVN Server. No entanto, apesar desse caminho ser relativamente mais simples, é provável haver situações que você necessite realizar upgrades, acréscimo/remoção de plugins, migração de banco de dados e outras tarefas que exigirá um conhecimento maior sobre o processo, os componentes instalados e suas funcionalidades.

Pensando nisso, esse documento não usará de atalhos ou simplificações e o passo-a-passo da instalação e integração da ferramenta Trac será detalhado de uma forma completa, descrevendo as razões para as respectivas escolhas.


2. Considerações
 
A ferramenta Trac foi desenvolvida na linguagem de programação Python e conta com uma gama variada de plugins. Os plugins também são desenvolvidos em Python. No entanto, é comum um determinado plugin suportar uma ou outra versão do Python, limitando seu uso.
 
Além disso, muitos plugins também só podem ser integrados a versões específicas do Trac.

Antes de iniciar a instalação dos softwares requisitos, é importante avaliar quais os plugins deseja instalar ao Trac de forma a escolher adequadamente a versão do Python e de outros softwares requisitos.


Passo 1 – Instalação do Python
 
A versão atual do Trac (0.11.5) requer a versão 2.3 até 2.6 do Python. A recomendação aqui é utilizar a versão 2.5 devido à maior compatibilidade e suporte por parte dos plugins e drivers.
 
Apesar de esse documento utilizar tal versão, novamente destaco a importância de avaliar a compatibilidade exigida pelos demais softwares/plugins que serão integrados.

Mais especificamente falando, será usado a versão 2.5 em vez da 2.5.4 (mais recente) porque um dos drivers necessários (mod_python 3.3.1) está, atualmente, disponível para tal release. Essa questão será novamente abordada em outro tópico.

A instalação pode ser baixada através do link: http://www.python.org/ftp/python/2.5/python-2.5.msi.

Por padrão, a instalação assume o diretório C:\Python25 como o local onde serão descompactados os arquivos. No entanto usaremos o path C:\Program Files\Python\.

O processo é simples bastando apenas avançar as abas da instalação (Next, Next, Finish).

Passo 1.1 – System Path
 
Nas variáveis de ambiente do Windows, acrescente o caminho da instalação do Python no System Path.
 
Também será necessário adicionar uma variável de ambiente “PYTHONHOME” com o caminho da instalação do Python.




Passo 2 – Instalação do SetupTools

O SetupTools é um coleção de ferramentas para o Python distutils que permite a criação e distribuição de pacotes Python mais facilmente.

Necessitaremos desse pacote para a instalação de plugins e, inclusive, a própria ferramenta Trac.

A instalação pode ser feita através de forma automática, utilizando a instalação binária, ou de forma manual.

Particularmente eu recomendo a instalação manual porque essa opção elimina a possibilidade de você instalar uma versão inadequada ou não suportada pela versão do Python. Nesse caso, será necessário baixar o bootstrap module e copia-lo para a pasta scripts, no diretório de instalação do Python (C:\Program Files\Python\Scripts).

$ cd c:\Program Files\Python
$ md Scripts
$ cd Scripts
$ ez_setup.py

O script ez_setup.py irá fazer o download e instalar a versão adequada do SetupTools.
O resultado da execução desse script deve ser semelhante à apresentada abaixo:

C:\Program Files\Python\scripts>ez_setup.py
Downloading http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c9-py
2.5.egg
Processing setuptools-0.6c9-py2.5.egg
Copying setuptools-0.6c9-py2.5.egg to c:\program files\python\lib\site-packages
Adding setuptools 0.6c9 to easy-install.pth file
Installing easy_install-script.py script to C:\Program Files\Python\Scripts
Installing easy_install.exe script to C:\Program Files\Python\Scripts
Installing easy_install-2.5-script.py script to C:\Program Files\Python\Scripts
Installing easy_install-2.5.exe script to C:\Program Files\Python\Scripts

Installed c:\program files\python\lib\site-packages\setuptools-0.6c9-py2.5.egg
Processing dependencies for setuptools==0.6c9
Finished processing dependencies for setuptools==0.6c9

Como dito acima, pode-se optar pela instalação binária. Nesse caso, será necessário baixar a versão do SetupTools compatível com a versão do Python.

No link http://pypi.python.org/pypi/setuptools é possível encontrar a instalação binária for MS Windows.
Concluído o download, o processo de instalação é igualmente simples (Next, Next, Finish). A versão utilizada na criação desse tutorial é a 0.6c9 e foi baixada desse link.



Após a instalação, confirme a existência do subdiretório Scripts e, dentro dele, o arquivo easy_install.exe.

OBS: É necessário que seja dado permissão de administrador para a instalação (Run as Administrator).

Passo 2.1 – Path

Acrescente o subdiretório Scripts (C:\Program Files\Python\Scripts) no system path, igualmente como feito no passo 1.1.
 
 
Passo 3 – Instalação do Genshi

A instalação pode ser feita através do SetupTools ou através da instalação binária.
Em caso de ser feita através do SetupTools, basta abrir um prompt de comandos (aberto com privilégio de Administrador) e digitar:

$ easy_install Genshi



Se desejar a instalação binária, faça o download pelo link e instale-o normalmente (com privilégio de Administrador).

Também há outras formas. Se achar necessário, consulte esse site.

OBS: A imagem acima mostra a instação bem sucedida do Genshi utilizando o SetupTools. No entanto, após concluir toda as etapas de instalação e acessar o Trac via web browser, o módulo de interpretação do python (mod_python) notificou erro informando que o Genshi não estava instalado. O problema só foi solucionado utilizando a instalação binária.

 
Passo 4 – ClearSilver

O ClearSilver é uma linguagem neutra para system de template HTML. Sua instalação é opcional, mas é requerida para alguns plugins.

Durante a criação desse tutorial, tentou-se realizar a instalação através do comando “easy_install ClearSilver”. O SetupTools fez o download da versão 0.10.1, mas falhou ao instalar o pacote. A solução foi utilizar a url completa para o pacote de versão 0.10.4 para python 2.5.



Passo 5 – Instalando Banco de Dados

Será necessária a instalação de um banco de dados na qual o Trac usará para seu funcionamento. Atualmente é oferecido suporte ao SQLite, PostgresSQL e MySQL. Isso porque o Python possui drives para apenas esses três banco de dados, sendo o driver para MySQL ainda experimental.

Passo 5.1 – Drive para PostgresSQL

Para utilizar o PostgresSQL como gerenciador de banco de dados no Track, é necessário a instalação do driver pyPgSQL e do pacote mxDataTime e são detalhados nos sub-tópicos abaixo.

Apesar dos procedimentos de instalação ser simples, em diversos outros momentos onde optei por utilizar o PostgreSQL, enfrentei diversos problemas e conflitos. Por isso, sugiro o uso do SQLite que é bem mais flexível, principalmente em caso de migração.

Passo 5.1.1 – O gerenciador de banco de dados

Se você já possui o PostgreSQL instalado, pule essa etapa. Caso contrário acesse o link http://www.postgres.org/ e baixe a versão desejada.
Mas antes de fazer o download, leia o próximo tópico.

Passo 5.1.2 – Driver pyPgSQL

A versão do drive pyPgSQL a ser instada depende da versão do gerenciador de banco de dados e da versão do Python.
No link http://www.stickpeople.com/projects/python/win-psycopg/ você encontra uma tabela de downloads que, atualmente, disponibiliza a versão 2.0.12 para o Python 2.5 (usado nesse documento) e para o PostgreSQL 8.4.0 (link direto).

OBS.: A instalação binária deve ser executada com permissões de administrador.

Passo 5.1.3 – Instalando mxDateTime

O mxDateTime implementa novos tipos de objetos: DateTime, DateTimeDelta e RelativeDateTime e varias outras ferramentas destas for tornar fácil a conversar em formatos date/time. Esse pacote é uma exigência do drive pyPgSQL.
Com a instalação do SetupTools, basta executar o seguinte comando no prompt:

easy_install egenix-mx-base

Em caso de falha, é possível fazer o download da instalação binária (para download link aqui)

OBS.: Mais uma vez lembrando de que é requerida permissão de administrador para a instalação binária.

Passo 5.2 – Drive para SQLite

No caso de você optar pelo SQLite, o processo é mais simples.

Passo 5.2.1 – O gerenciador de banco de dados

Esse gerenciador de banco de dados é muito simples (nem mesmo requer um instalador).
a) Acesse o site oficial (http://www.sqlite.org/) e faça o download da versão for Windows mais recente. Atualmente a versão disponível é a 3.6.17 (link direto);
b) Crie uma pasta chamada “SQLite” em “C:\Program Files” e descompacte o arquivo dentro dessa pasta;
c) Por último, acrescente C:\Program Files\SQLite no path do sistema (semelhante ao tópico 1.1)
Uma outra sugestão, em vez de criar um diretório específico para o SQLite, você pode copia-lo para, por exemplo, o diretório de instalação do Python (que já está referenciado no system path).

Passo 5.2.2 – Drive pySQLite

É necessário instalar o drive pySQLite no Python. Essa instalação pode ser feita manualmente (recomendável) ou automática, através de uma instalação binária.
Se optar por fazer manualmente (processo mais simples) abra um prompt de comandos com privilégio de administrador e simplesmente digite o comando abaixo.

$ easy_install pysqlite

Além de mais simples e rápido, e modo remove a possibilidade de você instalar uma versão incompatível com a do Python.

A instalação binária pode ser encontrada nesse link. Deve observar que é necessário a instalar o drive correspondente à versão do Python que foi instalado, que nesse caso é a 2.5 (link direto).




Passo 6 – SVN Python

A instalação dos pacotes de acesso e gerenciamento do repositório svn no python é um dos requisitos obrigatórios e deve considerar a versão do servidor Apache que está instalado e também a versão do Python. E como esse tutorial é destinado a integrar o Trac no VisualSVN Server (mas especificamente a versão 2.0.6 do VisualSVN Server), então você deverá optar por uma instalação com suporte ao Apache 2.2.x.

Isso porque o VisualSVN Server (versão 2.0.6) é composto pelo Apache/2.2.13 (Win32) DAV/2 SVN/1.6.5 mod_ssl/2.2.13 OpenSSL/0.9.8k.

Esse pacote pode ser acrescentado ao Python através de uma instalação binária ou de forma manual (usando o SetupTools), mas deve-se adotar uma instalação compatível com a versão do Subversion (que nesse caso é a 1.6.5) e a versão do Python (2.5).

O instalador é encontrado no mesmo site do Subversion, descrito no tópico acima (http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100) ou pelo link direto.

OBS: É requerido “Run as administrator”.

Se preferir, pode fazer a instalação através do SetupTools:

$ easy_install -Z http://subversion.tigris.org/files/documents/15/46527/svn-python-1.6.5.win32-py2.5.exe


Passo 7 - Módulo mod_python

Esse módulo é particularmente necessário para o Apache HTTP Server e deve ser compatível com a versão do python e do próprio Apache instalado (v2.2.13 – VisualSVN Server).

O site oficial (http://www.modpython.org/) redireciona para a página de download de módulos do Apache (http://httpd.apache.org/modules/python-download.cgi). A versão atual 3.3.1 é destinada ao Apache HTTP Server 2.0 e 2.2 (que é o nosso caso). Link direto.

Antes de fazer a instalação é recomendável criar uma pasta chamada “modules” no diretório de instalação do VisualSVN Server. Isso porque o instalador do mod_python irá solicitar, em um dado momento, que informe o diretório de instalação do Apache. No entanto, o VisualSVN Server tem uma estrutura de diretório levemente diferente da do Apache, e isso inclui não possuir a pasta “modules” para onde o instalador irá copiar o módulo mod_python.so.

$ cd c:\Program Files\VisualSVN Server\
$ md modules



OBS: Se após concluída todas as etapas de instalação do Trac ou em um upgrade for exibida a mensagem abaixo no web browser, isso é um forte indicativo de que necessite instalar/reinstalar o mod_python.

Warning: Can't synchronize with the repository (Unsupported version control system "svn": "No module named svn"). Look in the Trac log for more information.


Passo 8 – Trac

Passo 8.1 – Instalando o Track

Para instalar o Track você pode fazer através da instalação binária (), ou então através do SetupTools.
Através do SetupTools basta executar o comando abaixo no prompt de comandos.

$ easy_install Trac

Também pode-se utilizar o link do repositório subversion.

$ easy_install http://svn.edgewall.org/repos/trac/tags/trac-0.11



OBS: O prompt de comandos deve ser aberto com privilégios de administrador.

Vá até o diretório C:\Program Files\Python\Tools\Scripts e execute o comando:

$ cd C:\Program Files\Python\Tools\Scripts
$ python setup.py install


Passo 8.2 – Trac-Admin

O Trac environment é onde serão armazenadas as informações sobre Wiki pages, Tickets, Reports, Settings, etc. Um environment é basicamente um diretório que contém um arquivo de configuração específico juntamente com vários outros arquivos e diretórios.

Para exemplificação, nosso projeto será criado no caminho “D:\Repositories\Trac”. Esse diretório deve ser manualmente criado.

Criado o diretório, vamos criar um novo ambiente usando trac-admin:

$ trac-admin D:\Repositories\Trac\MyProject initenv

O trac-admin irá solicitar informações necessárias para criar o ambiente, tal como nome do projeto, string de conexão com a base de dados, repositório, ...

Para exemplificar usaremos o projeto “MyProject”. Assim, quando solicitado informe:

Passo 8.2.1 – Se estiver usando PostgresSQL

Project Name[My Project]: MyProject
Database connection string [sqlite:db/trac.db]: postgres://tracuser:tracpass@localhost:5432/trac
Repository type [svn]: svn
Path to repository [/path/to/repos]: D:\Repositories\SVN\MyProject



OBS: A string de conexão com o banco de dados especifica: O usuário tracuser, cuja senha é tracpass, tem acesso à base de dados trac cuja conexão é feita através o hostname localhost e porta 5432.

Tanto o usuário quanto a base de dados devem ser previamente criados pela ferramenta DB administrativa de sua preferência.

Passo 8.2.2 – Se estiver usando SQLite

Project Name[My Project]: MyProject
Database connection string [sqlite:db/trac.db]: sqlite:db/myproject.db
Repository type [svn]: svn
Path to repository [/path/to/repos]: D:\Repositories\SVN\MyProject



Passo 8.3 – Execução do Servidor Standalone

Após a criação do ambiente trac, você pode facilmente acessar a interface web através da execução do standalone server tracd.

$ tracd --port 8000 D:\Repositories\Trac\MyProject

No browser digite: http://127.0.0.1:8000/

Passo 8.4 – Firewall / Permissões para o Python

Quando você inicializar o servidor em modelo standalone pela primeira vez, o firewall do Windows 7 irá questiolá-lo sobre o tipo de permissão que o Python deverá receber.

Uma tela semelhante à imagem abaixo será exibida.



Marque das duas opções (como é mostrado na imagem) e clique no botão “Allow access”.

Passo 8.5 – Criando repositório svn

Ao acessar http://127.0.0.1:8000/MyProject/ pelo browser, provavelmente será exibida a seguinte mensagem:

Warning: Can't synchronize with the repository (D:/Repositories/SVN/MyProject does not appear to be a Subversion repository.). Look in the Trac log for more information.

Essa mensagem é porque o repositório svn (MyProject) não foi criado.

$ cd c:\Repositories
$ svnadmin create c:\Repositories\MyProject


Passo 9 – Configurando o VisualSVN Server

Esse capítulo é dedicado a configuração do VisualSVN Server para integrar a ferramenta Trac. E os passos abaixo foram descritos considerando a versão 2.0.7 do VisualSVN Server.
Essa versão utiliza o Apache 2.2.13 e o Subversion 1.6.5.

Passo 9.1 - Arquivo vsvnvars.bat

No arquivo vsvnvars.bat, localizado em C:\Program Files\VisualSVN Server\, acrescente uma linha contendo: set PYTHONHOME=c:\Program Files\Python

@echo off

set PYTHONHOME=c:\Program Files\Python

set PATH=%~dp0bin;%PATH%

IF "%1" == "" GOTO welcome
%*

GOTO end

:welcome
echo Welcome to VisualSVN Server command prompt!
echo Use 'svn help', 'svnadmin help' and 'svnlook help' for more information.
echo Type 'exit' to exit.
GOTO end

:end

Passo 9.2 - Configurando httpd-custom.conf

Em C:\Program Files\VisualSVN Server\conf, abra o arquivo httpd-custom.conf e acrescente no final as seguintes linhas:

LoadModule python_module modules/mod_python.so
LoadModule authz_user_module bin/mod_authz_user.so
<Location /trac>
  SetHandler        mod_python
  PythonInterpreter main_interpreter
  PythonHandler     trac.web.modpython_frontend
  PythonOption      TracEnvParentDir D:\Repositories\Trac
  PythonOption      TracUriRoot /trac
  PythonDebug       on

  AuthName          "Trac"
  AuthType          Basic
  AuthBasicProvider file
  AuthUserFile      "D:/Repositories/SVN/htpasswd"
  Require valid-user
</Location>

Agora basta resetar o VisualSVN Server.

quarta-feira, 23 de setembro de 2009

Windows 7 + VisualSVN Server + OpenSSL + TortoiseSVN + Trac (Parte 3)


1. SVN Client / Tourtoise


O TortoiseSVN é desenvolvido pela Tigris.org, um consórcio que desenvolve soluções open source. Este software é classificado como "Windows Explorer Shell Extension", e uma vez instalado, acrescenta uma opção do menu contextual do Windows Explorer (menu exibido com o clique do botão direito do mouse) apresentando todas as tarefas de controle de versão poderá executar.

Essa ferramenta é muito prática, devido sua integração com o Windows Explorer. Se preferir, pode utilizar um client de linha de comando.

Esse documento não irá detalhar os recursos e funcionalidades do Tortoise, mas sim como configurar o sistema para utilizar o certificado client automaticamente.

Devido ao VisualSVN Server estar configurado para exigir uma conexão SSL e, consequentemente, solicitar um certificado de cliente, em toda operação que exigir leitura ou escrita no repositório svn, o tortoise irá apresentar um dialog solicitando que você informe o caminho onde o certificado client (arquivo .pfx) encontra-se e, posteriormente, a senha de proteção deste certificado.

Para evitar que tortoise sempre solicite o certificado será necessário modificar o arquivo de configuração do subversion (servers). Esse arquivo está localizado em %APPDATA%\Subversion sob o nome “servers” (não possui extenção).

O diretório %APPDATA% é relativo ao sistema operacional, ao idioma do sistema operacional e ao usuário logado. Por exemplo, se você estiver usando Windows XP, uma possível de localização é: C:\Documents and Settings\lovatof\Application Data\Subversion.

Em caso de dúvida, basta digitar %APPDATA% [ENTER] no Windows Explorer e ele expandirá essa variável de ambiente para o correspondente valor.

Abra o arquivo “servers” e localize a linha onde aparece “[groups]”.

O trecho abaixo é um exemplo de configuração que você pode utilizar como base.
[groups]
flovatosvn               = flovatosvn.myserver.com.br
local                    = localhost

[flovatosvn]
ssl-client-cert-file     = C:\Program Files\VisualSVN Server\bin\Cert\Fabius.pfx
ssl-client-cert-password = [minha senha]
ssl-trust-default-ca     = yes
http-compression         = yes
http-timeout             = 300

[local]
ssl-client-cert-file     = C:\Program Files\VisualSVN Server\bin\Cert\Fabius.pfx
ssl-client-cert-password = [Minha Senha]
ssl-trust-default-ca     = yes
http-compression         = yes
http-timeout             = 300


Em “[groups]” foi acrescentado a definição de dois grupos (flovatosvn e local). Isso significa que quando o svn client (no caso o tortoise) for acessar um repositório svn utilizando o endereço localhost (por exemplo: https://localhost:8443/svn) deverá ser utilizado as configurações do grupo “local”. O mesmo vale para o grupo “flovatosvn”.

O item “ssl-client-cert-file” define a localização completa do certificado de cliente que deve ser utilizado para a autenticação. Nesse caso, modifique esse parâmetro para refletir a condição definida em seu ambiente de trabalho.

O item “ssl-client-cert-password” é utilizado para armazenar a senha de proteção do certificado. Particularmente eu não recomendo ignorar esse item de configuração por questões de segurança. No entanto, se não for especificado o tortoise irá solicitar tal senha sempre que um acesso ao repositório for realizado.


2. PHP

O suporte ao PHP é um requisito para o funcionamento do WebSVN que será abordado no capítulo abaixo. Além disso, o uso do PHP pode estender à outros frameworks ou mesmo implementações internas.

As etapas abaixos descreve o que deve ser feito a instalação/configuração do PHP5 no Windows 7 com foco ao VisualSVN Server, no entanto esse mesmo passo-a-passo pode ser utilizado (como uma ou outra diferença) para a instalação em outra versão do MS Windows e/ou com foco no Apache.

    2.1 Instalação
No site http://br.php.net/ você encontra informações mais detalhadas sobre o PHP. No nosso tutorial utilizaremos a versão mais recente e estável disponível (verão 5.2.11). Se desejar, faça o download pelo link direto.

Crie uma pasta chamada “PHP” em %ProgramFiles% (C:\Program Files\PHP) e descompacte a instalação para essa pasta.

    2.2 Configurando o PHP
a) No diretório de instalação do PHP (%ProgramFiles%\PHP), copie o arquivo php5ts.dll para a pasta bin do diretório de instalação do VisualSVN Server (%ProgramFiles%\VisualSVN Server\bin). Se você preferir pode copiá-lo para a pasta system32 do Windows (%WINDIR%\system32) como a maioria dos tutoriais recomendam.
A finalidade é tornar acessível tal arquivo acrescentando-o em um local “visível” ao servidor http(s). Não cheguei a testar, mas acredito que simplesmente acrescentando a pasta C:\Program Files\PHP no system path tenha o mesmo efeito.

b) Também no diretório de instalação do PHP você encontra o arquivo “php.ini”. Abra-o e localize a linha onde aparece extension_dir = “./” e modifique para extension_dir = “c:/Program Files/PHP/ext/”, que é o caminho complato do diretório onde ficam as extensões do php (GD, MySQL, Postgres, ...).

c) Caso você tenha instalado MySQL, PostgresSQL, ou outro banco de dados e deseje acessá-los) via PHP, localize, por exemplo, a linha onde aparece ;extension=php_mysql.dll e remova o caracter ponto-e-vírgula (;). Esse caracter tem funcionalidade de comentário (assim como // ou /* */ em C/C++ ou { } em Delphi, ...).
Nesse exemplo, é acrescentado suporte ao MySQL, mas há diversas outras extensões que também estão comentadas, bastando remover o caracter de comentário (;).

d) Seguindo o passo anterior, habilite suporte ao módulo “php_openssl.dll”.

e) O arquivo de configuração do PHP (php.ini) possui muitos parâmetros que permite a customização. Assim, depois de feitas as devidas alterações, salve o arquivo e mova-o para a pasta "conf" do diretório de instalação do VisualSVN (C:\Program Files\VisualSVN\conf).
Aqui a situação é a mesma, o objetivo é tornar o arquivo "visível" ao servidor web. Se preferir, pode mover o php.ini para o diretório de instalação do Windows (%WINDIR%), como é recomendado pela maioria dos guias de instalação.

    2.3. Suporte ao PHP no VisualSVN Server
No arquivo httpd-custom.conf, acrescente as seguintes linhas:
LoadModule php5_module "C:/Program Files/PHP/php5apache2_2.dll"
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps

# configure the path to php.ini
PHPIniDir "C:/Program Files/PHP"

No arquivo httpd.conf, localize a linha onde aparece “<ifmodule dir_module>” e modifique a instrução Directoryindex como aparece abaixo.
<ifmodule dir_module>
    DirectoryIndex index.html index.php default.php main.php
</ifmodule>

Isso fará com que o servidor reconheça os arquivos index.php, default.php e main.php a um diretório aberto. Apesar desses nomes de arquivos serem largamente utilizado, não é obrigatório que utilize-os, você pode incluir inicio.php ou qualquer outro nome de sua escolha.

    2.4 Testando o PHP no browser
Acrescentado a configuração acima listada, é necessório um reset o VisualSVN Server para que este inicialize o PHP.

a) Para verificar se o PHP está sendo carregado e funcionando corretamente, crie um arquivo com extensão .php (exemp: info.php) com o seguinte conteúdo:

<? phpinfo(); ?>

b) Salve esse arquivo na pasta "htdocs" do VisualSVN (%ProgramFiles\VisualSVN Server\htdocs).

c) Em seguida, abra um webbrowser e digite: https://localhost:8443/info.php. Se tudo correr bem, deve se exibido uma tela com as informações sobre a configuração do PHP:


3. WebSVN

O WebSVN, como o próprio nome sugere, é um solução open source que permite a navegação no repositório svn através de uma interface web.

É uma solução cuja proposta se assemelha em muito com ao do VisualSVN Server (no quesito web browser), mas possui uma interface melhorada e com vários recursos.

Acesse o site oficial (http://www.websvn.info/) para obter maiores detalhes.
 
    3.1 Instalação
O processo de instalação é muito simples.

Faça o download a versão mais recente (http://www.websvn.info/download/ ) e descompacte o arquivo para uma pasta qualquer dentro de htdocs do VisualSVN (%ProgramFiles\VisualSVN Server\htdocs).

Se preferir pode deixar o diretório com o nome da pasta contida no arquivo de instalação (websvn-2.2.1), mas sugiro renomeá-lo para websvn.

    3.2. Configurando o acesso ao WebSVN
Caso deseje posteriormente utilizar o WebSVN acrescente o trecho abaixo no final do arquivo (https.conf) ou, o mais recomendado, acrescente as linhas abaixo no arquivo httpd-custom.conf.
<location /websvn/>

    SVNListParentPath  on
    SVNParentPath      "D:/Repositories/SVN/"
    SVNIndexXSLT       "/svnindex.xsl"

    AuthName           "Subversion Web Browsing"
    AuthType           Basic
    AuthBasicProvider  file
    AuthUserFile       "D:/Repositories/SVN/htpasswd"
    AuthzSVNAccessFile "D:/Repositories/SVN/authz"

    Require valid-user
</location>

OBS: O WebSVN faz uso do php para o sincronismo com o svn, além da construção dinâmica das próprias páginas. Sendo assim, é necessário que o PHP esteja instalado, conforme descrito no capítulo anterior.

Agora, resete o VisualSVN Server (ou Apache) e digite no browser: https://localhost:8443/websvn/.

Provavelmente irá aparecer a seguinte mensagem:

File "includes\config.php" does not exists, please create one. The example is located under "includes\distconfig.php"

Em %ProgramFiles%\VisualSVN Server\htdocs\websvn\include, você encontrará um arquivo chamado “distconfig.php”, faça uma cópia desse arquivo com o nome “config.php”.

Abra o arquivo config.php e acrescente a linha “$config->parentPath('D:\\Repositories\\svn');”. Logicamente, se o repositório svn em sua máquina não está localizado em D:\Repositories\svn, altere para refletir o caminho correto.

Salve o arquivo; volte ao browser e tente novamente. Voai lá.