quarta-feira, 26 de março de 2014

Shell-script para web

Poucas pessoas sabem que é possivel usar shell-script em servidores web , atraves de cgi ,
esse texto é uma pequena introdução , com alguns exemplos didaticos =).
Nesse tutorial eu estarei usando o servidor apache , com todas as suas configurações padrões.


Starting

Nas configurações padrões do apache , existe um diretorio ja predefinido para scripts cgi rodarem
é o /usr/lib/cgi-bin/ , irei criar um arquivo com o nome tutorial.sh. Nos scripts cgi , a saida dos
scripts (/dev/stdout e /dev/stderr) , são enviadas para o cliente , as primeiras linhas do script
serão o cabeçalho de resposta da requisição http , desse modo podemos forçar o download de arquivos ,
fazer redirecionamentos , etc ... , a quebra de linha é que separa o que sera o cabeçalho e o que sera
o resto da resposta.

Voltando ao arquivo tutorial.sh , dentro do arquivo , coloque as seguintes linhas:
#!/bin/bash
echo -e "Content-Type: text/plain\n" # -> E o Cabeçalho , e a quebra de linha
echo "Hello World" # -> Agora e o resto do request, o conteudo que é exibido pelos navegadores xD
Salve o arquivo , e deixe as permissões como 775 (lembrando: r = 4 , w = 2 , x = 1).
Acessem o link: localhost/cgi-bin/tutorial.sh , e irão ver a mensagem "Hello World"

Por padrão o apache ja configura o cabeçalho de resposta , se não quiser você pode por no começo
do script , echo -e "\n" ou printf "\n\n" , e deixar que o apache configure o cabeçalho , que normalmente sera algo parecido com isso:
HTTP/1.1 200 OK
Date: [...]
Server: Apache [...]
Content-Type: text/x-sh
Variáveis de ambiente

Em shell-script podemos ver quais são as variaveis de ambientes , usando o comando env , se voce digitar esse comando em um terminal , serão listadas diversas variaveis como SHELL,HOME,USERNAME, etc & etc...
Para listarmos as variaveis de ambiente no apache , basta incluir no script o comando env , ficando da seguinte forma:
#!/bin/bash
echo -e "Content-Type: text/plain\n"
env
Salvei esse arquivo e acessei a pagina web usando o comando curl , o resultado foi:
$ curl -i localhost/cgi-bin/tutorial.sh
HTTP/1.1 200 OK
Date: Tue, 25 Mar 2014 00:24:04 GMT
Server: Apache/2.2.22 (Debian)
Vary: Accept-Encoding
Transfer-Encoding: chunked
Content-Type: text/plain

SERVER_SIGNATURE=<address>Apache/2.2.22 (Debian) Server at localhost Port 80</address>

HTTP_USER_AGENT=curl/7.26.0
SERVER_PORT=80
HTTP_HOST=localhost
DOCUMENT_ROOT=/var/www
SCRIPT_FILENAME=/usr/lib/cgi-bin/tutorial.sh
REQUEST_URI=/cgi-bin/tutorial.sh
SCRIPT_NAME=/cgi-bin/tutorial.sh
REMOTE_PORT=46944
PATH=/usr/local/bin:/usr/bin:/bin
PWD=/usr/lib/cgi-bin
SERVER_ADMIN=webmaster@localhost
HTTP_ACCEPT=*/*
REMOTE_ADDR=127.0.0.1
SHLVL=1
SERVER_NAME=localhost
SERVER_SOFTWARE=Apache/2.2.22 (Debian)
QUERY_STRING=
SERVER_ADDR=127.0.0.1
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
REQUEST_METHOD=GET
_=/usr/bin/env
Entendendo algumas variaveis:

HTTP_USER_AGENT => User-agent que o cliente esta usando.
Pode ser usada para pegar informações sobre quem visita a sua pagina (navegador & sistema operacional)

REQUEST_METHOD => Metodo de requisão utilizado , exe: GET,PUT,POST,OPTIONS,HEAD
Pode ser bem util, pelo menos para mim ja foi xD.

QUERY_STRING => Dados enviados via get , exe: localhost/cgi-bin/tutorial.sh?a=f&b=f (a => f , b => f)

REMOTE_ADDR => I.P do cliente.

Exemplo de utilização de variaveis de ambiente:
#!/bin/bash

cat <<A
Content-Type: text/plain

-----------------------------------
Seu User-agent => $HTTP_USER_AGENT
Seu I.P => $REMOTE_ADDR
-----------------------------------
A
Testando:
$ curl localhost/cgi-bin/tutorial.sh
-----------------------------------
Seu User-agent => curl/7.26.0
Seu I.P => 127.0.0.1
-----------------------------------
Pegando dados enviados via post e get

Quando trabalhamos com desenvolvimento web , é comum receber dados via post e/ou get.
Irei começar com o get , para post é praticamente a mesma coisa , so muda mesmo quando se faz upload de arquivos.

Em bash script , a variavel IFS funciona como uma função split , colocamos o que sera delimitado , a variavel seguinte
se transforma em um vetor. Depois usando o laço for , podemos extrair os campos e valores respectivos enviados via get:
#!/bin/bash
echo -e "\n"
IFS='=&'
GET=($QUERY_STRING)


for ((i=0; i<${#GET[@]}; i+=2))
do
        echo "${GET[@]:$i:1} = ${GET[@]:$i+1:1}"
done
Testando:
$ curl 'localhost/cgi-bin/tutorial.sh?teste=ffff&teste2=xxxxx'

teste = ffff
teste2 = xxxxx
Com POST a unica diferença é que não existe uma variavel de ambiente com os valores , para obter esses valores basta o seguinte comando:
read variavel
Os dados enviados via post ficam em /dev/stdin , por isso quando utilizamos o read esses valores são obtidos. Feito isso é so trocar o GET por POST:
#!/bin/bash
echo -e "\n"
read var
IFS='=&'
POST=($var)

for ((i=0; i<${#POST[@]}; i+=2))
do
        echo "${POST[@]:$i:1} = ${POST[@]:$i+1:1}"
done
Testando:
$ curl localhost/cgi-bin/tutorial.sh --data "autor=mmxm&blog=hc0der.blogspot.com"

autor = mmxm
blog = hc0der.blogspot.com
Exemplos finais:

Alguns exemplos que podem ser uteis.

Redirecionamento usando header:
#!/bin/bash

printf "Status: 302 Found\n"
printf "Location: http://hc0der.blogspot.com\n\n";
Testando:
$ curl localhost/cgi-bin/tutorial.sh -i
HTTP/1.1 302 Found
Date: Wed, 26 Mar 2014 00:01:08 GMT
Server: Apache/2.2.22 (Debian)
Location: http://hc0der.blogspot.com
Content-Length: 0
Content-Type: text/x-sh
Falso 404 not found:
#!/bin/bash

printf "Status: 404 Not Found\n\n"
Testando:
$ curl localhost/cgi-bin/tutorial.sh -i
HTTP/1.1 404 Not Found
Date: Wed, 26 Mar 2014 00:06:53 GMT
Server: Apache/2.2.22 (Debian)
Content-Length: 0
Content-Type: text/x-sh
Fazendo download de arquivo:
#!/bin/bash

arquivo_local=/etc/issue
stat -c "Content-Length: %s" $arquivo_local # enviar tamanho do arquivo em bytes
printf "Content-Disposition: attachment; filename=$(basename $arquivo_local)\n"
printf "Content-Type: application/octet-stream\n\n"
cat $arquivo_local
Testando:
curl localhost/cgi-bin/tutorial.sh -i
HTTP/1.1 200 OK
Date: Wed, 26 Mar 2014 00:13:32 GMT
Server: Apache/2.2.22 (Debian)
Content-Disposition: attachment; filename=issue
Content-Length: 17
Vary: Accept-Encoding
Content-Type: application/octet-stream

nao te interessa
E via browser:



Fim>

Para uma introdução esta de bom tamanho , bash-script é foda, conhecendo bem
as ferramentas do seu sistema não há limites para o que você pode fazer =>.
Referencias de pesquisas aqui:

w3.org
wikipedia.org
yolinux.com
stackoverflow.com
stackoverflow.com

That's all folks

Um comentário: