terça-feira, 26 de janeiro de 2010

Woof em Chicken

Há uns dias li sobre o woof, um programa para compartilhar arquivos via HTTP. A descrição do site é mais detalhada:


I guess everybody with a laptop has experienced this problem at some
point: You plug into a network and just want to exchange files with
other participants. It always is a pain until you can exchange
files with the person vis-a-vis.

Of course there are a lot of tools to tackle this problem. For large
scale communities there are dozens of filesharing networks. However,
they don't work for small local networks. Of course you could put your
stuff to exchange on a local web server, but who really wants to
maintain this? Tools like the ingenious
npush/npoll are
extremely helpful, provided that both parties have it installed,

SAFT/sendfile
also aims to solve this problem, but needs a permanently running daemon...

Woof (Web Offer One File) tries a different approach. It
assumes that everybody has a web-browser or a commandline web-client
installed. Woof is a small simple stupid webserver that can
easily be invoked on a single file. Your partner can access the file
with tools he trusts (e.g. wget). No need to enter
passwords on keyboards where you don't know about keyboard sniffers, no
need to start a huge lot of infrastructure, just do a

$ woof filename

and tell the recipient the URL woof spits out. When he got that
file, woof will quit and everything is done.


Abaixo está uma implementação em setenta e poucas linhas de código em Chicken Scheme, usando o servidor web Spiffy.

#! /usr/bin/csi -s
(use posix srfi-13 spiffy)

(define (woof file count ip-addr port)
(when port (server-port port))
(root-path (or (pathname-directory file) (current-directory)))
(let ((current-count 0)
(ip-addresses (and ip-addr (string-split ip-addr ",")))
(file (pathname-strip-directory file)))
(handle-file
(lambda (path)
(if (equal? (pathname-strip-directory path) file)
(if (or (not ip-addr) (member (remote-address) ip-addresses))
(let* ((ext (pathname-extension path))
(handler (alist-ref ext (file-extension-handlers)
string-ci=? send-static-file)))
(set! current-count (add1 current-count))
(print "Serving file " file
" to " (remote-address)
" (count=" current-count ").")
(handler path)
(when (>= current-count count)
(exit 0)))
(begin

(print "Denying access to host " (remote-address) ".")
(send-status 403 "Forbidden")))
(begin
(print "Denying access to file " path ".")
(send-status 403 "Forbidden")))))
(print "Sharing '" file "' with "

(if ip-addresses
(string-intersperse ip-addresses ", ")
"anybody")
".")
(print "URL: http://" (get-host-name) ":" port "/" file)
(start-server)))

(define (usage #!optional exit-code)
(display
(string-append
(pathname-strip-directory (program-name))
" [ --port=<port> ] [ --ip-addr=<ip address> ] [ --count=<count> ] file\n")
(if exit-code (current-error-port) (current-output-port)))
(when exit-code (exit exit-code)))

(define (cl-option-value option args #!optional (converter identity))
(let loop ((args args))
(and (not (null? args))
(let ((arg (car args)))
(if (string-prefix? option arg)
(cond ((string-match (string-append option "=(.*)") arg)
=> (lambda (m)
(converter (cadr m))))
(else (loop (cdr args))))
(loop (cdr args)))))))

(let ((args (command-line-arguments)))
(when (null? args)
(usage 1))
(when (or (member "-h" args) (member "--help" args))
(usage 0))
(let ((count (cl-option-value "--count" args string->number))
(ip-addr (cl-option-value "--ip-addr" args))
(port (cl-option-value "--port" args string->number))
(file (car (reverse args))))
(if (file-exists? file)
(woof file (or count 1) ip-addr port)
(begin

(display (string-append file " does not exist.\n")
(current-error-port))
(exit 1)))))


Nem todas as funcionalidades do woof original estão presentes, mas é possível especificar:

  • o arquivo a ser compartilhado

  • a porta a ser usada pelo servidor

  • o número IP da máquina que poderá pegar o arquivo (pode-se liberar o acesso para mais de uma máquina separando os números IP por vírgula)

  • o número de vezes que o arquivo pode ser acessado



Abaixo está a sintaxe para utilização do woof em Chicken na linha de comando:
$ woof --help                                  
woof [ --port=<port> ] [ --ip-addr=<ip address> ] [ --count=<count> ] file

sábado, 23 de janeiro de 2010

jsmin para Chicken Scheme

Há pouco fiz o commit no repositório de extensões do sistema Chicken de uma tradução para Scheme do código C do jsmin, de Douglas Crockford.

A extensão fornece apenas dois procedimentos: jsmin-string e jsmin-file, as quais compactam o código javascript considerando como entrada, respectivamente, uma string e um arquivo.

O código Scheme traduzido de código C (sem uso da FFI) não é dos mais bonitos :-), pois a tradução foi feita praticamente linha-a-linha (inclusive mantendo os returns, implementados através de call/cc).

Programação para a Web com Awful

Há uns dias comecei a desenvolver mais uma tentativa de tornar a programação para a Web mais "fácil". O resultado é uma extensão para o sistema Chicken (versão 4) que fornece as seguintes funcionalidades:

  • Interfaces simples para o uso de bases de dados Postgresql

  • Suporte a Ajax através da biblioteca JQuery

  • Acesso fácil às variáveis de requisições HTTP (tanto da query string (método GET) quando do corpo da requisição (e.g., método POST)

  • Flexibilidade: há vários parâmetros de configuração

  • A compilação de páginas para código nativo para a plataforma alvo é fácil

  • Disponibilidade de um visualizador de sessão

  • Disponibilidade de um REPL que pode ser acessado através do navegador (via HTTP) para a depuração de aplicações para a web



Tentei fazer uma documentação razoável, que cobrisse boa parte da implementação. Ela está em http://chicken.wiki.br/eggref/4/awful. Na documentação podem ser vistos exemplos de aplicações simples que demonstram algumas das funcionalidades.

Sobre o nome: é só um nome. :-) Em http://chicken.wiki.br/eggref/4/awful#the-name há algumas possíveis interpretações caso AWFUL fosse uma sigla.