sábado, 23 de junho de 2007

LuaWsgi

Lua/Python Como eu havia prometido nas Reflexões de Monte Gasppa e Giulia C., aqui está o artigo sobre LuaWsgi.

Histórico


Eu ia contar aqui a história de como surgiu o LuaWsgi, mas decidi não fazer isso.

Basta dizer que envolve intriga, maus entendidos e até uma certa dose de má fé.

Mas também gostaria de dizer que desenvolvi o primeiro protótipo funcional em apenas um domingo de diversão.

Outra coisa que é importante dizer é que LuaWsgi nasceu como uma terapia e resolvi publicá-lo para ser meu trabalho de conclusão de curso.

O que é WSGI?


WSGI é uma proposta formal de melhoria para Python, PEP, que sugere uma nova abordagem para o desenvolvimento web mais simpática para programadores.

A forma original como páginas web foram concebidas é muito interessante para não-programadores, pois o HTTP era apenas uma extensão do FTP voltada para a visualização.

Usando HTML, uma linguagem de marcação, era possível criar páginas atraentes com pouco ou nenhum conhecimento de programação.

Porém «marcação» não permite muitos recursos usados na programação. Para suprir essa necessidade, foram criados o CGI as páginas dinâmicas, que são geradas por um programa.

Depois criaram os servlets.

Até aqui, no entanto, o formato continuava desconfortável para programadores – apesar de «arrastadores de componentes» (component drag-n-droppers) se sentirem verdadeiros programadores assim e de alguns servlets serem bastante amigáveis.

Então Phillip J. Eby sugeriu uma API mais interessante para Python: WSGI.

E WSGI é ainda mais! A proposta foi tão bem feita que pode ser implementada em quase qualquer linguagem de programação, quase como um protocolo!

Para saber mais leia a PEP 333.

O que é Lua?


Lua é uma linguagem de programação livre projetada e implementada em 1993 por uma equipe no Lablua da PUC-Rio.

Atualmente se encontra na versão 5.1.2.

É uma linguagem estruturada, orientada a tabelas e com suporte a orientação a objetos.

Sua tipagem é dinâmica, como Python. Não é tão forte quanto a tipagem de Python, mas possui sete tipos bem definidos e de conversão simplificada quando possível.

Para saber mais, leia o livro do Roberto Ierusalimschy.

O que é LuaWsgi?


É uma implementação de WSGI para linguagem de programação Lua da forma mais precisa possível.

Dependências


Para instalar LuaWsgi você precisa instalar antes:


Mas há um jeito mais fácil…

Você pode simplesmente instalar o Projeto Kepler, que já traz todos os módulos que vamos precisar.

Até agora – o momento em que escrevo – não há uma versão final do Kepler 1.1. Se quando você for baixar houver, baixe-a. Se não, baixe o último snapshot.

Obs.: para instalar Kepler 1.1, você precisa ter instalado Expat, ZZipLib, algum SGBD e GNU Readline (você provavelmente possui algum ou todas as dependências, mas é bom verificar).

Não siga as instruções de instalação de Kepler. Faça o seguinte:

Desempacote o tarball (tar xzvf kepler-*.tar.gz). Isso irá gerar um diretório kepler-1.1/. Acesse-o.

Então execute o ./configure com as seguintes opções:
bash$ ./configure --prefix=/usr \
--sysconfdir=/etc \
--lua-suffix='' \
--kepler-web=/var/xavante \
--with-optional=lualogging,luasql,luaexpat,luazip,md5 \
--enable-lua


O que estamos fazendo é dizendo para o configurador onde o instalador deve colocar os arquivos, que módulos queremos usar (todos) e para instalar Lua também.

Você também pode usar a opção --with-luasql-driver= para especificar qual SGBD você está usando. Para obter informações tente:
bash$ ./configure --help


Se tudo correr bem, o resto é instalação padrão:
bash$ make
bash$ sudo make install


Se não houver problemas, você já terá todos os módulos que precisará instalados – e mais alguns úteis. Se quiser testar o servidor web Xavante, ele também estará instalado – mas aí já é outro assunto.

Opa! Falta ainda instalar o lua-unistd!

Você tem duas escolhas: 1baixar o fonte e compilá-lo ou 2baixar o binário pré-compilado.

Acredito que o binário seja suficiente, mas se você tiver problemas, para compilar o fonte o comando é:
bash$ gcc -c -fPIC -O3 -Wall unistd.c
bash$ ld -shared -lm -lcrypt unistd.o -o libunistd.so.1.0.1


Mova a biblioteca para /usr/lib/lua/5.1/ e crie neste mesmo diretório uma ligação (link) simbólica chamada unistd.so:
bash# ln -s libunistd.so.1.0.1 unistd.so


Configurando o sistema


Apesar de Kepler não precisar de variáveis de ambiente, LuaWsgi precisa. Crie as seguintes variáveis:
export LUA_PATH=?.lua;?/?.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/?.lua


Se estiver difícil de visualizar no navegador, trata-se da seguinte sequência, trocando as mudanças de linha por ponto-e-vírgula:
?.lua
?/?.lua
/usr/share/lua/5.1/?.lua
/usr/share/lua/5.1/?/?.lua


A outra variável é:
export LUA_CPATH=?.so;l?.so;/usr/lib/lua/5.1/?.so;/usr/lib/lua/5.1/?/?.so


Idem acima:
?.so
l?.so
/usr/lib/lua/5.1/?.so
/usr/lib/lua/5.1/?/?.so


Aconselho colocar esses comandos em /etc/profile

Agora, para testar, execute o seguinte:
bash$ lua
Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio
> require "unistd"


Se nenhum erro ocorrer, você já está com o ambiente configurado! ;)

Finalmente: instalando LuaWsgi


Essa parte é complicada… =D hehehehehe

Baixe o pacote mais recente da página de arquivos (a versão mais recente no momento em que escrevo é a 7.06.1).

Desempacote o tarball em /usr/share/lua/5.1/:
bash# tar xzvf luawsgi-*.tar.gz -C /usr/share/lua/5.1/


E pronto!

Primeiro exemplo


Crie um arquivo (pode ser em seu homedir) chamado teste.lua:
require "wsgi"
require "wsgi.session"
require "middleware.auth"


local function authhandler(user, pass)
return user == "eu" and pass == "senha"
end


local function app(environ, start_response)
-- Cria ambiente de sessão
local session = wsgi.session(environ, start_response)

-- Se não houver um login, lê de POST
if not session.login then
print "Criando login com dados de POST"
session.login = environ.params.login
end
print("Usuário: " .. session.login)

-- Configura status e cabeçalho (não envia)
start_response { "200 OK";
["Content-Type"] = "text/html; charset=utf-8",
}

-- Encerra retornando o corpo da página
return {
"<html>\n",
"<head>\n",
"<title>Ola ",
(environ.params.login or ""),
"</title>\n",
"</head>\n",
"<body>\n",
'<form action="" method="POST">\n',
'<input type="text" id="login" ',
'name="login" />\n',
'<input type="submit" />\n',
"</body>\n",
"</html>\n",
}
end


-- Este middleware garante autenticação
app = middleware.auth(app, authhandler)
wsgi.serve { app; host="*", port=8001 }


O submódulo wsgi.session permite um acesso fácil a sessão e o submódulo middleware.auth oferece recursos para autenticação.

Salve execute:
bash$ lua teste.lua


E acesse no navegador http://localhost:8001. Veja na função authhandler() que o usuário é «eu» e a senha é «senha».

Descendo o rio… AJAX


Vamos usar AJAX? Para isso temos o submódulo wsgi.ajax. Crie testeDeAjax.lua:
require "wsgi"
require "wsgi.ajax"


local function app(environ, start_response)
-- Cria ambiente AJAX
local ajax = wsgi.ajax(environ, start_response)

ajax.export("som", function (x, y) return x + y end)
ajax.export("sub", function (x, y) return x - y end)
ajax.export("mul", function (x, y) return x * y end)
ajax.export("div", function (x, y) return x / y end)

-- Descomente para depuração
--ajax.setdebug()

-- Responde AJAX
if ajax.handle then
return ajax.handle()
end

-- Configura status e cabeçalho
start_response{ "200 OK";
["Content-Type"] = "text/html; charset=utf-8",
["Cache-Control"] = "no-cache, must-revalidate",
["Pragma"] = "no-cache",
}

-- Retorna o corpo da página (recheado de JavaScript!)
return {[[<html>
<head>
<title>Teste de AJAX</title>
<script language="javascript"><!--
]],
ajax.get_js(),
[[
function set_x(x) {
document.getElementById('x').value = x;
document.getElementById('y').value = '';
}

function som() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_som(x, y, set_x);
document.getElementById('y').focus();
return true;
}

function sub() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_sub(x, y, set_x);
document.getElementById('y').focus();
return true;
}

function mul() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_mul(x, y, set_x);
document.getElementById('y').focus();
return true;
}

function div() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_div(x, y, set_x);
document.getElementById('y').focus();
return true;
}
//--></script>
<style rel="stylesheet"><!--
body {
font-size: 14pt;
text-align: center;
}
--></style>
</head>
<body onLoad="document.getElementById('x').focus(); return true;">
<input type="txt" id="x" value="2" />
?
<input type="txt" id="y" value="3" />
<br />
<input type="button" value="+" onClick="som()" />
<input type="button" value="-" onClick="sub()" />
<input type="button" value="×" onClick="mul()" />
<input type="button" value="÷" onClick="div()" />
</body>
</html>
]]
}
end

wsgi.serve { app; host="*", port=8001 }


Execute:
bash$ lua testeDeAjax.lua


Agora acesse: http://localhost:8001.

Uma brincadeira com JSON


Fala-se muito atualmente em XML atualmente, que é um recurso interessante e até eficiente para o que se propõe.

Mas é um descaramento afirmar que XML seja pau pra toda obra. Quem diz isso ou está iludido ou está querendo enganar alguém.

Por exemplo, para transferência de dados em tempo real, XML é altamente ineficiente.

Para tanto temos JSON.

Além de LuaWsgi trazer integrado JSON4Lua, o submódulo wsgi.ajax possui uma função que adapta o comportamento de uma função para trabalhar com JSON.

Vamos ao exemplo testeDeJson.lua:
require "wsgi"
require "wsgi.ajax"

local function dobro(t)
print(t.val)
return { val = t.val * 2 }
end

local function app(environ, start_response)
local ajax = wsgi.ajax(environ, start_response)

-- Esta é a função JSON-import → jsonport ;)
ajax.jsonport("nada", dobro)

if ajax.handle then
return ajax.handle()
end

start_response{ "200 OK";
["Content-Type"] = "text/html; charset=utf-8",
["Cache-Control"] = "no-cache, must-revalidate",
["Pragma"] = "no-cache",
}

return {[[<html>
<head>
<title>Teste de AJAX</title>
<script
type="text/javascript"
src="http://www.json.org/json.js"
></script>
<script language="javascript"><!--
]],
ajax.get_js(),
[[
function set_x(x) {
var e = x.parseJSON();
document.getElementById("x").value = e.val;
return true;
}

function nada() {
var o = { "val": document.getElementById("x").value };
ajax_nada(o.toJSONString(), set_x);
return true;
}
//--></script>
<style rel="stylesheet"><!--
body {
font-size: 14pt;
text-align: center;
}
--></style>
</head>
<body>
<input type="text" id="x" value=" " />
<input type="button" value="Vai!" onClick="nada()" />
</body>
</html>
]]
}
end

wsgi.serve { app; host="*", port=8001 }


Mesmo esquema!

Conclusão


Ainda há mais o que ver sobre LuaWsgi, como o middleware publicador (middleware.publisher) e mais algumas coisinhas interessantes, mas este artigo já se tornou extenso demais. =(

Estão todos convidados a experimentar LuaWsgi e, mais importante, a participar de seu desenvolvimento!

[]'s
Rodrigo Cacilhas

sábado, 2 de junho de 2007

Re: desafio de criptoanálise

Baal Há pouco mais de um mês postei um desafio de criptoanálise até um tanto malicioso.

O desafio consistia em uma cifra e algumas perguntas a serem respondidas:
  1. A mensagem é na verdade uma pergunta. Qual a pergunta?
  2. Qual a resposta à pergunta?
  3. Qual o algoritmo criptográfico usado?
  4. Qual o erro cometido?


Algumas dicas foram fornecidas nos comentários:
  • A chave simétrica está escondida no próprio criptograma.
  • É preciso trabalhar com os códigos ASCII dos bytes.
  • É preciso efetuar uma adição com esses códigos.
  • O resultado é binário, e você vai descobrir se deu certo quando o comando file retornar algo diferente de data.


Agora vou responder às perguntas 3 e 4 e deixar o resto com vocês!

Qual o algoritmo criptográfico usado?


O algoritmo criptográfico usado foi Vigenère-Vernam, também conhecido como «one time pad».

Este algoritmo é uma variação da Cifra de Vigenère onde a chave pseudo-aleatória tem o mesmo tamanho da mensagem. Isto torna a cifra matematicamente inquebrável.

Para cifrar a mensagem a chave foi subtraída da mensagem byte a byte, sempre somando 256 quando o resultado era negativo. Portanto, tendo a chave fica simples criar um código que extraia a mensagem.

Qual o erro cometido?


No entanto, como a entropia é muito alta, é impossível (de verdade) identificar a chave.

Mas foi cometido um erro intencional! E já contei qual foi…

Olhem lá nas dicas… num dos comentários eu disse:
É… pelo visto está difícil.

Vou dar mais uma dica: a chave é simétrica e está escondida no próprio criptograma.

[]'s


Eu simplesmente concatenei a chave no final da cifra!

Portanto a primeira metade do arquivo é a cifra em si, a segunda metade é a chave. =P


Que tal quebrar a cifra agora?

Então continuam as duas primeiras perguntas e adiciono mais uma:
— Qual o formato da mensagem (file pode ajudar)?

Vamos lá! A brincadeira ficou mais fácil! Postem seus códigos nos comentários! =D

[]'s
Rodrigo Cacilhas

sexta-feira, 1 de junho de 2007

SQLAlchemy no TurboGears

Depois de tempos postando sobre política posso dizer que estou voltando a ativa novamente. Neste post trataremos de um assunto que nunca mais havia tocado: TurboGears.

A minha framework web favorita escrita em Python está na versão 1.0.2.2 e correndo para a versão 1.1, principalmente depois de um sprint que ocorreu recentemente. Os desenvolvedores substituirão o sistema de templates Kid pelo Genshi e o SQLObject pelo SQLAlchemy. O que posso dizer é que podemos usar o SQLAlchemy atualmente sem muitos problemas. Constatei isso implementando um exemplo bem conhecido em Ruby on Rails: um livro de receitas eletrônico (cookbook em inglês).

Vamos mostrar algum código, uma vez que isso é mais divertido. Precisamos do TurboGears e do SQLAlchemy:
easy_install -U TurboGears
easy_install SQLAlchemy

Antes de criarmos uma infraestrutura para o projeto, gostaria que vocês conseguissem o servidor de banco de dados MySQL. Vamos fazer o nosso exemplo de acordo com o do Rails. Se vocês quisserem fazer o mesmo exemplo em Rails sugiro lerem alguns posts do blog do Brasília on Rails. Vamos criar nosso banco de dados com o seguinte comando num terminal bash do Linux:
$ mysql -u root -p
> create database cookbook;
> grant all on cookbook.* to 'usuario@localhost';

O esquema do banco é o seguinte:
drop table if exists recipes;
drop table if exists categories;
create table categories ( id int not null auto_increment,
name varchar(100) not null default '',
primary key(id)
) engine=InnoDB;

create table recipes ( id int not null auto_increment,
category_id int not null,
title varchar(100) not null default '',
description varchar(255) null,
date date null,
instructions text null,
constraint fk_recipes_categories foreign key (category_id) references categories(id),
primary key(id)
) engine=InnoDB;

Colem este esquema num arquivo (digamos, create.sql) e executem:
mysql cookbook <dbcreate.sql

Feito isso, vamos ao TurboGears:

tg-admin quickstart --sqlalchemy

Chamarei o projeto de cookbook. Selecionem 'não' quando perguntado se querem usar o Identity.

Dentro da pasta do projeto há o arquivo dev.cfg para as configurações no ambiente de desenvolvimento. Lá informaremos à aplicação a string de conexão:

sqlalchemy.dburi="mysql://root:oinc@localhost/cookbook"
Executemos o script start-cookbook.py para rodar o servidor. Depois acesse o endereço http://localhost:8080/ para testar a aplicação.

python start-cookbook.py

Depois disso, vamos no arquivo cookbook/model.py para mapear para objetos o nosso banco com SQLALchemy:
from sqlalchemy import *
from turbogears.database import metadata, session, bind_meta_data
#from sqlalchemy.ext.assignmapper import assign_mapper

bind_meta_data()

categoria_tabela = Table('categories',metadata,autoload=True)

receita_tabela = Table('recipes',metadata,autoload=True)

class Categoria(object):
def __init__(self,name):
self.name = name
def __repr__(self):
return self.name

class Receita(object):
def __init__(self,title):
self.title = title
def __repr__(self):
return self.title
def nome_categoria(self):
return self.categoria.name

mapper(Categoria, categoria_tabela, properties={'receitas' : relation(Receita,backref='categoria')})
mapper(Receita, receita_tabela)

Para o post não ficar muito grande, faremos somente uma página que lista todas as receitas. Eu não cheguei a implementar todo o exemplo do Cookbook, no entanto é muito mais do que mostrarei para vocês daqui pra frente.

Copiem o template welcome.kid e criem o arquivo receita_list.kid. Retirem o corpo da página e coloquem somente o seguinte:
${grid.display(receitas)}

Editem o arquivo cookbook/controllers.py. Escreveremos um método que acesse o banco de dados para pegar todas as receitas e passe essa lista para o template que criamos.

@expose(template="cookbook.templates.receita_list")
def receitas(self):
receitas = session.query(Receita).select()
grid = DataGrid(fields=[('Nome','title'), ('Categoria',Receita.nome_categoria)])
return dict(receitas = receitas, grid = grid)

Para que esse método funcione, temos de importar model.py e widgets.DataGrid.

from model import *
from turbogears.widgets import DataGrid

A primeira linha do método utiliza o objeto session para fazer uma consulta no banco na tabela recipes (mapeada pela classe Receita). Na segunda linha, instanciamos o DataGrid que será usado para gerar a tabela de resultados. Por fim, a última declara os objetos que serão usados em nosso template.

Acessemos a URL http://localhost:8080/receitas. Espero que tenha funcionado! ;) Problemas e dúvidas, por favor, comentem.