#!/usr/bin/perl # FreeBSD Brasil LTDA - Patrick Tracanelli # # fila-qmail - programa pra gerenciar a complexa e poderosa fila de mensagens # locais e remotas do MTA Qmail. ## # Baseado no script de Kasperv e Michelle algumacoisa... ## # Distribuido junto aos Servidores de Email implementados pela FreeBSD Brasil LTDA. # FreeBSD, Qmail, Vpopmail, Qmail-scanner, spamassassin, Daemontools, # McAfee UVScan - Unix Virus Scanner, Mysql, e o necessario pra isso tudo funcionar junto. use strict; use diagnostics; my ($CID) = 'clientefreebsdbrasil.com.br'; #################### Variaveis que PODEM ser alteradas pela usuario. ##### # Caminho completo pro seu diretorio de filas do qmail... my ($queue) = '/var/qmail/queue/'; ##### # Se voce possuir algum script ou comando externo que inicie e mate # o seu Qmail, entao defina-os nas variaveis $stopmail e $startmail # NAO pode haver mais de uma variavel com esses nomes. # Caso voce utilize o Daemontools... my ($stopqmail) = '/usr/local/bin/svc -d /service/qmail'; my ($startqmail) = '/usr/local/bin/svc -u /service/qmail'; # Se preferir, deixe eu matar o qmail sozinho. # my ($stopqmail) = ''; ##### # Defina aqui o comando ou script que retorne o PID do qmail-send # pidof pode ser instalado pelo Ports do FreeBSD #my ($pidcmd) = 'pidof qmail-send'; ###################################################### # DAQUI PRA BAIXO COMECA O PROGRAMA. NAO MEXA MUITO # # SE NAO SOUBER OQ ESTAS FAZENDO... # ###################################################### # Se nao existir protousuario input if ($#ARGV == -1) { &Usage(); } # Define variaveis globais my ($norestart) = 0; # Cria as listas para e-mail nas filas local e remota my (@queues) = ("remote", "local"); my (@msglist) = (); my (%type) = (); foreach my $currentqueue (@queues) { # Lista as mensagens de cada fila opendir(DIR,"${queue}$currentqueue"); my (@dirlist) = grep !/\./, readdir DIR; closedir DIR; foreach my $dir (@dirlist) { opendir (SUBDIR,"${queue}${currentqueue}/$dir"); my (@files) = grep !/\./, map "$dir/$_", readdir SUBDIR; foreach my $file (@files) { push @msglist, "$file"; ($currentqueue eq "remote") ? ($type{"$file"} = 'R') : ($type{"$file"} = 'L'); } closedir SUBDIR; } } # Verifica user-level input my ($color) = 1; # Saida SEMPRE colorida. my ($summary) = 0; my (@actions) = (); foreach my $arg (@ARGV) { SWITCH: { $arg eq '-l' and do { push @actions, "&ListMsg('A')"; last SWITCH; }; $arg eq '-R' and do { push @actions, "&ListMsg('L')"; last SWITCH; }; $arg eq '-L' and do { push @actions, "&ListMsg('R')"; last SWITCH; }; $arg eq '-N' and do { $summary = 1; last SWITCH; }; $arg eq '-c' and do { $color = 0; last SWITCH; }; $arg eq '-s' and do { push @actions, "&Stats()"; last SWITCH; }; $arg =~ /^-v(.+)/ and do { push @actions, "&ViewMsg($1)"; last SWITCH; }; $arg =~ /^-d(.+)/ and do { push @actions, "&DelMsg($1)"; last SWITCH; }; $arg eq '-D' and do { push @actions, "&DelAll()"; last SWITCH; }; &Usage(); } } foreach my $action(@actions) { eval "$action"; } exit(0); sub ListMsg { my ($q) = shift; my (%ret, %date, %from, %subj, %to, %cc, %fsize); if ($summary == 0) { foreach my $msg(@msglist) { # Descobre o return-path open (MSG, "${queue}info/$msg"); $ret{$msg} = ; substr($ret{$msg}, 0, 1) = ''; chop ($ret{$msg}); close (MSG); # Descobre o tamanho do e-mail em disco. $fsize{$msg} = (stat("${queue}mess/$msg"))[7]; # Pegar dados basicos do cabecalho do e-mail open (MSG, "${queue}mess/$msg"); while () { if ($_ =~ /^Date: /) { $date{$msg} = $'; chop ($date{$msg}); } elsif ( $_ =~ /^From: /) { $from{$msg} = $'; chop ($from{$msg}); } elsif ( $_ =~ /^Subject: /) { $subj{$msg} = $'; chop ($subj{$msg}); } elsif ( $_ =~ /^To: /) { $to{$msg} = $'; chop ($to{$msg}); } elsif ( $_ =~ /^Cc: /) { $cc{$msg} = $'; chop ($cc{$msg}); } elsif ( $_ eq "\n") { last; } } } } if ($color == 1) { foreach my $msg(@msglist) { unless ($q eq $type{$msg}) { my ($dir, $rmsg) = split (/\//, $msg); print chr(27)."[01;34m$rmsg ($dir, $type{$msg})\n"; if ($summary == 0) { defined($ret{$msg}) and print " \e[01;31mReturn-path\e[00m: $ret{$msg}\n"; defined($from{$msg}) and print " \e[01;31mFrom\e[00m: $from{$msg}\n"; defined($to{$msg}) and print " \e[01;31mTo\e[00m: $to{$msg}\n"; defined($cc{$msg}) and print " \e[01;31mCc\e[00m: $cc{$msg}\n"; defined($subj{$msg}) and print " \e[01;31mSubject\e[00m: $subj{$msg}\n"; defined($date{$msg}) and print " \e[01;31mDate\e[00m: $date{$msg}\n"; defined($fsize{$msg}) and print " \e[01;31mSize\e[00m: $fsize{$msg} bytes\n\n"; } } } } else { foreach my $msg(@msglist) { unless ($q eq $type{$msg}) { my ($dir, $rmsg) = split (/\//, $msg); print "$rmsg ($dir, $type{$msg})\n"; if ($summary == 0) { defined($ret{$msg}) and print " Return-path: $ret{$msg}\n"; defined($from{$msg}) and print " From: $from{$msg}\n"; defined($to{$msg}) and print " To: $to{$msg}\n"; defined($cc{$msg}) and print " Cc: $cc{$msg}\n"; defined($subj{$msg}) and print " Subject: $subj{$msg}\n"; defined($date{$msg}) and print " Date: $date{$msg}\n"; defined($fsize{$msg}) and print " Size: $fsize{$msg} bytes\n\n"; } } } } &Stats(); } # Mostra conteudo da mensagem sub ViewMsg { my ($rmsg) = shift; unless ($rmsg =~ /^\d+$/) { print "$rmsg nao e' um numero de e-mail valido!\n"; } else { # Encontra o email my ($ok) = 0; foreach my $msg(@msglist) { if ($msg =~ /$rmsg$/) { $ok = 1; print "\n --------------\n NUMERO DO E-MAIL NA FILA: $rmsg \n --------------\n\n"; open (MSG, "${queue}mess/$msg"); while () { print $_; } close (MSG); last; } } # Se nao encontrar a msg if ($ok == 0) { print "Mensagem numero $rmsg nao foi encontrada na fila!\n"; } } } # Deleta uma mensagem da fila sub DelMsg { my ($rmsg) = shift; unless ($rmsg =~ /^\d+$/) { print "$rmsg nao e' uma mensagem valida!\n"; } else { # Mata o qmail &stopQmail() or die "O processo qmail nao pode ser interrompido: $!"; # Encontra o email my ($ok) = 0; foreach my $msg(@msglist) { if ($msg =~ /$rmsg$/) { $ok = 1; unlink "${queue}mess/$msg"; unlink "${queue}info/$msg"; if ($type{$msg} eq 'R') { unlink "${queue}remote/$msg"; } else { unlink "${queue}local/$msg"; } last; } } # Se nao achar o email, if ($ok == 0) { print "O e-mail $rmsg nao foi encontrado!\n"; } # Ressuscita Qmail &startQmail(); } } # Apaga todas as mensagens de ambas as filas. sub DelAll { my ($rmsg) = shift; # Para o qmail &stopQmail() or die "O processo qmail nao pode ser interrompido: $!"; # Search messages my ($ok) = 0; foreach my $msg (@msglist) { $ok = 1; print "Apagando o email $msg...\n"; unlink "${queue}mess/$msg"; unlink "${queue}info/$msg"; if ($type{$msg} eq 'R') { unlink "${queue}remote/$msg"; } else { unlink "${queue}local/$msg"; } } # Se nao houverem mensagens... if ($ok == 0) { print "Nao foram encontradas mensagens nas filas!\n"; } # Reinicia o Qmail... &startQmail(); } # Stats, oras... sub Stats { my ($l) = 0; my ($r) = 0; foreach my $msg(@msglist) { if ($type{$msg} eq 'R') { $r++; } else { $l++; } } if ($color == 1) { print chr(27)."[01;31mMensagens na fila Local".chr(27)."[00m: $l\n"; print chr(27)."[01;31mMensagens na fila Remota".chr(27)."[00m: $r\n"; } else { print "Mensagens na fila Local: $l\n"; print "Mensagens na fila Remota: $r\n"; } } # Pega o processo do qmail sub qmailPid { my $qmpid = `/bin/ps waux | /usr/bin/grep qmail-send | /usr/bin/grep -v "/bin/ps" | /usr/bin/awk '{print \$2}'`; # my $qmpid = `$pidcmd`; chomp ($qmpid); if ($qmpid =~ /^\d+$/) { return $qmpid; } return 0; } # Mata qmail sub stopQmail { my ($qmpid) = &qmailPid(); # Caso Exista o PID do Qmail, vamos mata-lo. if ($qmpid != 0) { # Se existir um script ou comando externo para mata-lo, vamos usa-lo. if ($stopqmail ne '') { print "Chamando comando externo para parar o qmail...\n"; if (system($stopqmail) > 0) { return 0; } # Senao, vamos tentar matar nos mesmos! } else { print "Terminando qmail (pid $qmpid)... essa operacao pode levar algum tempo caso existam mensagens sendo entregues.\n"; kill 'TERM', $qmpid; while (&qmailPid() eq "$qmpid\n"){ sleep 1; } } # Se o qmail nao estiver rodando, nao vamos querer reinicia-lo depois # evitando alteracoes no ambiente externo. } else { print "Qmail nao esta no ar.\n"; $norestart = 1; } return 1; } # Starta qmail sub startQmail { my ($qmpid) = &qmailPid(); if ($qmpid != 0) { print "Qmail esta ativo novamente, por isso nao sera reiniciado.\n"; } elsif ($norestart == 1) { print "Qmail nao estava em execussao quando o fila-qmail foi chamado, por isso nao sera reiniciado \n"; } else { print "Reiniciando qmail... "; system($startqmail); print "OK (esperamos).\n"; } return 1; } sub Usage { print "\n"; print chr(27)."[01;39m\n\t\t\tQmail/FreeBSD Mail Service da $CID\n".chr(27)."[00m"; print "\t\t\t (desenv. por FreeBSD Brasil LTDA)"; print "\n"; print chr(27)."[01;33m"; print " __ _ _ _ _ \n"; print " / _(_) | __ _ __ _ _ __ ___ __ _(_) | \n"; print " | |_| | |/ _` | _____ / _` | '_ ` _ \\ / _` | | | \n"; print " | _| | | (_| | |_____| | (_| | | | | | | (_| | | | \n"; print " |_| |_|_|\\__,_| \\__, |_| |_| |_|\\__,_|_|_| \n"; print " |_| \n"; print chr(27)."[00m"; print "\n"; print chr(27)."[01;31m\t\t\tEl Super fila-qmail v0.2 da $CID\n\n".chr(27)."[00m"; print "\n"; print chr(27)."[01;34mOpcoes possiveis sao:\n".chr(27)."[00m"; print " -l : lista todas as mensagens nas filas remota e local.\n"; print " -L : lista apenas mensagens na fila local.\n"; print " -R : lista apenas mensagens na fila remota.\n"; print " -s : mostra a saida do qmail-qstat.\n"; print " -vN : mostra o conteudo da mensagem numero N.\n"; print " -dN : apaga a mensagem numero N da fila.\n"; print " -D : deleta todas as mensagens nas filas remota e local.\n"; print "\n"; print chr(27)."[01;35mOpcoes adicionais:\n".chr(27)."[00m"; print " -c : NAO imprime cores - ideal para criar scripts.\n"; print " -N : lista apenas o numero das mensagens nas respectivas filas.\n"; print " (pode ser usado com -l, -L ou -R - ex: fila-qmail -N -l)\n\n"; print "NOTA: E' possivel executar varios comandos simultaneamente.\n\n"; exit 1; }