2.2. Envio de mensagens para múltiplos destinatários
2.3. Verificar o funcionamento de servidores
2.4. Teste de funcionamento dos serviços
2.6. Extração de endereços de email de arquivos de log
2.7. Configuração automática do firewall
Hoje veremos diversos exemplos do uso de estruturas de laço e decisão em Shell Bash. Estes exemplos foram recolhidos de situações reais com as quais lidamos ao longo dos anos. Você poderá ver que a síntese dos comandos é praticamente a mesma, e verá também como é facílimo replicar estas estruturas para resolver os seus problemas.
Então, vamos lá!
A CBN costumava publicar as gravações de seus programas na web, em um formato que obedecia a uma sintaxe, com o seguinte formato:
coluna_AAMMDD.mp3 max_041102.mp3
Por exemplo, os podcasts do Max Gehringer tinham o formato max_041102.mp3
.
Para baixar todos os podcasts do Max Gehringer, desde o começo de sua coluna na CBN, eu teria que fazer um script que fosse alterando o valor do ano, do mes e do dia. Para isto, posso criar três laços, um dentro do outro:
Supondo que a coluna do Max Gehringer tenha começado em 2004, para baixar todos os podcasts até o ano de 2017, podemos fazer da seguinte forma:
#!/bin/bash for A in {04..17} do for M in {01..12} do for D in {01..31} do wget http://www.cbn.com.br/podcasts/max_$A$M$D.mp3 sleep 10 done done done
O laço mais externo, que gera o ano, roda mais devagar, ou seja, para
cada valor da variável $A
, a variável $M
varia de 1 a 12, e para
cada valor de $M
a variável $D
varia de 1 a 31. São três relógios,
o mais externo (o ano) roda mais devagar, o do meio (o mês) roda um pouco
mais rápido e o mais interno (o dia) roda mais rápido do que todos os outros.
Infelizmente a CBN mudou o esquema de armazenamento de seus podcasts e este script não funciona mais, entretanto, continua sendo um bom exemplo de laços aninhados.
Para a CBN não pensar que está sofrendo um ataque de negação de serviço,
podemos fazer com que o sistema execute uma pausa ao final de cada
download. Para isto inserimos o comando sleep 10
para fazer com que
entre um download e outro o sistema faça uma pausa de 10 segundos.
É claro que nem todos os arquivos que eu solicitar existirão, mas neste caso ocorrerá apenas uma mensagem de erro sobre a qual não preciso tomar nenhuma atitude. Este script também não trata meses com duração diferente de 31 dias, que também julguei não ser preciso. Todavia, existem situações em que um tratamento completo precisa ser inserido na Shell.
Como não podia deixar de ser, também dá para fazer tudo em uma linha:
echo {15..17}{01..12}{01..31} | \ tr ' ' '\n' | \ xargs -i wget http://www.cbn.com.br/podcasts/max_{}.mp3
Faça um teste e veja como funciona bem. As mágicas do xargs
serão
explicadas em detalhe no curso de programação shell Linux que será oferecido
em novembro.
O envio de mensagens de email para múltiplos destinatários pode ser automatizado por meio de um programa em Shell muito simples.
O primeiro passo é montar o cabeçalho da mensagem, que deve conter, no
mínimo, o nome do destinatário (To:
), o nome do remetente (From:
)
e o assunto da mensagem (Subject:
). Como exemplo, vamos utilizar uma
versão resumida da primeira mensagem da Dicas-L, enviada no dia 3 de março
de 1997 (já faz um tempinho, né?)
while read nome email do /usr/sbin/sendmail $email << EOF From: Rubens Queiroz de Almeida <queiroz@dicas-l.com.br> To: user@gmail.com Subject: Receitas de uso do comando find Colaboração: Rubens Queiroz de Almeida Data de Publicação: 03 de Março de 1997 O comando find é extremamente poderoso e flexível para descobrir arquivos que atendem a determinadas especificações. Por exemplo, suponhamos que queiramos descobrir todos os arquivos que não possuem dono em nosso sistema. Esta situação é extremamente comum, visto que usuários são criados e apagados diariamente e ficam vagando pelo sistema e podem eventualmente vir a comprometer a segurança. O comando find / -nouser -print —----------------------------------------------------------------------- As mensagens da Dicas-L são enviadas diariamente para `wc -l lista.txt` assinantes. EOF done < lista.txt
Esta Shell usa um recurso muito útil, o Here Document. Com este
recurso nós podemos fornecer ao programa um trecho de texto, que serão
os dados entregues ao programa especificado, em nosso caso, o programa
sendmail
. O programa sendmail
irá interpretar como entrada todos os
caracteres que encontrar até que os caracteres EOF
sejam encontrados na
primeira posição da linha.
Observe também a seguinte frase:
As mensagens da Dicas-L são enviadas diariamente para `wc -l lista.txt` assinantes.
Uma prática que tenho seguido há vários anos, é sempre inserir no rodapé
das mensagens o número de assinantes da minha lista. No texto, este valor é
inserido como um resultado do comando wc
(word count), com a diretiva
-l
, que me fornece o número de linhas do arquivo que contém a lista
dos assinantes.
Note que o comando é delimitado por aspas invertidas (que costumam chamar
de crase, apesar de ser um acento grave e não uma crase), o que faz com
que no texto seja inserido o resultado do comando wc -l
, pois as crases,
servem para dar prioridade de execução, por exemplo:
Arqs=ls
Dessa forma eu estaria atribuindo o literal ls
à variável $Arqs
. Se eu quiser
atribuir a saída do comando ls
, eu deveria fazer:
Arqs=`ls`
Como em qualquer interpretador as operações são feitas de cima para baixo e da
esquerda para a direita, precisamos dizer ao Shell para ele priorizar o comando
ls
e isso se consegue de duas maneiras:
Eu poderia fazer algo semelhante com qualquer comando disponível no sistema. Nesta mesma linha, supondo que este seja um e-mail que eu mande diariamente, eu poderia substituir:
Data de Publicação: 03 de Março de 1997
Por:
Data de Publicação: $(date "+%d de %B de %Y")
No comando date
, se houver um sinal de adição (+
), tudo que for
precedido por um porcento (%
) é considerado um caractere de formatação
de tempo, senão, será considerado um literal e:
%d | Dia (01-31) |
%B | Mês por extenso |
%Y | Ano com século (4 algarismos) |
Uma coisa muito pouco conhecida e que é provável que até você desconheça: 90%
das vezes que usamos Here Document, prejudicamos a indentação porque o
label utilizado (em nosso exemplo usei EOF) não pode ter espaços antes
nem depois, mas o que é segredo de estado é que pode ter <TAB>
s antes,
desde que você use um hifen após os sinais de maior (<<-
). Experimente fazer:
if .... then .... cat <<- Fim No dia $(date) meu diretório tinha: $(ls -l) Fim fi
IMPORTANTE: Antes da palavra Fim
tem um <TAB>
.
Para verificar se todos os servidores sob a sua supervisão estão funcionando corretamente, podemos montar um pequeno script que, a partir de uma lista de máquinas, realize um teste de tempos em tempos:
Antes de escrever o script, precisamos conhecer os códigos gerados pelo
comando ping
em cada situação que encontrar:
Status | Código |
---|---|
Sucesso | 0 |
Sem resposta | 1 |
Outros erros | 2 |
Se o código for zero, está tudo bem, não precisamos nos preocupar, se for diferente de zero (1 ou 2), o administrador deverá ser alertado.
O nosso script deverá então verificar estes códigos. Mas como fazer isso? O comando
if
do Shell é bem mais poderoso que os seus congêneres de outras linguagens,
que testam somente condições (e somente 7 condições, verifique). Aqui esta
instrução testa se um comando, no caso o ping
, foi bem sucedido (código de
retorno zero), quando executará o bloco do then
. Caso contrário (código de
retorno diferente de zero) o bloco do else
será executado.
Os códigos de retorno que citamos são apresentados pela variável $?
e existem, com valores diferentes, para todos os comandos, mas, por
convenção, sempre que um código de retorno ($?
) é zero, significa que
o comando correspondente foi bem sucedido.
Só para despreocupar vocês, já adianto que o Shell tem uma instrução específica
para testar condições, que é o test
, que testa mais de 30 condições distintas,
inclusive em nível de arquivos.
Vamos então ao script:
$ cat check_server.sh
#!/bin/bash
# check_server.sh v1.0
while read servidor
do
echo "Testando $servidor"
if ping -q -c5 $servidor
then
echo $servidor OK
else
echo $servidor não está respondendo
/usr/lib/sendmail queiroz@dicas-l.com.br << EOF
Subject: **URGENTE**: Servidor $servidor não está respondendo
From: System Admin <admin@dicas-l.com.br>
O servidor $servidor não está respondendo. Por favor, verifique o que
está ocorrendo o mais rápido possível.
Atenciosamente,
System Administrator
EOF
fi
done < lista_de_servidores.txt
Note que neste script inserimos uma estrutura de controle de laço
(while
) e uma estrutura de controle de decisão (if
). O laço while
faz a leitura de todas as linhas do arquivo lista_de_servidores.txt
e envia um ping
para cada um dos servidores contidos na lista. Já o if
testa a execução do comando ping
caso ele tenha sido bem sucedido e, de acordo
com a resposta que obtiver, toma as providências necessárias para cada situação.
No comando ping
eu pedi que fossem enviados cinco pacotes para o servidor sendo
testado. Se eu enviasse apenas um pacote, eu poderia criar um alarme falso,
pois apenas um pacote de teste é muito pouco. Eu indiquei também que quero
que o comando ping
se comporte de maneira mais silenciosa (diretiva -q
).
O segredo reside no if
testando o retorno do ping
, o que me indica se
o comando foi bem sucedido ou não. Caso ele tenha se dado mal, quando seu
código de retorno ($?
) será diferente de zero, o bloco de comandos do else
será executado e o administrador do sistema deve ser alertado para tomar
as devidas providências. Neste caso, além da mensagem de erro ecoada para
a tela, é enviada uma mensagem para o administrador da máquina. O arquivo
lista_de_servidores.txt
contém a relação de todos os servidores a serem
testados.
Importante, veja que na mensagem enviada ao administrador foi incluída
a variável $servidor
, obtida a partir da leitura do arquivo
lista_de_servidores.txt
. As mensagens configuradas a partir dos
Here Documents podem ser configuradas de maneira bastante completa,
inserindo substituição de variáveis e resultados de comandos, como já vimos.
Outra possibilidade muito interessante, que pode ser facilmente implementada com Shell scripts, é verificar se todos os serviços (DNS, Apache, MySQL, etc) estão funcionando corretamente.
O princípio é o mesmo, insiro em um arquivo os nomes de todos os serviços
que desejo monitorar e por meio do laço while
e da estrutura de decisão
if
, faço uma busca na lista de processos pelo nome do serviço. Se eu
não encontrar o serviço, é sinal de problema.
$ cat check_services.sh
#!/bin/bash
# check_services.sh v1.0
Servidor=www.dicas-l.com.br
while read Servico
do
if ps -ef | grep $Servico | grep -v grep 1>&2 > /dev/null
then
echo $Servico OK
else
echo $Servico está fora do ar
/usr/lib/sendmail queiroz@dicas-l.com.br << EOF
Subject: **URGENTE**: O servico $Servico está fora do ar
From: System Admin <admin@dicas-l.com.br>
O serviço $Servico do $servidor não está
respondendo. Por favor, verifique o que
está ocorrendo o mais rápido possível.
Atenciosamente,
System Administrator
EOF
fi
done < lista_de_servicos.txt
Importante, note que na linha em que usamos o grep
, nós removemos da listagem
o próprio comando grep
, pois este grep
irá constar da lista de processos
justamente com o nome do serviço que estamos pesquisando.
$ ps -ef | grep named
bind 19501 1 0 22:35 ? 00:00:00 /usr/sbin/named -f -u bind
queiroz 19839 16913 0 22:42 pts/0 00:00:00 grep named
Caso queiramos monitorar os serviços Apache, MySQL, DNS, postfix e ssh,
inserimos no arquivo lista_de_servicos.txt
as seguintes linhas:
apache2 mysqld named postfix sshd
Este valores podem variar de um sistema para outro, então, por meio do comando
ps -ef
, veja qual a denominação correta para estes serviços em seu sistema.
Ao executarmos o comando check_services.sh
será ecoado para a tela o
resultado da verificação:
$ ./check_services.sh
apache2 OK
mysqld OK
named está fora do ar
postfix está fora do ar
sshd OK
Para todos os serviços que não estiverem operantes, será enviado um email de alerta para o administrador.
A diretiva case
não pode ser considerada um laço, visto que não executa
repetidamente um bloco de código. Porém, como os laços, ele direcionam o fluxo
do programa de acordo com as condições verificadas no início ou final do bloco.
A sintaxe da diretiva case
é a seguinte:
case $variable in padrão-1) comandos ;; padrão-2) comandos ;; padrão-3|padrão-4|padrão-5) comandos ;; padrão-N) comandos ;; *) comandos ;; esac
Como não poderia deixar de ser, aqui vai mais um exemplo. No portal Dicas-L com
bastante frequencia eu preciso editar um artigo para corrigir algum erro de
conteúdo ou simplesmente para ajeitar a formatação. A seguir, apresento
um trecho deste script, apenas para demonstrar o uso da estrutura case
.
$ cat mktip.sh
#!/bin/bash
# O shell script recebe como argumento o nome
# do arquivo que se quer editar. Esta condição
# é verificada no início do script.
if [ $# -ne 1 ];
then
echo Uso: ${0#*/} dica # Apaga tudo à esquerda da barra (/)
exit 1
fi
# A resposta fornecida é armazenada na
# variável $ans
# Repare que o S está sendo oferecido como default
read -n1 -p "Gostaria de editar esta dica? (S|n) : " ans
ans=${ans:-S} # Caso vazia (aceitou default), $ans recebe S
[ ${ans^} == S ] && vi $Dica.t2t
GeraDica
Se a resposta para a pergunta Gostaria de editar esta dica? (S|N) :
for sim (s
ou S
), o arquivo será aberto no editor de textos
vi
e em seguida será invocada uma função chamada GeraDica
(não
mostrada aqui) que irá gerar o arquivo html. Se a resposta for não (n
,
N
, ou qualquer outra resposta), será invocada apenas a função GeraDica
.
Mais um exemplo:
$ cat tecla.sh
#!/bin/bash
# Identificando o caractere digitado
read -n1 -p "Pressione uma tecla: " tecla
case "$tecla" in
[[:lower:]] ) echo -e "\nLetra minúscula";;
[[:upper:]] ) echo -e "\nLetra maiúscula";;
[0-9] ) echo -e "\nNúmero";;
* ) echo -e "\nPontuação, espaço em branco ou outros";;
esac
Neste exemplo, o script executa uma única vez e encerra. Para fazer com
que ele seja encerrado apenas quando eu digitar <CTRL>+C, posso colocar o
código do case
dentro de um laço while
:
$ cat tecla.sh
#!/bin/bash
while true
do
# Identificando o caractere digitado
echo; echo "Pressione uma tecla e em seguida pressione enter"
read tecla
case "$tecla" in
[[:lower:]] ) echo -e "\nLetra minúscula";;
[[:upper:]] ) echo -e "\nLetra maiúscula";;
[0-9] ) echo -e "\nNúmero";;
* ) echo -e "\nPontuação, espaço em branco ou outros";;
esac
done
Referência: Advanced Bash-Scripting Guide.
O Bash 4.0 introduziu duas novas facilidades no comando case
. A partir dessa
versão, existem mais dois terminadores de bloco além do ;;
, que são:
;;&
- Quando um bloco de comandos for encerrado com este terminador,
o programa não sairá do case
, mas testará os próximos padrões.
;&
- Neste caso, o próximo bloco será executado sem sequer testar o seu padrão.
Exemplos
Suponha que no seu programa possam ocorrer quatro tipos de erro e você resolva representar cada um deles por uma potência de 2, isto é, 20=1, 21=2, 22=4, 23=8, de forma que a soma dos erros ocorridos gere um número único para representá-los (é assim que se formam os números binários). Assim, se ocorrem erros dos tipos 1 e 4, será passado 5 (4+1) para o programa e se os erros forem 1, 4 e 8, será passado 13 (8+4+1). Observe a tabela a seguir:
Soma | Erros | |||
---|---|---|---|---|
8 | 4 | 2 | 1 | |
8 | x | - | - | - |
7 | - | x | x | x |
6 | - | x | x | - |
5 | - | x | - | x |
4 | - | x | - | - |
3 | - | - | x | x |
2 | - | - | x | - |
1 | - | - | - | x |
0 | - | - | - | - |
$ cat case.sh
#!/bin/bash
# Recebe um código formado pela soma de 4 tipos
#+ de erro e dá as msgs correspondentes. Assim,
#+ se houveram erros tipo 4 e 2, o script receberá 6
#+ Se os erros foram 1 e 2, será passado 3. Enfim
#+ os códigos de erro seguem uma formação binária.
Bin=$(bc <<< "obase=2; $1") Passa para binário
Zeros=0000
Len=${#Bin} Pega tamanho de $Bin
Bin=${Zeros:$Len}$Bin Preenche com zeros à esquerda
# Poderíamos fazer o mesmo que foi feito acima
#+ com um cmd printf, como veremos no capítulo 6
case $Bin in
1[01][01][01]) echo Erro tipo 8;;&
[01]1[01][01]) echo Erro tipo 4;;&
[01][01]1[01]) echo Erro tipo 2;;&
[01][01][01]1) echo Erro tipo 1;;&
0000) echo Não há erro;;&
*) echo Binário final: $Bin
esac
Repare que todas as opções serão testadas para saber quais são bits ligados (zero=desligado, um=ligado). No final aparece o binário gerado para que você possa comparar com o resultado. Testando:
$ case.sh 5 Erro tipo 4 Erro tipo 1 Binário final: 0101 $ case.sh 13 Erro tipo 8 Erro tipo 4 Erro tipo 1 Binário gerado: 1101
Obs.: Todas as listas [01]
neste exemplo poderiam ser substituídas por um
ponto de interrogação (?
), já que ele é um metacaractere que representa
qualquer caractere e o nosso programa só gerou zeros ou uns para cada uma
dessas posições.
Alguns anos atrás a Dicas-L foi expulsa de um provedor nos EUA sob a alegação de que eu estava fazendo spam. Segundo o provedor, eles tinham tolerância zero para spam e eu fui condenado sem direito a defesa.
Como eu não faço spam, todos os assinantes da Dicas-L assinam a lista mediante um mecanismo de confirmação dupla, preciso explicar o que aconteceu.
Com o tempo, muitos assinantes deixam de usar seus endereços eletrônicos fazendo com que os provedores os removam. Uma das técnicas utilizadas por provedores para detectar spam é inserir endereços de email não existentes em alguns locais para que os softwares que colhem emails pela web os capturem. Desta forma, se alguém envia mensagens para estes endereços propositalmente espalhados pela Internet, certamente é um spammer.
Bom, no meu caso não foi bem assim. Eu fui considerado um spammer por enviar mensagem para um endereço não existente mas que já havia existido um dia.
Felizmente, como eu mantenho múltiplos backups do meu conteúdo, eu consegui contratar um novo provedor e colocar o site novamente no ar em algumas horas.
Para evitar uma repetição deste fato, eu faço uma limpeza periódica da lista de assinantes, removendo os endereços que não existem mais. Como não poderia deixar de ser, eu uso um pequeno script bash para realizar esta tarefa:
$ cat clean_mail.sh
#!/bin/bash
assinantes=dicas-l.list
log=/var/log/mail.log
log_processado=/tmp/mail.log.$$
enderecos_invalidos=/tmp/enderecos_invalidos.$$
enderecos_validos=/tmp/enderecos_validos.$$
cd /var/log/
for log in mail.*
do
fgrep -i "unknown user" $log | \
grep -o '[[:alnum:]+._-]*@[[:alnum:]+._-]*' | \
sort | uniq -i >> $enderecos_invalidos
done
vi $enderecos_invalidos
grep -v -f $enderecos_invalidos $assinantes > $enderecos_validos
wc $enderecos_validos $assinantes
Eu utilizo o programa fgrep
para buscar nos vários arquivos de log
do sistema (mail.*
) as palavras unknown user. Sempre que eu tento
enviar mensagens para um endereço eletrônico e ele não mais existir
no servidor remoto, estas palavras são registradas no log do sistema,
juntamente com o endereço eletrônico inválido. O comando fgrep
se
diferencia do programa grep
por ser muito mais rápido e por fazer a
busca baseando-se apenas nos caracteres fornecidos na linha de comando,
sem nenhum processamento adicional. Como os arquivos de log para um site
como a Dicas-L, com milhares de assinantes, são enormes, a diferença na
velocidade de processamento faz toda a diferença.
A diretiva -o
do comando grep
faz com que sejam impressos apenas os
caracteres que batem com a expressão regular, e não a linha inteira. Desta
forma, apenas o endereço de email é capturado.
A expressão regular
[[:alnum:]+._-]*@[[:alnum:]+._-]*
é a representação de um endereço eletrônico. O resultado é então
ordenado (sort
) e removidas as linhas duplicadas (uniq -i
).
O comando wc
é usado apenas para comparar a listagem original com a listagem
processada, para ver quantos endereços serão removidos da lista de assinantes
da Dicas-L.
A parte final, que é a atualização da lista de assinantes, eu faço manualmente, para evitar algum eventual problema ou catástrofe. É claro que eu tenho múltiplos backups para recuperação em caso de problemas, mas eu prefiro ser cuidadoso.
Vocês devem estar se perguntando como interpretar a expressão regular montada para identificar endereços de email no arquivo de log, mas esta explicação está fora do escopo destas aulas introdutórias. Uma explicação bastante aprofundada e detalhada faz parte do curso de programação Shell Linux criado pelo Júlio Neves, e que será lançado no início de novembro de 2017.
Algum tempo atrás eu notei um aumento elevado de acesso aos meus sites a partir de computadores oriundos da China. E nos últimos dias, conforme apontado pelo software OSSEC praticamente metade de todos os acessos aos sites se constituiam em tentativas de invasão.
Em vista disto, resolvi tomar uma atitude mais radical, bloqueando todos os acessos a partir das redes da China. Ao tentar localizar os números das redes chinesas, constatei que o problema está se agravando e é muito mais comum do que eu imaginava.
Criei então um pequeno script para criar as regras de firewall proibindo acessos a partir das redes chinesas.
Este script, reproduzido a seguir, é carregado durante o boot da máquina.
$ cat blockchina.sh
#!/bin/bash
cd /root/
BLOCKDB=/root/chinese-iptables-blocklist.txt
if [ -f $BLOCKDB ]
then
while read IPS
do
iptables -A INPUT -s $IPS -j DROP
iptables -A OUTPUT -d $IPS -j DROP
done < $BLOCKDB
else
echo "Arquivo $BLOCKDB não existe"
exit 1
fi
A primeira coisa a ser feita é verificar se existe o arquivo contendo a lista de endereços IP da rede chinesa. Se existir o arquivo, passo então a bloquear os endereços, um a um.
ALERTA IMPORTANTE: como qualquer medida que faz o bloqueio de acesso, tome cuidado para não se excluir, perdendo o contato com o servidor e ficando impossibilitado de realizar os ajustes que se fizerem necessários. Para variar, isto aconteceu comigo. Da mesma forma, caso se decida a seguir os procedimentos aqui descritos, a responsabilidade é inteiramente sua, não ofereço nenhum tipo de garantia quanto ao funcionamento deste roteiro. Faça testes exaustivos antes de colocar em produção.
Se você chegou até aqui, parabéns! Eu tenho certeza de que você está maravilhado com o poder e simplicidade da programação shell Linux.
Você pode agora observar que a maioria dos scripts utilizados neste
tutorial seguem uma mesma lógica, o que varia são os comandos e a função
de cada um deles. Utilizei apenas exemplos práticos e úteis. Resumidamente,
recheando com os comandos corretos um trecho de código com quatro linhas
descrevendo um laço while
ou for
, e testando algumas condições
com o comando if
, podemos executar tarefas bastante complexas.
Este texto não é uma demonstração exaustiva de todas as possibilidades, isto é feito no curso de programação Shell que lançaremos em novembro, mas apenas com o que foi abordado aqui você pode aumentar em muito a sua produtividade e fazer bonito com o seu chefe e os seus colegas de trabalho. Mas continue estudando, embora você tenha avançado muito, ainda tem muita coisa boa pela frente.
Se gostou ou não, se tiver comentários, sugestões, enfim, qualquer coisa que deseje nos transmitir, por favor, deixe sua mensagem abaixo. Sua opinião vale muito para nós e irá nos ajudar a melhorar este material.
O campo de comentário usa um plugin do Facebook. Caso você não tenha conta no Facebook, envie seu comentário para o endereço programacao_shell@dicas-l.com.br, que sua mensagem será encaminhada para mim (Rubens Queiroz) e para o Júlio Neves. Tenha certeza de que todas as mensagens serão respondidas por nós.
Não se esqueça, a terceira e última parte será enviada no dia 6 de novembro. Até lá!
Júlio Cezar Neves | Rubens Queiroz de Almeida |