terça-feira, 24 de março de 2009

SPAMDYKE - UM ALIADO CONTRA O SPAM NO QMAIL

ATENÇÃO!!! Existem algumas particularidades com relação a pasta de localização do vpopmail, em meu caso não é /home/vpopmail, mas sim: /var/qmail/vpopmail (depende da versão do qmail instalada em seu servidor)

Outra observação importante, a localização do (run) do qmail (é um arquivo que é executado durante uma conexão com o q-mail), em meu caso, fica localizado em:
/var/qmail/related/etc/qmail-smtpd/

-------------------------------------------- * * * * ----------------------------------------

Um dos maiores problemas atuais da informática é o famigerado Spam. Todo SysAdmin acorda sempre tendo que ler logs, vasculhar ips, criar blacklists, scripts, procurar boas soluções etc etc. Em resumo, boa parte do tempo de nossa vida profissional estaremos lutando contra os malucos que teimam em encher a caixa postal das pessoas de lixo.
O Spam é sem dúvida um negócio. O que eu mais vejo por aí são as famosas empresas de Email Marketing proliferando. No fim, o Email Marketing nada mais é que uma forma mais bonita de falar, Spam.
E, enquanto tem gente ganhando dinheiro mandando mensagem para você, seus gerentes estão ali lhe cobrando a velocidade de acesso em um link abarrotado de mensagens não "solicitadas". Ou seja, enquanto alguns ganham dinheiro, outros perdem dinheiro por causa disto.

Várias empresas hoje ganham a vida em cima do Spam. A IronPort, que hoje é parte da Cisco, tem um dos melhores appliances do mercado para isto. Do lado brasileiro, temos a mineira Mais Informática, com seu produto MAV, que não deixa a desejar em nada a seus grandes concorrentes. E, também, a maioria das empresas de anti-vírus possui produtos nesta área.

Eu, sempre, sou adepto de soluções livres. E, como em geral, os servidores de email são baseados ou no Postfix ou no Qmail, descobrir soluções para estes produtos, é um dos meus esportes preferidos.

Pessoalmente, apesar das intensas opiniões contrárias de outros analistas eu adoro implementar soluções com o Qmail. E, o post em questão fala sobre uma ferramenta que é o Spamdyke é um tipo de canivete suiço para o Qmail.

O grande trunfo do SpamDyke em cima de outras soluções baseadas no Qmail, é não pedir com a maioria das features do produto, que você aplique um patch nos fontes e o recompile.

Na realidade, o SpamDyke é um programa ( módulo ) que se adiciona ao Qmail e faz a coisa funcionar de um jeito melhor do que ele já faz ( e como faz, viu ).

A vantagem do SpamDyke é o corte da conexão já lá no início do processamento. Ou seja, meu servidor está tentando mandar um spam para o servidor que tem o SpamDyke instalado. Caso a minha mensagem ( ou servidor ) seja caracterizado como fonte de spam, eu terei minha conexão cortada ali.
Isto elimina custo de processamento ( pois a mensagem não vai se processada pelo servidor ) e banda ( ou seja, não vai haver a transferência da mensagem para o servidor ). Este custo, ao longo dos dias em que se instala o SpamDyke é sentido no acesso remoto a máquina e na banda que fica disponível para outros serviços.

O processo de instalação do SpamDyke é bem fácil. Na realidade, ele está muito bem documentado no site do projeto, mas uma documentação em português não faz mal a ninguém né ?

Baixe o pacote do SpamDyke no site do projeto e faça o processo padrão :

# wget -c http://sitedospamdyke/download/spamdyke-x.y.z.tgz
# tar -xvzf spamdyke-x.y.z.tgz
# cd spamdyke-x.y.z
# ./configure
# make
# cp spamdyke /usr/local/bin

Ou seja, com este processo, seu spamdyke já está prontinho para funcionar em qualquer servidor que esteja com o Qmail já em operação.
No meu caso, eu uso o QmailToaster, que integra com a maioria das distribuições espalhadas pelo mundo ( se bem que eu já usei a maioria das variações das implementações de Qmail disponíveis por aí :) ).

Em geral, você vai procurar o arquivo run do smtpd, e nele você vai usar a seguinte sintaxe :

exec /usr/local/bin/softlimit -m 40000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/tcp.smtp.cdb -c "$MAXSMTPD" -u "$QMAILDUID" -g "$NOFILESGID" 0 smtp \
/usr/local/bin/spamdyke -f /etc/spamdyke.conf \
/var/qmail/bin/qmail-smtpd /home/vpopmail/bin/vchkpw /bin/true \
2>&1

Ou seja, seu arquivo de run do smtpd vai ter uma linha como a que está em negrito no trecho que eu peguei do run. Ou seja, aqui você vai estar chamando o SpamDyke antes de ativar o qmail-smtpd ( por um acaso o daemon de smtp do Qmail :-) ) .

Assim, entende-se por este processo o porque tudo é parado antes de gerar um processamento maior na máquina.

O arquivo de configuração, exemplificado abaixo, é o citado inclusive, no site oficial do SpamDyke pelo autor do programa :

log-level=2
local-domains-file=/var/qmail/control/rcpthosts
max-recipients=5
idle-timeout-secs=60
graylist-dir=/home/vpopmail/graylist
graylist-min-secs=300
graylist-max-secs=1814400
policy-url=http://my.policy.explanation.url/
sender-blacklist-file=/home/vpopmail/blacklist_senders
recipient-blacklist-file=/home/vpopmail/blacklist_recipients
ip-in-rdns-keyword-file=/home/vpopmail/blacklist_keywords
ip-blacklist-file=/home/vpopmail/blacklist_ip
rdns-blacklist-dir=/home/vpopmail/blacklist_rdns.d
reject-empty-rdns
reject-unresolvable-rdns
reject-ip-in-cc-rdns
rdns-whitelist-file=/home/vpopmail/whitelist_rdns
ip-whitelist-file=/home/vpopmail/whitelist_ip
greeting-delay-secs=5
check-dnsrbl=zombie.dnsbl.sorbs.net
check-dnsrbl=dul.dnsbl.sorbs.net
check-dnsrbl=bogons.cymru.com
reject-missing-sender-mx
tls-certificate-file=/var/qmail/control/servercert.pem

Quem conhece bem servidores de email vai bater o olho na maioria destas linhas e entender de cara. Os detalhes de cada opção acima eu vou colocar abaixo. O post vai ficar um pouco extenso por causa disto, mas acho que o interessado no mesmo não vai se preocupar com isto.
Em breve vou soltar uma documentação do tipo Hardening Qmail, com estas opções, até para a pessoa não ter que ficar procurando um monte de coisa solta aqui no site.

Bom, as duas primeiras linhas se auto-explicam, uma é o nível de debug e a outra, o seu arquivo de domínios válidos do Qmail.


max-recipients=5

Esta é uma opção que eu gosto muito. Sabe aqueles famosos emails que um recebe do outro com aqueles pps otimistas e sai mandando para todo mundo ? Este é o modo mais fácil de forçar o seu usuário a desistir de mandar a mensagem, pois quando ele exceder o número máximo ( 5 destinatários por mensagem ), o servidor vai recusar a saída/entrada de mensagens deste tipo.

idle-timeout-secs=60

Timeout da conexão. Ou seja, se em 60 segundos nada responde, diga adeus, não preciso de mandar este troço ou receber este troço.

graylist-dir=/home/vpopmail/graylist
graylist-min-secs=300
graylist-max-secs=1814400

Opções relacionadas ao Greylisting.
O conceito de Greylisting é uma idéia interessante, desenvolvida por Evan Harris e consiste em recusar temporariamente uma mensagem e esperar por sua retransmissão. Esta técnica parte da premissao de que emails válidos são enviados a partir de MTAs
(Mail Transfer Agents) legítimos, que mantém filas e possuem políticas de
retransmissão em caso de erros temporários e geralmente spammers e códigos
maliciosos raramente usam MTAs legítimos.
Apesar de hoje spammers se utilizarem de MTAs legítimos, esta técnica também se mostra interessante para barrar vírus e trojans, que usam estruturas de smtp embutidas no mesmo, que não utilizam-se do tratamento que um MTA faz com as mensagens.

policy-url=http://my.policy.explanation.url/

Aqui você vai colocar um endereço onde você explicará as políticas de anti-spam que você utiliza, e como o usuário ( ou seja, quem quer lhe mandar mensagens ) deve proceder caso tenha algum problema de envio ao seu servidor.

sender-blacklist-file=/home/vpopmail/blacklist_senders

Endereços ou parte de domínios que não serão aceitos pelo seu servidor. Sintaxes aceitadas : email@domini.com.br, @dominio.com.br.

recipient-blacklist-file=/home/vpopmail/blacklist_recipients

Caminho contrário. Lista de domínios para onde o seu servidor não vai enviar mensagens ( ou seja, seus usuários não poderão mandar mensagens para este local ). Mesma estrutura de sintaxe aceita pela outra listagem.

ip-in-rdns-keyword-file=/home/vpopmail/blacklist_keywords

Lista de palavras não aceitas quando for feita a resolução reversa do DNS. Exemplo : recebo um email do dominio sexo.com.br e tenho esta palavra "negada" na minha blacklist de palavras do dns reverso.
Quando ele for resolvido pelo SpamDyke, esta mensagem será considerada inválida por possuir uma palavra que está nesta blacklist.
Uma blacklist interessante, principalmente, porque spammers vão usar alguns domínios específicos para suas ações.


ip-blacklist-file=/home/vpopmail/blacklist_ip

Ips de servidor aos quais você não quer realmente receber mensagens. Aqui, é interessante usar uma dica que já dei, que é bloquear os ips advindos por exemplo, da China e da Koreia, que são dois países sabidamente gerados de grande volume de spam.

rdns-blacklist-dir=/home/vpopmail/blacklist_rdns.d

Este aqui é um ponto interessante. É um diretório onde estarão "diretórios" com os nomes dos domínios que serão bloqueados durante consultas. Ou seja, todo domínio que estiver neste diretório será consultado e caso bata com algum, será dropado logo já na conexão.
Não vou conseguir ser tão claro quanto ao funcionamento, porque eu ainda não achei uma boa lógica para trabalhar com isto. No fundo, quando achar, devo gerar algum script para criação dos diretórios e afins :-)
Para quem quiser saber mais sobre isto, clique neste link aqui e dê uma estudada.



reject-empty-rdns
reject-unresolvable-rdns

Aqui, o básico. Não tem DNS reverso ou não conseguiu se resolver o DNS reverso do cara, a mensagem é negada.

reject-ip-in-cc-rdns

Procura pelo ip do servidor remoto ( DNS reverso ) e também pelas duas letras que levam ao código do país. Caso ambos sejam encontrados, diga adeus, a conexão é rejeitada ( toda a busca é feita no nome do domínio em questão ).

rdns-whitelist-file=/home/vpopmail/whitelist_rdns

Mesmo que hajam erros no DNS reverso, caso ele esteja nesta listagem, vai ser aceito qualquer conexão dos ips em questão. Um ip por linha.

ip-whitelist-file=/home/vpopmail/whitelist_ip

Mesmo esquema, só que relacionado a ips que estejam em alguma rbl ou coisa parecida.

greeting-delay-secs=5

Tempo aceito de demora para que o servidor receba o famoso Hello, primeiro passo de uma conexão smtp.


check-dnsrbl=zombie.dnsbl.sorbs.net
check-dnsrbl=dul.dnsbl.sorbs.net
check-dnsrbl=bogons.cymru.com

RBLS que serão utilizadas pelo Spamdyke. Uso estas e mais algumas que o pessoal indica por aí :-)

reject-missing-sender-mx

Se o domínio não tiver um MX válido, logicamente, não vai ser aceito conexão do mesmo. Por incrível que pareça, isto ainda acontece com diversos domínios que estão configurados por aí.
Erros de DNS são muito comuns ainda.

tls-certificate-file=/var/qmail/control/servercert.pem

Caso esteja usando uma conexão segura, use o mesmo certificado já criado na hora de configurar o seu Qmail.

O post foi um pouco longo, mas foi mesmo para apresentar esta ferramenta que é o SpamDyke. Com a instalação do mesmo, nesta configuração pura, já cheguei a ter por volta de 90% de negação de spams ( de cara ) e isto me deixou um pouco animado com o mesmo.
Com esta ferramenta dominada, duvido que a porcentagem não suba.

Ainda não há um script oficial para estatísticas, mas na lista do SpamDyke eu consegui um script muito legal ( na realidade, precisei editar ele diversas vezes ) e ele conseguiu me mostrar o seguinte resultado :

[root@neo ~]# cat /var/log/maillog | spamdyke-stats
7804 DENIED_IP_IN_CC_RDNS
6803 DENIED_RDNS_MISSING
4362 DENIED_RBL_MATCH
2394 ALLOWED
1532 DENIED_RDNS_RESOLVE
553 DENIED_OTHER
418 DENIED_BLACKLIST_IP
376 TIMEOUT
29 DENIED_TOO_MANY_RECIPIENTS
22 DENIED_SENDER_NO_MX

Allowed: 2394
Denied : 21899
Sum: 24293
% Spam : 90.15%

Ou seja, não é o ideal, porque o bom é ter um controle geral dos logs, mas já ajuda para ver como está o rendimento do spamdyke. Para quem quiser baixar, deixei o código pronto por aqui ;-)
Espero que o post ajude a quem está tentando implementar o SpamDyke em seus servidores. E, vamos trocar idéias sobre o mesmo porque quanto mais experiências sobre ele

-------------------------------------------- * * * * ----------------------------------------

Este documento foi estraido do site: http://www.ataliba.eti.br/node/1603

Site Oficial do SpamDyke: http://www.spamdyke.org/

-------------------------------------------- * * * * ----------------------------------------

Script de Correção no Q-MAIL


Este Script corrige alguns bugs nas pastas de controle do q-mail, onde as mensagens e informações dos e-mails são armazenadas. Útil também para limpar aquelas longas filas de SPAM que ficam armazenadas no queue do q-mail.

Se você executá-lo sem parâmetros, ele apenas verifica, não corrige nada;
Usando com o parâmetro LIVE (minúsculo), será corrigido toda e qualquer anormalidade encontrada;
Usando com o parâmetro EMPTY (minúsculo), será limpado (esvaziado) todo e qualquer e-mail que esteja na fila (queue) do q-mail.

Sintase:
./qfixq live empty

Salve o script abaixo com o nome de qfixq
mude o atributo para executável (chmod +x qfixq)
execute para verificação ou para correção.


#------------------- Início do Script ---------------------------------
#!/usr/bin/perl -w
#
# qfixq
# John Simpson 2003-10-17
#
# repairs a messed-up qmail queue structure.
#
# *********************************************************************
# *** ***
# *** DANGER! DANGER! DANGER! ***
# *** ***
# *** DO NOT RUN THIS WHILE ANY QMAIL-RELATED PROGRAMS ARE RUNNING! ***
# *** ***
# *** DANGER! DANGER! DANGER! ***
# *** ***
# *********************************************************************
#
# 2004-01-20 jms1 - fixed an issue with directory/file permissions being set
# incorrectly in some cases
#
# 2004-01-21 jms1 - fixed issue where /v/q/queue/lock/trigger was being set
# to the wrong owner, causing queue slowdowns as detailed here:
# http://lifewithqmail.org/lwq.html#trigger
#
# 2004-01-22 jms1 - fixed a REALLY minor issue- mess/*/* files were being
# forced to perm 0640, where their native state in a correct queue is 0644.
# the old way did no damage (the ownership was correct so it didn't really
# matter) but it was dumping a lot of un-necessary warnings when it ran,
# which may make people think there was a problem when there wasn't one.
#
# 2004-10-13 jms1 - at least one version of perl considers mkdir() with
# only one argument to be an error, so i've added specific permissions to
# all mkdir() calls. i've also added a specific umask() call, just because
# it's a good idea for any program which creates files or directories which
# shouldn't be world-readable when they're first created. thanks go to
# Tom Clegg for the suggestion.
#
# 2005-04-11 jms1 - (no code changed.) changed the copyright notice to
# specify that the license is the GPL VERSION 2 ONLY. i'm not comfortable
# with the "or future versions" clause until i know what these "future
# versions" will look like.
#
# 2005-04-14 jms1 - (no code changed.) added comments to show which lines
# are to be changed for configuring the script to work on non-standard
# machines.
#
# 2005-04-20 jms1 - once upon a time, there was a guy who had a queue with
# over 200 buckets. we don't know why, maybe his server handles millions
# of message per day, but whatever... he downloaded this script, and even
# though he had been specifically told to fix the bucket count first, he
# ran it without fixing the bucket count first. in doing so he destroyed
# his queue so badly that qmail-send wouldn't work- it started spewing
# "unable to open info/24, sleeping..." over and over again.
#
# so now the script will run qmail-showctl and figure out how many buckets
# to use automatically.
#
# 2005-04-21 jms1 - again in the interest of safety, i'm adding an extra
# safety feature to the script which will allow it to FIND problems, but
# not fix them unless you run the script as "qfixq live".
#
# 2005-04-22 jms1 - to protect people from thinking they've fixed a problem
# which still exists, now if the script is not running in live mode, it
# will print a reminder at the end of the output as well as the beginning.
#
# 2005-08-30 jms1 - fixed two minor permissions issues. thanks to Michael
# Martinell for spotting the problem. i keep saying i shouldn't write
# code when i'm tired...
#
# 2005-11-15 jms1 - adding a "empty" option which will delete any files
# relating to individual messages. this should leave you with an
# empty queue.
#
# i also removed "default number of buckets" as an option- basically,
# if "qmail-showctl" can't give you the right answer, it would be too
# dangerous to even think of trying to run this script- because that
# would mean that there's a lot more wrong than just your queue being
# corrupted.
#
###############################################################################
#
# Copyright (C) 2003-2005 John Simpson.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# or visit http://www.gnu.org/licenses/gpl.txt
#
###############################################################################

require 5.003 ;
use strict ;

###############################################################################
#
# configuration here

my $vq = "/var/qmail" ;
my $qmailq = getpwnam ( "qmailq" ) ;
my $qmailr = getpwnam ( "qmailr" ) ;
my $qmails = getpwnam ( "qmails" ) ;
my $qmail = getgrnam ( "qmail" ) ;

###############################################################################
#
# it should not be necessary to change anything below this point, however
# if you do find a bug or have an idea to make it work better, please let
# me know.
#
###############################################################################

umask ( 077 ) ;

my %dirown =
(
"bounce" => $qmails ,
"info" => $qmails ,
"intd" => $qmailq ,
"local" => $qmails ,
"mess" => $qmailq ,
"remote" => $qmails ,
"todo" => $qmailq ,
) ;

my %dirperm =
(
"bounce" => 0700 ,
"info" => 0700 ,
"intd" => 0700 ,
"local" => 0700 ,
"mess" => 0750 ,
"remote" => 0700 ,
"todo" => 0750 ,
) ;

my %fileperm =
(
"bounce" => 0600 ,
"info" => 0600 ,
"intd" => 0644 ,
"local" => 0600 ,
"mess" => 0644 ,
"remote" => 0600 ,
"todo" => 0644 ,
) ;

my %dirbuckets =
(
"bounce" => 0 ,
"info" => 1 ,
"intd" => 0 ,
"local" => 1 ,
"mess" => 1 ,
"remote" => 1 ,
"todo" => 0 ,
) ;

my $vqq = "$vq/queue" ;
my $live = 0 ;
my $empty = 0 ;

my ( %file , %msg , %ren , %del , $buckets ) ;

$| = 1 ;

###############################################################################
#
# fix/set ownership and permissions on a file

sub chownmod($$$@)
{
my $uid = shift ;
my $gid = shift ;
my $prm = shift ;

while ( my $f = shift )
{
my @s = stat $f ;

if ( ( $s[4] != $uid ) || ( $s[5] != $gid ) )
{
if ( $s[4] != $uid )
{
printf "Fixing uid of $f (%d s/b %d)\n" ,
$s[4] , $uid ;
}

if ( $s[5] != $uid )
{
printf "Fixing gid of $f (%d s/b %d)\n" ,
$s[5] , $gid ;
}

$live && chown ( $uid , $gid , $f ) ;
}

if ( ( $s[2] & 0777 ) != ( $prm & 0777 ) )
{
printf "Fixing permissions on $f (%04o s/b %04o)\n" ,
( $s[2] & 0777 ) , ( $prm & 0777 ) ;
$live && chmod ( ( $prm & 0777 ) , $f ) ;
}
}
}

###############################################################################
###############################################################################
###############################################################################
#
# sanity checks

$< && die "This program requires root privileges.\n" ;

while ( my $z = shift @ARGV )
{
if ( $z eq "live" )
{
$live = 1 ;
}
elsif ( $z eq "empty" )
{
$empty = 1 ;
}
}

if ( $live && $empty )
{
print <
Running in LIVE and EMPTY mode. All messages WILL BE DELETED from the
queue.

EOF
}
elsif ( $live )
{
print <
Running in LIVE mode. All fixes will be written to the disk.

EOF
}
elsif ( $empty )
{
print <
Running in EMPTY mode, but not LIVE mode. Messages will NOT actually be
deleted.

If you wish to entirely empty the queue, use "$0 live empty".

EOF
}
else
{
print <
Running in FIND mode. Any fixes described will NOT be written to the disk.

If you wish to run in LIVE mode and fix problems, use "$0 live".

If you wish to entirely empty the queue, use "$0 live empty".

EOF
}

###############################################################################
#
# figure out how many buckets we have to play with

open ( B , "$vq/bin/qmail-showctl |" )
or die "Can\'t run $vq/bin/qmail-showctl: $!\n" ;
while ( my $line = )
{
next unless ( $line =~ /split\: (\d+)/ ) ;
$buckets = $1 ;
last ;
}
close B ;

if ( $buckets )
{
print "Using $buckets buckets as ordered by qmail-showctl.\n" ;
}
else
{
die <

Cannot determine how many buckets to use, cannot continue.

EOF
}

###############################################################################
#
# fix directory ownerships and permissions

chownmod ( $qmailq , $qmail , 0750 , $vqq ) ;
chownmod ( $qmailq , $qmail , 0750 , "$vqq/lock" ) ;
chownmod ( $qmails , $qmail , 0600 , "$vqq/lock/sendmutex" ) ;
chownmod ( $qmailr , $qmail , 0644 , "$vqq/lock/tcpto" ) ;
chownmod ( $qmails , $qmail , 0622 , "$vqq/lock/trigger" ) ;
chownmod ( $qmailq , $qmail , 0700 , "$vqq/pid" ) ;

for my $dir ( sort keys %dirown )
{
unless ( -d "$vqq/$dir" )
{
print "Creating missing directory $vqq/dir\n" ;
$live && mkdir ( "$vqq/$dir" , 0700 ) ;
}

chownmod ( $dirown{$dir} , $qmail , $dirperm{$dir} , "$vqq/$dir" ) ;

if ( $dirbuckets{$dir} )
{
for my $n ( 0 .. ( $buckets - 1 ) )
{
unless ( -d "$vqq/$dir/$n" )
{
print "Creating missing bucket $vqq/$dir/$n\n" ;
$live && mkdir ( "$vqq/$dir/$n" , 0700 ) ;
}

chownmod ( $dirown{$dir} , $qmail , $dirperm{$dir} ,
"$vqq/$dir/$n" ) ;
}
}
}

# dunno what to do with files in "pid"... delete? ignore? anyone?
# thought about deleting, ignoring them for now...
# rm -r $vqq/pid/*

########################################
# make a list of what files exist for each message

for my $dir ( sort keys %dirown )
{
print "Reading $vqq/$dir\n" ;

open ( L , "find $vqq/$dir -type f |" )
or die "Can\'t run [find $vqq/$dir -type f]: $!\n" ;

while ( my $line = )
{
chomp $line ;
$line =~ m|.*/(.*)| ;
my $n = $1 ;

if ( $empty )
{
$file{"$n:$dir"} = $line ;
$msg{$n} = "" ;
$del{$n} = "" ;
next ;
}

chownmod ( $dirown{$dir} , $qmail , $fileperm{$dir} , $line ) ;

my @s = stat $line ;

unless ( $s[7] )
{
print "Remving zero-byte file $line\n" ;
$live && unlink $line ;
next ;
}

if ( exists $file{"$n:$dir"} )
{
# duplicate names (i.e. info/3/101 and info/5/101) ???
print "Duplicate [$n:$dir] message will be killed\n" ;
print "\tRemoving $line\n" ;
$live && unlink $line ;
$del{$n} = 1 ;
next ;
}

$file{"$n:$dir"} = $line ;
$msg{$n} = "" ;

if ( $dirbuckets{$dir} )
{
$line =~ m|.*/(.*?)/$n| ;
my $b = $1 ;
if ( $b != ( $n % $buckets ) )
{
print "$n is in the wrong bucket\n" ;
$ren{$n} ||= $n ;
}
}

if ( $dir eq "mess" )
{
if ( $n != $s[1] )
{
print "$n should be $s[1]\n" ;
$ren{$n} = $s[1] ;
}
}
}

close L ;
}

###############################################################################
#
# kill off any messages which need to be deleted

for my $m ( sort keys %del )
{
print "Killing message $m\n" ;

for my $dir ( sort keys %dirown )
{
if ( exists $file{"$m:$dir"} )
{
my $f = $file{"$m:$dir"} ;
print "\tRemoving $f\n" ;

$live && unlink $f ;

delete $file{"$m:$dir"} ;
}
}

if ( exists $ren{$m} )
{
delete $ren{$m} ;
}

delete $del{$m} ;
delete $msg{$m} ;
}

###############################################################################
#
# analyze the lists, find and delete mesages with missing pieces

print "Analyzing the results...\n" ;

for my $m ( sort keys %msg )
{
if ( exists $file{"$m:bounce"} ) { $msg{$m} .= "B" ; }
if ( exists $file{"$m:intd"} ) { $msg{$m} .= "D" ; }
if ( exists $file{"$m:info"} ) { $msg{$m} .= "F" ; }
if ( exists $file{"$m:local"} ) { $msg{$m} .= "L" ; }
if ( exists $file{"$m:mess"} ) { $msg{$m} .= "M" ; }
if ( exists $file{"$m:remote"} ) { $msg{$m} .= "R" ; }
if ( exists $file{"$m:todo"} ) { $msg{$m} .= "T" ; }

next if ( ( $msg{$m} eq "DMT" ) # waiting to be processed
|| ( $msg{$m} eq "FLM" ) # waiting on local delivery
|| ( $msg{$m} eq "FMR" ) # waiting on remote delivery
|| ( $msg{$m} eq "BFLM" ) # bounce from local delivery
|| ( $msg{$m} eq "BFMR" ) # bounce from remote delivery
) ;

print "$m: [$msg{$m}] illegal file combination, removing\n" ;

for my $dir ( sort keys %dirown )
{
if ( exists $file{"$m:$dir"} )
{
my $f = $file{"$m:$dir"} ;
print "\tRemoving $f\n" ;

$live && unlink $f ;

delete $file{"$m:$dir"} ;
}
}

if ( exists $ren{$m} )
{
delete $ren{$m} ;
}

delete $msg{$m} ;
}

###############################################################################
#
# handle any renaming that needs to be done, either because "mess" filenames
# are not the same as the inode numbers, or because the files are in the
# wrong buckets.
#
# we do this as two passes, just in case a message's
# new filename already exists.

########################################
# first pass: all files get ".temp" added first

for my $m ( sort keys %ren )
{
for my $dir ( sort keys %dirown )
{
if ( exists $file{"$m:$dir"} )
{
my $f = $file{"$m:$dir"} ;
my $n = "$f.temp" ;

print "Renaming(1) $f to $n\n" ;
$live && rename ( $f , $n ) ;

$file{"$m:$dir"} = $n ;
}
}
}

########################################
# second pass: the ".temp" files get their final names

for my $m ( sort keys %ren )
{
########################################
# these directories use a bucket number
# which must be part of the final filename

for my $dir ( sort keys %dirown )
{
if ( exists $file{"$m:$dir"} )
{
my $f = $file{"$m:$dir"} ;
my $n = "$vqq/$dir/$ren{$m}" ;

if ( $dirbuckets{$dir} )
{
my $b = $ren{$m} % $buckets ;
$n = "$vqq/$dir/$b/$ren{$m}" ;
}

print "Renaming(2) $f to $n\n" ;
$live && rename ( $f , $n ) ;

delete $file{"$m:$dir"} ;
$file{"$ren{$m}:$dir"} = $n ;
}
}

$msg{$ren{$m}} = $msg{$m} ;
delete $msg{$m} ;
}

###############################################################################
#
# in case they missed it the first time...

unless ( $live )
{
print <

******************************************************************************

This was not LIVE mode.
Anything described above was NOT written to the disk.

If you wish to run in live mode, use "$0 live".

If you wish to entirely empty the queue, use "$0 live empty".

******************************************************************************

EOF
}
#
#------------------------------ Final do Script ---------------------------------------
#
#

O Documento original, encontra-se em http://qmail.jms1.net/scripts/qfixq.shtml