initial commit of file from CVS for smeserver-unjunkmgr on Sat Sep 7 21:11:06 AEST 2024

This commit is contained in:
Trevor Batley 2024-09-07 21:11:06 +10:00
parent 14a99ec4d0
commit 320f4a90e3
24 changed files with 2484 additions and 2 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.rpm
*.log
*spec-20*
*.tgz

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
# Makefile for source rpm: smeserver-unjunkmgr
# $Id: Makefile,v 1.1 2020/11/16 11:07:56 brianr Exp $
NAME := smeserver-unjunkmgr
SPECFILE = $(firstword $(wildcard *.spec))
define find-makefile-common
for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done
endef
MAKEFILE_COMMON := $(shell $(find-makefile-common))
ifeq ($(MAKEFILE_COMMON),)
# attept a checkout
define checkout-makefile-common
test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2
endef
MAKEFILE_COMMON := $(shell $(checkout-makefile-common))
endif
include $(MAKEFILE_COMMON)

View File

@ -1,3 +1,16 @@
# smeserver-unjunkmgr # <img src="https://www.koozali.org/images/koozali/Logo/Png/Koozali_logo_2016.png" width="25%" vertical="auto" style="vertical-align:bottom"> smeserver-unjunkmgr
SMEServer Koozali developed git repo for smeserver-unjunkmgr smecontribs SMEServer Koozali developed git repo for smeserver-unjunkmgr smecontribs
## Wiki
<br />https://wiki.koozali.org/Unjunkmgr
<br />https://wiki.koozali.org/Sme-unjunkmgr
## Bugzilla
Show list of outstanding bugs: [here](https://bugs.koozali.org/buglist.cgi?component=smeserver-unjunkmgr&product=SME%20Contribs&query_format=advanced&limit=0&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=CONFIRMED)
## Description
<br />*This description has been generated by an LLM AI system and cannot be relied on to be fully correct.*
*Once it has been checked, then this comment will be deleted*
<br />

1
contriborbase Normal file
View File

@ -0,0 +1 @@
contribs10

63
createlinks Normal file
View File

@ -0,0 +1,63 @@
#!/usr/bin/perl -w
use esmith::Build::CreateLinks qw(:all);
# our event specific for updating with yum without reboot
$event = 'smeserver-unjunkmgr-update';
#add here the path to your templates needed to expand
#see the /etc/systemd/system-preset/49-koozali.preset should be present for systemd integration on all you yum update event
foreach my $file (qw(
/etc/systemd/system-preset/49-koozali.preset
/etc/httpd/conf/httpd.conf
/etc/crontab
))
{
templates2events( $file, $event );
}
#action needed in case we have a systemd unit
event_link('systemd-default', $event, '10');
event_link('systemd-reload', $event, '50');
#action specific to this package
#event_link('action', $event, '30');
#services we need to restart
safe_symlink('restart',"root/etc/e-smith/events/$event/services2adjust/httpd-e-smith");
#and Server Manager panel link
#panel_link('somefunction', 'manager');
use esmith::Build::CreateLinks qw(:all);
# Start and stop links
#--------------------------------------------------
# functions for manager panel
#--------------------------------------------------
my $panel = "manager";
panel_link( "unjunkmgr", $panel );
# Events links
my $event = 'unjunkmgr-conf';
# Templates
foreach (qw(
/etc/httpd/conf/httpd.conf
/etc/crontab
))
{
templates2events( "$_", qw(
unjunkmgr-conf
console-save
bootstrap-console-save
)
);
}
# Actions
# Services
safe_symlink( "sigusr1",
"root/etc/e-smith/events/$event/services2adjust/httpd-e-smith" );

View File

@ -0,0 +1 @@
yes

View File

@ -0,0 +1 @@
no

View File

@ -0,0 +1 @@
yes

View File

@ -0,0 +1 @@
enabled

View File

@ -0,0 +1 @@
central.swerts-knudsen.dk

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
yes

View File

@ -0,0 +1,28 @@
{
use esmith::ConfigDB;
my $dbh = esmith::ConfigDB->open()
|| die "Unable to open configuration dbase.";
my %sa_conf = $dbh->get('unjunkmgr')->props;
while ( my ( $parameter, $value ) = each(%sa_conf) ) {
if ( $parameter eq 'enabled' ) {
$enabled = $value;
}
if ( $parameter eq 'useremails' ) {
$user_emails = $value;
}
}
$OUT = "";
if ( uc($enabled) eq 'YES' ) {
$OUT .= "# Schedule the UnJunk every 5 minutes\n";
$OUT
.= "0-59/5 * * * * root /usr/local/unjunkmgr/spamchanger.pl -file=/tmp/unjunk.file\n";
$OUT .= "\n";
$OUT
.= "# Schedule the weekly Blocked Junk Summary to arrive at 1PM Friday\n";
$OUT .= "0 13 * * 5 root /usr/local/unjunkmgr/spamreminder.pl\n";
}
}

View File

@ -0,0 +1,33 @@
{
use esmith::ConfigDB;
my $dbh = esmith::ConfigDB->open()
|| die "Unable to open configuration dbase.";
my %sa_conf = $dbh->get('unjunkmgr')->props;
while ( my ( $parameter, $value ) = each(%sa_conf) ) {
if ( $parameter eq 'enabled' ) {
$enabled = $value;
}
}
$OUT = "";
if ( uc($enabled) eq 'YES' ) {
my $random_hour = 5 + int( rand(50) );
$OUT .= "# Hourly Antivirus stats (random minute)\n";
$OUT .= $random_hour . " * * * * root /usr/local/unjunkmgr/spamfilter-statsclient.pl viruswebstats hour\n";
$OUT .= "# The last 24 hour Antivirus stats (random minute)\n";
$OUT .= $random_hour . " * * * * root /usr/local/unjunkmgr/spamfilter-statsclient.pl viruswebstats day\n";
$OUT .= "# Hourly Spam stats (random minute)\n";
$OUT .= $random_hour . " * * * * root /usr/local/unjunkmgr/spamfilter-statsclient.pl spamwebstats hour\n";
$OUT .= "# The last 24 hour Spam stats (random minute)\n";
$OUT .= $random_hour . " * * * * root /usr/local/unjunkmgr/spamfilter-statsclient.pl spamwebstats day\n";
$OUT .= "\n";
$OUT .= "# Now get the MRTG stats run\n";
$OUT .= $random_hour . ' * * * * root /usr/bin/mrtg /etc/mrtg/unjunkstats.cfg >/dev/null 2>&1';
$OUT .= "\n";
}
}

View File

@ -0,0 +1,46 @@
{
use esmith::NetworksDB;
use esmith::ConfigDB;
my $ndb = esmith::NetworksDB->open_ro();
$localAccess = $ndb->local_access_spec();
$localAccess =~ s#/255\.255\.255\.255##g;
my $dbh = esmith::ConfigDB->open() || die "Unable to open configuration dbase.";
my %sa_conf = $dbh->get('unjunkmgr')->props;
while (my ($parameter,$value) = each(%sa_conf)) {
if ($parameter eq 'LocalOnly') {
$local = $value;
}
if ($parameter eq 'enabled') {
$enabled = $value;
}
}
$OUT = "";
if (not (uc($enabled) eq 'YES')) {
return;
}
$OUT .= "# This is the location of the UnJunk web interface\n";
$OUT .= "AddHandler cgi-script .pl\n";
$OUT .= "Alias /unjunkmgr /usr/local/unjunkmgr\n";
$OUT .= "<Directory /usr/local/unjunkmgr>\n";
$OUT .= " Options +FollowSymLinks +ExecCGI\n";
$OUT .= " AllowOverride All\n";
$OUT .= " <FilesMatch \.php\$\>\n";
$OUT .= " SetHandler \"proxy:unix:/var/run/php-fpm/php$version.sock|fcgi://localhost\"\n";
$OUT .= " </FilesMatch>\n";
if (uc($local) eq 'YES') {
$OUT .= " Require ip $localAccess\n";
} else {
$OUT .= " Require all granted\n";
}
$OUT .= "</Directory>\n";
}

View File

@ -0,0 +1,28 @@
#!/usr/bin/perl
#----------------------------------------------------------------------
# heading : Administration
# description : Unjunkmgr
# navigation : 4000 4200
#----------------------------------------------------------------------
use strict;
use CGI ':all';
use CGI::Carp qw(fatalsToBrowser);
BEGIN {
$ENV{'PATH'} = '/bin:/usr/bin:/sbin';
$ENV{'SHELL'} = '/bin/bash';
delete $ENV{'ENV'};
}
my $q = new CGI;
my $content
= "0; url=https://" . $ENV{'HTTP_X_FORWARDED_HOST'} . "/unjunkmgr";
$q->default_dtd('-//W3C//DTD XHTML 1.0 Transitional//EN');
print $q->header('text/html');
print $q->start_html(
-head => meta( { -http_equiv => 'refresh', -content => $content } ) );
print $q->end_html;

View File

@ -0,0 +1,40 @@
workdir: /usr/local/unjunkmgr/
interval: 60
#---------------------------------------------------------------
Target[spam]: `/usr/local/unjunkmgr/spamfilter-statsclient.pl spam hour`
AddHead[spam]: <link rel="stylesheet" type="text/css" href="unjunkmgr.css">
MaxBytes[spam]: 100000
Options[spam]: gauge,nopercent,dorelpercent,nobanner,nolegend
Title[spam]: Spam Statistics
PageTop[spam]: <H1> Email Spam Statistics</H1>
WithPeak[spam]: dwmy
YLegend[spam]: messages
ShortLegend[spam]: messages
LegendI[spam]: &nbsp;Spam:
LegendO[spam]: &nbsp;Scanned:
Legend1[spam]: &nbsp;Spam:
Legend2[spam]: &nbsp;Scanned:
Legend3[spam]:
Legend4[spam]:
Legend5[spam]: &nbsp;Spam Percent:
Colours[spam]: RED#FF0000,DARK GREEN#23D016,WHITE#FFFFFF,WHITE#FFFFFF,RED#FF0000
Target[virus]: `/usr/local/unjunkmgr/spamfilter-statsclient.pl virus hour`
AddHead[virus]: <link rel="stylesheet" type="text/css" href="unjunkmgr.css">
MaxBytes[virus]: 100000
Options[virus]: gauge,nopercent,dorelpercent,nobanner,nolegend
Title[virus]: Virus Statistics
PageTop[virus]: <H1> Email Virus Statistics</H1>
WithPeak[virus]: dwmy
YLegend[virus]: messages
ShortLegend[virus]: messages
LegendI[virus]: &nbsp;Virus:
LegendO[virus]: &nbsp;Scanned:
Legend1[virus]: &nbsp;Virus:
Legend2[virus]: &nbsp;Scanned:
Legend3[virus]:
Legend4[virus]:
Legend5[virus]: &nbsp;Virus Percent:
Colours[virus]: RED#FF0000,DARK GREEN#23D016,WHITE#FFFFFF,WHITE#FFFFFF,RED#FF0000

View File

@ -0,0 +1,443 @@
<?php
include ("jpgraph/jpgraph.php");
include ("jpgraph/jpgraph_pie.php");
// These are called from the HTML further down due to the way Jpgraph renders
// Check for a type. This is not called on the first run through
$parm1 = $_GET['type'];
// For tests
// printf("TYPE=%s - %s\n",$type,$parm1);
// So we only run these if we are called with a type. Which is what happens in the html further down.
if ($parm1 == 'spamhour') {
read_and_print_spam_log('/usr/local/unjunkmgr/spamfilterstats.spam.hour', 'last hour', 'graph');
} elseif ($parm1 == 'spamday') {
read_and_print_spam_log('/usr/local/unjunkmgr/spamfilterstats.spam.day', 'last 24 hours', 'graph');
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Corporate Virus and Spam Statistics</title>
<meta http-equiv="Refresh" content="300">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="Mon, 19 Aug 2002 07:47:20 GMT">
<link rel="stylesheet" type="text/css" href="unjunkmgr.css">
</head>
<body>
<center>
<table border="3" width="940">
<tr>
<td align="center">
<h1>Corporate Spam and Virus Statistics</h1><br>
</td>
</tr>
<tr>
<td>
<table border="0" width="920" align="center">
<tr>
<!-- Top row 24 hours Spam & Virus stats -->
<td width="25%" align="center">
<h2 style="word-spacing: 0; margin-top: 0; margin-bottom: 0" align="center">Hourly Spam Statistics over last 24 hours</h2>
<h2 style="word-spacing: 0; margin-top: 0; margin-bottom: 0"><a href="spam.html"><img border="1" src="spam-day.png" width="450" height="121"></a></h2>
</td>
<td width="25%" align="center">
<h2 style="word-spacing: 0; margin-top: 0; margin-bottom: 0" align="center">Hourly Virus Statistics over last 24 hours</h2>
<h2 style="word-spacing: 0; margin-top: 0; margin-bottom: 0"><a href="virus.html"><img border="1" src="virus-day.png" width="450" height="121"></a></h2>
</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<!-- Second row last hour/24 hours Spam Pie charts-->
<!-- this is where we call the script again to draw the Pie Charts-->
<td align="center"><img src="index.php?type=spamhour"></td>
<td align="center"><img src="index.php?type=spamday"></td>
</tr>
<tr><td colspan=2>&nbsp;</td></tr>
<!-- This does the Virus stats charts -->
<tr>
<td align="center">
<?php
read_and_print_virus_log('/usr/local/unjunkmgr/spamfilterstats.virus.hour', 'hour', 'table'); ?>
</td>
<td align="center">
<?php
read_and_print_virus_log('/usr/local/unjunkmgr/spamfilterstats.virus.day', '24 hours', 'table'); ?>
</td>
</tr>
<tr><td colspan=2>&nbsp;</td></tr>
<!-- This does the Spam pie charts -->
<tr>
<td align="center">
<?php //read_and_print_spam_log('/usr/local/unjunkmgr/spamfilterstats.spam.hour', 'hour', 'table');
?>
</td>
<td align="center">
<?php //read_and_print_virus_log('/usr/local/unjunkmgr/spamfilterstats.spam.day', '24 hours', 'table');
?>
</td>
</tr>
<tr><td colspan=2>&nbsp;</td></tr>
</table>
</td>
</tr>
</table>
</center>
</body>
</html>
<?php
function read_and_print_virus_log($log_filename, $text, $style)
{
if (!file_exists($log_filename) || !is_readable($log_filename) || !$fd = fopen($log_filename, "r")) {
print ('<td width="10%" align="left"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2" colspan=2>');
print ('Statistics database not found');
print ('</td>');
return;
}
//print ('get data virus_log');
flock($fd, 1); // get a shared lock
$linecount = 0;
while (!feof($fd)) {
$line = fgets($fd);
if ($line) {
// printf("line = %s\n",$line);
// $keys = split ("\|", $line) ;
$keys = preg_split("/\|/", $line);
$list = array();
foreach ($keys as $item) {
// $key = split ("\=", $item);
$key = preg_split("/\=/", $item);
// printf("key = %s\n",$key);
if ($key[0] == 'SCANNED') {
$scanned = $key[1];
}
if ($key[0] == 'BAD') {
$bad = $key[1];
}
if ($key[0] == 'NAME') {
$name = $key[1];
} elseif ($key[0] == 'COUNT') {
$count = $key[1];
$list['count'] = $count;
} elseif ($key[0] == 'LATEST') {
$latest = $key[1];
$list['latest'] = $latest;
}
}
if ($name) {
$list = array(
'name' => $name,
'count' => $count,
'latest' => $latest
);
$print_list[$linecount++] = $list;
}
}
}
flock($fd, 3); // release the lock
fclose($fd);
// Now print out results if they exist
if (isset($print_list)) {
$sorted_list = msort($print_list, "count", false);
}
// print_r($sorted_list);
print ('<table border="2" align="center"width="98%">');
// This will always be table - used to have 'oldstyle'
if ($style == 'table') {
print ('<tr><td width="100%" align="center" valign="top" colspan=2 bgcolor=#888888>');
print ('<H2 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
printf("Virus Threats last %s ", $text);
print ('</td>');
print ('<tr><td width="70%" align="left" valign="top"><H3 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
printf("Emails Scanned for Virus");
print ('</td>');
print ('<td align="right"><H3 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
printf("%s", number_format($scanned, 0, 0, '.'));
print ('</td>');
print ('</tr>');
print ('<tr><td width="50%" align="left"><H3 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
printf("Infected with Virus");
print ('</td>');
print ('<td align="right"><H3 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
if ($scanned > 0) printf("%s (%.2f %%)", number_format($bad, 0, 2, '.') , number_format(($bad / $scanned) * 100, 2, '.', ''));
print ('</td>');
print ('</tr>');
}
if (isset($sorted_list)) {
// This will print emails if there are any
if ($linecount > 0) {
$linecount = 0;
print ('<tr><H3><td width="100%" align="left" valign="top" colspan=2 bgcolor=#888888>');
print ('<H4 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
// Always table ??
if ($style == 'table') {
print ('Top Blocked Virus Threats');
} else {
printf("Top Spammed Emails last %s", $text);
}
print ('</td>');
print ('</tr>');
print ('<tr><td width="70%" align="left" bgcolor=#BBBBBB><H5 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
// Always table ??
if ($style == 'table') {
print ('Name');
} else {
print ('Email address');
}
print ('</td>');
print ('<td align="right" bgcolor=#BBBBBB><H5 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
print ('Count');
print ('</td>');
print ('</tr>');
foreach ($sorted_list as $item) {
print ('<tr><td width="70%" align="left"><H5 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
printf("%s", $item['name']);
print ('</td>');
print ('<td align="right"><H5 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
printf("%s", $item['count']);
print ('</td>');
print ('</tr>');
$linecount++;
// Only show top 10...
if ($linecount >= 10) {
break;
}
}
}
} // End isset
print ('</table>');
return;
}
function read_and_print_spam_log($log_filename, $text, $style)
{
if (!file_exists($log_filename) || !is_readable($log_filename) || !$fd = fopen($log_filename, "r")) {
print ('<td width="10%" align="left"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2" colspan=2>');
print ('Statistics database not found');
print ('</td>');
return;
}
//print ('get data spam_log');
flock($fd, 1); // get a shared lock
$linecount = 0;
while (!feof($fd)) {
$line = fgets($fd);
if ($line) {
// printf("line = %s\n",$line);
// $keys = split ("\|", $line) ;
$keys = preg_split("/\|/", $line);
$list = array();
foreach ($keys as $item) {
// $key = split ("\=", $item);
$key = preg_split("/\=/", $item);
// printf("key = %s\n",$key);
if ($key[0] == 'SCANNED') {
$scanned = $key[1];
}
if ($key[0] == 'REJECT') {
$reject = $key[1];
}
if ($key[0] == 'TAGGED') {
$tagged = $key[1];
}
}
}
}
flock($fd, 3); // release the lock
fclose($fd);
//if ($style == 'oldstyle') {
// print('<table border="2" align="center" width="98%">');
//
// print('<tr><td width="70%" align="left"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
// printf("Scanned for Spam");
// print('</td>');
// print('<td align="right"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
// printf("%s",number_format($scanned,0,0,'.'));
// print('</td>');
// print('</tr>');
//
// print('<tr><td width="70%" align="left" valign="top"><H3 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
// printf("Good");
// print('</td>');
// print('<td align="right"><H3 style="word-spacing: 0; margin-top: 0; margin-bottom: 2; margin-left: 2">');
// $good_emails = $scanned-($reject+$tagged);
// printf("%s (%.2d %%)",number_format($good_emails,0,0,'.'),($good_emails/$scanned)*100);
// print('</td>');
// print('</tr>');
//
//
// print('<tr><td width="50%" align="left"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
// printf("Rejected as Spam");
// print('<td align="right"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
// printf("%s (%.2d %%)",number_format($reject,0,0,'.'),($reject/$scanned)*100);
// print('</td>');
// print('</tr>');
//
// print('<tr><td width="50%" align="left"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
// printf("Accepted but Tagged as Spam");
// print('<td align="right"><H3 style="word-spacing: 0; margin-top: 2; margin-bottom: 2; margin-left: 2">');
// printf("%s (%.2d %%)",number_format($tagged,0,0,'.'),($tagged/$scanned)*100);
// print('</td>');
// print('</tr>');
// print('<br>');
// print('</table>');
//
//} else {
$good_emails = $scanned - ($reject + $tagged);
// what if no emails went through??
if ($scanned == 0) {
include "jpgraph/jpgraph_canvas.php";
include "jpgraph/jpgraph_canvtools.php";
$g = new CanvasGraph(450, 260, 'auto');
$scale = new CanvasScale($g);
$scale->Set(0, 27, 0, 53);
$g->SetMargin(1, 2, 1, 2);
$g->SetColor('white');
$g->SetMarginColor("black");
$g->InitFrame();
$t = new CanvasRectangleText();
$t->SetFillColor('');
$t->SetFontColor('black');
$t->SetColor('');
$t->SetShadow('');
$t->SetFont(FF_ARIAL, FS_BOLD, 14);
$t->Set('No emails scanned for spam last hour', 8, 1, 8);
$t->Stroke($g->img, $scale);
$g->Stroke();
return;
}
// Some data
$data = array(
$good_emails,
$tagged,
$reject
);
// A new pie graph
$graph = new PieGraph(450, 260, 'auto');
// Setup title
$graph->title->Set("Spam Statistics " . $text);
$graph->title->SetFont(FF_ARIAL, FS_BOLD, 14);
$graph->title->SetMargin(3); // Add a little bit more margin from the top
$graph->footer->center->Set("Generated " . date('l jS \of F Y h:i:s A'));
// Create the pie plot
$p1 = new PiePlotC($data);
// $p1->value->SetColor("navy");
$p1->SetCenter(0.35, 0.50);
$p1->SetLegends(array(
"Good (" . number_format($good_emails, 0, 0, '.') . ")",
"Tagged (" . number_format($tagged, 0, 0, '.') . ")",
"Rejected (" . number_format($reject, 0, 0, '.') . ")",
));
$graph->legend->Pos(0.10, 0.30);
$graph->legend->SetLayout(LEGEND_VERT);
$p1->SetSliceColors(array(
"green",
"orange",
"red"
));
// Create the extra text box to show scanned emails number
// $txt = new Text();
// $txt->Set("Total scanned " . number_format($scanned,0,0,'.'));
// $txt->ParagraphAlign('right');
// $txt->Show();
// $txt->SetFont(FF_VERDANA,FS_NORMAL,8);
// $txt->Pos(0.82,0.5,'center','bottom');
// $txt->SetBox('gray9','black','gray9',0,2);
// $txt->SetShadow();
// $graph->AddText($txt);
// $p1->Explode(array(0,15,15,25,15));
// Set size of pie
$p1->SetSize(0.32);
// Label font and color setup
$p1->value->SetFont(FF_VERDANA, FS_BOLD, 10);
$p1->value->SetColor('black');
// Setup the title on the center circle
$p1->midtitle->Set("Total of\n" . number_format($scanned, 0, 0, '.') . "\nscanned");
$p1->midtitle->SetFont(FF_VERDANA, FS_NORMAL, 10);
// Set color for mid circle
$p1->SetMidColor('yellow');
// Use percentage values in the legends values (This is also the default)
$p1->SetLabelType(PIE_VALUE_PER);
// $p1->SetLabelPos(0.8);
// Add plot to pie graph
$graph->Add($p1);
// .. and send the image on it's merry way to the browser
$graph->Stroke();
//}
return;
}
function msort($array, $id = "id", $sort_ascending = true)
{
$temp_array = array();
while (count($array) > 0) {
$lowest_id = 0;
$index = 0;
foreach ($array as $item) {
if (isset($item[$id])) {
if ($array[$lowest_id][$id]) {
if ($item[$id] < $array[$lowest_id][$id]) {
$lowest_id = $index;
}
}
}
$index++;
}
$temp_array[] = $array[$lowest_id];
$array = array_merge(array_slice($array, 0, $lowest_id) , array_slice($array, $lowest_id + 1));
}
if ($sort_ascending) {
return $temp_array;
} else {
return array_reverse($temp_array);
}
}
?>

View File

@ -0,0 +1,127 @@
#!/usr/bin/perl
#############################################################################
#
# This script provides a weekly overview of email stored in the junkmail
# folder and allows for unjunking. When an email is being unjunked the
# bayesian filter in SpamAssassin is trained as ham.
#
# This script has been developed
# by Jesper Knudsen at http://sme.swerts-knudsen.dk
#
# Revision History:
#
# August 24, 2008: Initial version
#############################################################################
use Cwd "realpath";
use File::Basename;
use English;
use strict;
use CGI qw(:standard);
use File::Copy;
use File::Spec;
## If we are called as Perl Script then we are in "Move files" mode. The commandline then has the path
## to the unjunk file in the option file=<unjunk file>
if ( defined param('-file') ) {
printf( "UnJunk File = %s\n", param('-file') )
if ( defined( param('-verbose') ) );
my $LogFile = param('-file');
if ( not open( UNJUNK, "+< $LogFile" ) ) {
exit 0;
}
my @logfile = <UNJUNK>;
# Now truncate the file...
truncate( UNJUNK, 0 );
close(UNJUNK);
foreach my $email (@logfile) {
my ( $user, $file ) = $email =~ m/^USER:([^:]+):(.*)$/;
if ( defined($user) and defined($email) ) {
printf( "UnJunking file %s for user %s\n", $file, $user )
if ( defined( param('-verbose') ) );
# Now Move the file to /home/e-smith/files/users/<user>/Maildir/cur
my ( $name, $path, $suffix )
= File::Basename::fileparse( $file, '\..*' );
my $new_location
= sprintf( "/home/e-smith/files/users/%s/Maildir/cur/%s%s",
$user, $name, $suffix );
# If UnJunking a file lets learn as ham
my $result = `su - root -c "/usr/bin/sa-learn --ham $file"`;
if ( defined( param('-verbose') ) ) {
printf( "Result of sa-learn: %s\n", $result );
}
printf( "New location = %s\n", $new_location )
if ( defined( param('-verbose') ) );
if ( not rename( $file, $new_location ) ) {
printf( "Move was not successfull : %s\n", $! )
if ( defined( param('-verbose') ) );
}
}
else {
printf("Incorrect UnJunk file!!\n")
if ( defined( param('-verbose') ) );
}
}
exit 0;
}
####################################################################
## If we end here we are in the web mode and should output HTML data
####################################################################
print header; #<-- prints the http header using the CGI module
my $user = param('user');
my $from = param('from');
my $subject = param('subject');
my $email = param('email');
# Print header info
print "<HTML><HEAD><TITLE>UnJunk Manager</TITLE>\n";
printf
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/unjunkmgr/unjunkmgr.css\">\n";
print "</HEAD>\n";
if ( not defined($user) or not defined($email) ) {
print "<H2>Incorrect UnJunk Link</H2><br>\n";
exit 0;
}
my $email_msg .= sprintf "<table border=\"1\" width=\"900\"><tr>";
$email_msg .= sprintf
'<td width="600" align="left" valign="top" bgcolor="#C0C0C0" colspan=3>';
$email_msg
.= sprintf
"<b><H2>The following email has been scheduled for UnJunk</b></H2></td></tr>",
$user;
$email_msg .= sprintf
"<td bgcolor=\"#C0C0C0\"><font size=\"2\" face=\"Verdana\">From</td><td bgcolor=\"#C0C0C0\"><font face=\"Verdana\" size=\"2\">Subject</td></tr>";
$email_msg
.= sprintf
"<td><font face=\"Verdana\" size=\"2\">%-30.30s</td><td><font face=\"Verdana\" size=\"2\">%-40.40s</td></tr>",
$from, $subject;
$email_msg .= sprintf "</table>";
printf $email_msg;
my $LogFile = "/tmp/unjunk.file";
if ( not open( WRITE, ">> $LogFile" ) ) {
printf('<H2>Unable to write to the UnJunk schedular file</b></H2>');
}
else {
printf WRITE "USER:%s:%s\n", $user, $email;
close(WRITE);
}
exit 0;

View File

@ -0,0 +1,853 @@
#!/usr/bin/perl -w
#############################################################################
#
# This script provides daily SpamFilter statistics.
#
# Default configuration is:
# /sbin/e-smith/db config setprop unjunkmgr statsclient enabled
# /sbin/e-smith/db config setprop unjunkmgr statsclientport 1111
# /sbin/e-smith/db config setprop unjunkmgr statsclienthost central.swerts-knudsen.dk
# This script has been developed
# by Jesper Knudsen at http://sme.swerts-knudsen.dk
#
# Revision History:
#
# August 15, 2008: Initial version
#############################################################################
# internal modules (part of core perl distribution)
use Getopt::Long;
use Pod::Usage;
use POSIX qw/strftime floor/;
use Time::Local;
use Date::Manip;
use strict;
use Cwd "realpath";
use File::Basename;
use English;
use CGI qw(:standard);
use File::Copy;
use File::Spec;
use Data::Dumper;
use IO::Socket::INET;
use Carp;
use esmith::db;
use esmith::ConfigDB;
use esmith::AccountsDB;
#Behaving oddly, but seems to work...
if ( ( $#ARGV != 1 )
or ( $ARGV[0] !~ /spam|virus|webstats/ )
or ( ( $ARGV[1] !~ /hour|day/ ) ) )
{
die "\nUsage: $0 [spam|virus|viruswebstats|spamwebstats] [hour|day]\n";
}
my $type = $ARGV[0];
my $duration = $ARGV[1];
my $master_host_name;
my $master_host_port;
# Temp. log location
my $log_location = '/usr/local/unjunkmgr/';
# Initialize timezone
my $timezone = `date +%z`;
Date_Init("TZ=$timezone");
my ( $checked, $found ) = Corporate_Statistics($duration);
if ( not( $type eq 'viruswebstats' or $type eq 'spamwebstats' ) ) {
printf( "%s\n%s\n\n", $found, $checked );
}
exit 1;
########################################
# Process parms #
########################################
sub parse_arg {
my $startdate = shift;
my $enddate = shift;
my $secsinday = 86400;
my $time = 0;
my $start = UnixDate( $startdate, "%s" );
my $end = UnixDate( $enddate, "%s" );
if ( !$start && !$end ) {
$end = time;
$start = $end - $secsinday;
return ( $start, $end );
}
if ( !$start ) {
$start = $end - $secsinday;
return ( $start, $end );
}
if ( !$end ) {
$end = $start + $secsinday;
return ( $start, $end );
}
if ( $start > $end ) {
return ( $end, $start );
}
return ( $start, $end );
}
sub Corporate_Statistics {
my $duration = shift;
my $release_version;
my $enabled;
my $statsclient;
my $dbh = esmith::ConfigDB->open()
|| die "Unable to open configuration dbase.";
my %sa_conf = $dbh->get('unjunkmgr')->props;
while ( my ( $parameter, $value ) = each(%sa_conf) ) {
if ( $parameter eq 'enabled' ) {
$enabled = $value;
}
if ( $parameter eq 'statsclient' ) {
$statsclient = $value;
}
if ( $parameter eq 'statsclientport' ) {
$master_host_port = $value;
}
if ( $parameter eq 'statsclienthost' ) {
$master_host_name = $value;
}
}
if ( not( uc($enabled) eq 'YES' ) ) {
return ( 0, 0 );
}
%sa_conf = $dbh->get('sysconfig')->props;
while ( my ( $parameter, $value ) = each(%sa_conf) ) {
if ( $parameter eq 'ReleaseVersion' ) {
$release_version = $value;
}
}
my $SME_version;
# printf("SME Release Version %s\n",$release_version);
#if ( $release_version =~ m/[78]/ ) {
# $SME_version = 7;
#}
#else {
# $SME_version = 6;
#}
$SME_version = 10;
my $spam_reject_level;
my $spam_tag_level;
# Now get the spamassassin configuration
#if ( $SME_version == 6 ) {
#
# my $sa_dbase = '/home/e-smith/spamassassin_V3';
# my $sa_dbh = esmith::ConfigDB->open($sa_dbase)
# || die "Unable to open spamassassin configuration dbase.";
# my %sa_conf = $sa_dbh->get('conf.global')->props;
#
# my $parameter = "";
# my $value = "";
# while ( ( $parameter, $value ) = each(%sa_conf) ) {
# if ( $parameter eq 'required_hits' ) {
# $spam_tag_level = $value;
# }
# if ( $parameter eq 'auto_delete' ) {
# $spam_reject_level = $value;
# }
# if ( $parameter eq 'statsclientport' ) {
# $master_host_port = $value;
# }
# if ( $parameter eq 'statsclienthost' ) {
# $master_host_name = $value;
# }
# if ( $parameter eq 'statsclient' ) {
# if ( not( $value eq 'enabled' ) ) {
#
# # disabled - get out of here.
# return ( 0, 0 );
# }
# }
#
# }
#}
#elsif ( $SME_version == 7 ) {
$spam_reject_level
= esmith::ConfigDB->open_ro->get('spamassassin')->prop('RejectLevel');
$spam_tag_level
= esmith::ConfigDB->open_ro->get('spamassassin')->prop('TagLevel');
#}
my $logfile;
my $spamstring;
my $checkstring;
my $virusstring;
my $virusfoundstring;
my $spamclean;
# efficiency; don't rebuild the (constant) hash every loop iteration
my %month_list = (
'Jan' => 0,
'Feb' => 1,
'Mar' => 2,
'Apr' => 3,
'May' => 4,
'Jun' => 5,
'Jul' => 6,
'Aug' => 7,
'Sep' => 8,
'Oct' => 9,
'Nov' => 10,
'Dec' => 11
);
#if ( $SME_version == 7 ) {
# SME 7x
$logfile = '/var/log/qpsmtpd/current';
$spamstring = 'check_spam: Yes';
$checkstring = 'check_spam:';
$virusstring = 'clamscan results';
#}
#else {
# if ( $type eq 'virus' or $type eq 'viruswebstats' ) {
# $logfile = '/var/log/amavis-ng/amavis-ng.log';
# }
# else {
# $logfile = '/var/log/maillog';
# }
#
# $spamstring = 'identified spam';
# $spamclean = 'clean message';
# $checkstring = 'spamd: result:';
# $virusfoundstring = 'CLAMD found:';
# $virusstring = 'Starting AMaViS';
#}
my $virus_list;
my $virus_count = 0;
my $virus_scan_count = 0;
my $spam_tagged = 0;
my $spam_rejected = 0;
my $spam_checked = 0;
my $spam_email_list;
my $YEAR = ( localtime(time) )[5]; # this is years since 1900
my $start;
my $end;
if ( $duration eq 'day' ) {
( $start, $end ) = parse_arg( "yesterday", "" );
}
else {
( $start, $end ) = parse_arg( "1 hour ago", "" );
}
my $spams_found = 0;
# Its faster to pipe through grep for the right string....
if ( $type eq 'spam' or $type eq 'spamwebstats' ) {
#if ( $SME_version == 7 ) {
# if (not open(LOG,"/usr/local/bin/tai64nlocal < $logfile | /bin/grep $checkstring|")) {
if ( not open( LOG, "/usr/local/bin/tai64nlocal < $logfile |" ) ) {
printf(
"Error opening logfile (%s) for corporate spam reports - %s\n",
$logfile, $! );
return;
}
#}
#else {
# if ( not open( LOG, "/usr/local/bin/tai64nlocal < $logfile|" ) ) {
# printf(
# "Error opening logfile (%s) for corporate spam reports - %s\n",
# $logfile, $! );
# return;
# }
#}
}
else {
if ( not open( LOG, "/usr/local/bin/tai64nlocal < $logfile |" ) ) {
# if (not open(LOG,"/usr/local/bin/tai64nlocal < $logfile | /bin/grep -A 1 \'$virusstring\'|")) {
printf(
"Error opening logfile (%s) for corporate spam reports - %s\n",
$logfile, $! );
return;
}
}
my $virusmatch = 0;
foreach my $line (<LOG>) {
$line =~ s/[ \t\n]*$//;
# printf("Line = %s\n",$line);
my ( $year, $month, $day, $hour, $min, $sec, $rest );
my ( $abstime, $abshour );
#if ( $SME_version == 7 ) {
( $year, $month, $day, $hour, $min, $sec, $rest )
= $line
=~ m/^([^-]+)-([^-]+)-([^ ]+) ([^:]+):([^:]+):([^.]+).(.*)/;
if ( not defined($year) ) {
# printf("Match = %s-%s-%s - %s:%s:%s\n",$year,$month,$day,$hour,$min,$sec);
next;
}
# Convert to absolute time
$abstime = timelocal( $sec, $min, $hour, $day, $month - 1, $YEAR );
$abshour = floor( $abstime / 3600 ); # Hours since the epoch
#}
#else {
#
# # SME 6x format of log
# ( $month, $day, $hour, $min, $sec, $rest )
# = $line =~ m/^([^ ]+) ([^ ]+) ([^:]+):([^:]+):([^ ]+) (.*)/;
# if ( not defined($month) ) {
#
# # printf("Match = %s-%s - %s:%s:%s\n",$month,$day,$hour,$min,$sec);
# next;
# }
#
# # Convert to absolute time
# $abstime
# = timelocal( $sec, $min, $hour, $day, $month_list{$month},
# $YEAR );
# $abshour = floor( $abstime / 3600 ); # Hours since the epoch
#}
#If date specified, only process lines matching date
next if ( $abstime < $start );
# We can assume that logs are chronological
last if ( $abstime > $end );
if ($virusmatch) {
my ($virusname) = $line =~ m/\]\: (.*)/;
# Make sure its found and not OK
if ($virusname) {
$virusname =~ s/[ \t\n]*$//;
$virus_count++;
$virus_list->{$virusname}{'date'} = $abstime;
$virus_list->{$virusname}{'count'}++;
# printf("Found Virus = \"%s\"\n",$virusname);
}
$virusmatch = 0;
}
# Count Virus Results
my $virusname;
# I am not sure about this - I thkink it can go
#if ( $SME_version == 6 and $line =~ m/$virusfoundstring/ ) {
#
# # Have to get the name from next line - set flag
# # printf("Match...\n");
# $virusmatch = 1;
# next;
#}
if ( $line =~ m/$virusstring/ ) {
$virus_scan_count++;
#if ( $SME_version == 7 ) {
($virusname) = $line =~ m/$virusstring\: (.*)/;
# Make sure its found and not OK
if ( defined($virusname) and not $virusname =~ m/: OK/ ) {
# Lets not count various errors from Clam...
if ( lc($virusname) =~ m/warning/
or lc($virusname) =~ m/error/ )
{
next;
}
# Get rid of trailing spaces...
$virusname =~ s/[ \t\n]*$//;
$virus_count++;
$virus_list->{$virusname}{'date'} = $abstime;
$virus_list->{$virusname}{'count'}++;
# printf("Found Virus = \"%s\"\n",$virusname);
}
#}
}
# Now count checked Spam
if ( $line =~ m/$checkstring/ ) {
$spam_checked++;
}
# Find emails hit by spam
if ( $line =~ m/logging::logterse plugin:/ ) {
# printf("Found logterse entry = %s\n",$line);
my @logentry = split( /\t/, $line );
# printf("Found entry6 = %s\n",$logentry[6]);
# printf("Found entry7 = %s\n",$logentry[7]);
# printf("Found entry8 = %s\n",$logentry[8]);
# Check if it got to SA scanning
if ( defined( $logentry[8] ) ) {
# Now get email address if it was spam/rejected
if ( $logentry[8] =~ m/^Yes/ or $logentry[6] =~ m/^90/ ) {
my @emails = split( /,/, $logentry[4] );
# printf("Reject Reason = %s - %s\n",$logentry[8],$logentry[4]);
# Count rejected emails that didn't reach max score but was
# rejected before queuing.
if ( not( $logentry[8] =~ m/^Yes/ )
and $logentry[6] =~ m/^90/ )
{
$spam_rejected++;
$spams_found++;
$spam_checked++;
}
foreach my $email (@emails) {
$email =~ s/[<>]//g;
# printf("Email = \"%s\"\n",$email);
$spam_email_list->{$email}++;
}
}
}
}
# Match identified Spam
if ( $line =~ m/$spamstring/ ) {
$spams_found++;
# printf("Line = %s\n",$line);
my ( $score, $taglevel, $rest );
if ( $SME_version == 6 ) {
( $score, $taglevel, $rest )
= $line =~ m/identified spam \(([^\/]+)\/([^\)]+)\)(.*)/;
}
elsif ( $SME_version == 7 ) {
( $score, $taglevel, $rest )
= $line =~ m/hits=([^\,]+)\, required=([^\,]+)\,(.*)/;
}
if ( defined($score) ) {
# printf("Score = %s (%s)\n",$score,$taglevel);
if ( $score > $spam_tag_level
and $score < $spam_reject_level )
{
$spam_tagged++;
}
elsif ( $score > $spam_reject_level ) {
$spam_rejected++;
}
else {
# this can only happen if configuration were changed in the last period - discard them..
# printf("Configuration changed in last period - discarding item.....\n");
}
}
}
}
# printf ("Spam Emails found = %s out of %s emails\n",$spams_found,$spam_checks);
close(LOG);
# printf(Dumper($spam_email_list));
if ( $type eq 'viruswebstats' ) {
my $LogFile = sprintf( "%s/spamfilterstats.virus.%s", $log_location,
$duration );
if ( open( WRITE, "+> $LogFile" ) ) {
# printf WRITE ("START=%s|",$start);
# printf WRITE ("END=%s|",$end);
printf WRITE ( "SMEVERSION=%s|", $release_version );
printf WRITE ( "SCANNED=%s|", $virus_scan_count );
printf WRITE ( "BAD=%s|", $virus_count );
foreach my $virus ( keys %{$virus_list} ) {
# First write virus name
printf WRITE ( "NAME=%s|", $virus );
printf WRITE ( "COUNT=%s|",
$virus_list->{$virus}->{'count'} );
printf WRITE ( "LATEST=%s|",
$virus_list->{$virus}->{'date'} );
printf WRITE ("\n");
}
close(WRITE);
}
# Now send the statistics to the Statistice Server
if ( $duration eq 'hour' and uc($statsclient) eq 'ENABLED' ) {
ConnectAndSend( CreateStatisticsMsg('virus') );
}
}
if ( $type eq 'spamwebstats' ) {
my $LogFile = sprintf( "%s/spamfilterstats.spam.%s", $log_location,
$duration );
if ( open( WRITE, "+> $LogFile" ) ) {
# printf WRITE ("START=%s|",$start);
# printf WRITE ("END=%s|",$end);
printf WRITE ( "SMEVERSION=%s|", $release_version );
printf WRITE ( "SCANNED=%s|", $spam_checked );
printf WRITE ( "REJECT=%s|", $spam_rejected );
printf WRITE ( "TAGGED=%s|", $spam_tagged );
foreach my $email ( keys %{$spam_email_list} ) {
# First write virus name
printf WRITE ( "NAME=%s|", $email );
printf WRITE ( "COUNT=%s|", $spam_email_list->{$email} );
printf WRITE ("\n");
}
close(WRITE);
}
# Now send the statistics to the Statistice Server
if ( $duration eq 'hour' and uc($statsclient) eq 'ENABLED' ) {
ConnectAndSend( CreateStatisticsMsg('spam') );
}
}
if ( $type eq 'virus' ) {
return ( $virus_scan_count, $virus_count );
}
elsif ( $type eq 'spam' ) {
return ( $spam_checked, $spams_found );
}
else {
return ( 0, 0, 0 );
}
}
sub CreateStatisticsMsg {
my $stattype = shift;
# General
my $duration = 'hour';
my $encryption = 'NO';
my $compression = 'NO';
my $version = 1;
# Prepare Header
my $msg
= sprintf(
"COMMAND=LOGGING\nTYPE=%s\nCOMPRESSION=%s\nENCRYPTION=%s\nVERSION=%s\n",
uc($stattype), $compression, $encryption, $version );
$msg .= 'DATA=';
# printf("MSGHDR=%s\n",$msg);
my $LogFile = sprintf( "%s/spamfilterstats.%s.%s",
$log_location, $stattype, $duration );
if ( open( LOG, "< $LogFile" ) ) {
foreach my $line (<LOG>) {
$msg .= $line;
}
close(LOG);
}
# printf("MSG=%s",$msg);
return $msg;
}
sub ConnectAndSend() {
my $command = shift;
my $return_value = undef;
my $retry_attempts = 3;
my $socket
= job_start_client( $master_host_name, $master_host_port, 120 );
if ( not defined($socket) ) {
return $return_value;
}
TRY_AGAIN:
# printf("Initiating Contact with statistics server \"%s\" (%s)\n",$master_host_name,$retry_attempts);
my $message = sprintf( "ACCOUNT=%s\|\n", GetMacAddress() );
# printf("Tx Msg = %s",$message);
job_send_data( $socket, $message );
my $msg = <$socket>;
if ( not defined($msg) ) {
if ( $retry_attempts-- > 0 ) {
# Wait a little while before trying again if system seems ill....
sleep( int( rand(10) ) );
# debug(5,"Need a retry - %s retries left,",$retry_attempts);
goto TRY_AGAIN;
}
else {
job_stop_client($socket);
# printf("Timed out connecting to master");
return $return_value;
}
}
my $deserialized = deserialize_cmd($msg);
$msg = $deserialized;
$msg =~ s/[\r\n]*$//;
# printf("Rx Msg = %s\n",$msg);
my ( $status, $rest ) = $msg =~ m/STATUS=([^\|]+)\|(.*)/;
if ( not( $status eq 'OK' ) ) {
job_stop_client($socket);
# printf("Received not OK message from master (%s)",$msg);
return $return_value;
}
$message = sprintf( "%s\n", $command );
# printf("Message = %s",$message);
job_send_data( $socket, $message );
$msg = <$socket>;
if ( not defined($msg) ) {
job_stop_client($socket);
# printf("Timed out connecting to master to send command");
return $return_value;
}
$deserialized = deserialize_cmd($msg);
$msg = $deserialized;
$msg =~ s/[\r\n]*$//;
# printf("Rx Cmd Msg = %s\n",$msg);
( $status, $rest ) = $msg =~ m/STATUS=([^\|]+)\|(.*)/;
if ( not( $status eq 'OK' ) ) {
job_stop_client($socket);
# printf("Received not OK message from master (%s)\n",$msg);
return $return_value;
}
job_stop_client($socket);
# printf("Statistics uploaded successfully\n");
return 1;
}
sub GetMacAddress {
# This could fail
my $ifconfigin = `/sbin/ifconfig eth0`;
my @ifeth = split( /\n/, $ifconfigin );
my ( $junk, $macaddr ) = split( / HWaddr /, $ifeth[0] );
# remove the ":" and spaces
$macaddr =~ s/[\: ]//g;
# printf "MAC=\"$macaddr\"\n";
return $macaddr;
}
################################################################################
################################################################################
sub job_start_client {
my $master_host_name = shift;
my $master_host_port = shift;
my $timeout = shift;
my $silent = shift;
$timeout = 60 if ( not defined($timeout) );
$silent = 0 if ( not defined($silent) );
my $arp_refresh = 1;
if ($arp_refresh) {
# Suspecting ARP cache timeout and subsequent connect failures as cause for
# failures with "No Route to Host" error message
# Hack it by massaging the ARP into clients ARP table with 3 pings.
# debug(2,"JOB: Doing ping to host %s to refresh ARP cache...",$master_host_name);
my $system_cmd = sprintf( "ping -c 3 %s > %s 2>&1",
$master_host_name, "/dev/null" );
my $execute = `$system_cmd`;
if ( $? == -1 ) {
# debug(2,"ARP ping failed to exec: %s", $!);
}
elsif ( $? >> 8 ) {
# debug(2,"ARP ping exited with value %d\n", $? >> 8);
}
}
my $socket = IO::Socket::INET->new(
PeerAddr => $master_host_name,
PeerPort => $master_host_port,
Proto => "tcp",
Type => SOCK_STREAM,
Timeout => $timeout
);
if ( not defined($socket) ) {
if ( not $silent ) {
# printf("Couldn't connect to Job Master = %s (TCP Port=%s) : %s\n",$master_host_name ,$master_host_port,$!);
}
return undef;
}
else {
# Set timeout for socket
$socket->timeout($timeout);
}
return $socket;
}
################################################################################
################################################################################
sub job_stop_client {
my $client = shift;
close($client) if ( defined($client) );
}
################################################################################
# The function serializes a complex datastructure into a safe and compact
# single line string.
################################################################################
sub serialize_cmd {
my $cmd = shift;
# replace all newlines, CR and % with CGI-style encoded sequences
$cmd =~ s/([%\r\n])/sprintf("%%%02X", ord($1))/ge;
return $cmd;
}
################################################################################
# The function deserializes the input string into a complex datastructure.
################################################################################
sub deserialize_cmd {
my $serialized = shift;
# convert back escapes to the original chars
$serialized =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/ge;
return $serialized;
}
################################################################################
################################################################################
sub job_send_data {
my $socket = shift;
my $message = shift;
my $status = 0;
if ( defined($socket) ) {
my $msg = serialize_cmd($message);
my $r = print $socket "$msg\n";
# printf("Socket write error msg \'%s\' - %s",$msg,$!) if (!defined($r));
$status = 1;
}
return $status;
}
################################################################################
################################################################################
sub job_receive_data {
my $socket = shift;
my $timeout = shift;
$timeout = 1 if ( not defined($timeout) );
my $msg = undef;
if ( defined($socket) ) {
# Set timeout for socket
$socket->timeout($timeout);
# Now wait timeout time for someone to send something
my $client = $socket->accept();
if ( defined($client) ) {
$msg = <$client>;
my $deserialized = deserialize_cmd($msg);
return ( $client, $deserialized );
}
}
return undef;
}

View File

@ -0,0 +1,396 @@
#!/usr/bin/perl -w
#############################################################################
#
# This script provides weekly overview of email stored in the junkmail
# folder and allows for unjunking. When an email is being unjunked the
# baysian filter in SpamAssassin is trained as ham.
#
# This script has been developed
# by Jesper Knudsen at http://sme.swerts-knudsen.dk
#
# Revision History:
#
# August 24, 2008: Initial version
#############################################################################
# internal modules (part of core perl distribution)
use Getopt::Long;
use Pod::Usage;
use POSIX qw/strftime floor/;
use Time::Local;
use Date::Manip;
use strict;
use MIME::Lite;
use esmith::db;
use esmith::ConfigDB;
use esmith::AccountsDB;
#############################################################################
# Configuration
#############################################################################
# The address which will be copied on the weekly summary emails (default: none)
my $admin_email_addr = '';
my $domain_name = esmith::ConfigDB->open()->get('DomainName')->value;
my $unjunkhost = $domain_name;
my $enabled;
my $useremails = 'yes';
my $db = esmith::ConfigDB->open()
|| die "Unable to open configuration dbase.";
my %db_conf = $db->get('unjunkmgr')->props;
while ( my ( $parameter, $value ) = each(%db_conf) ) {
if ( $parameter eq 'enabled' ) {
$enabled = $value;
}
if ( $parameter eq 'adminemails' and uc($value) eq 'YES' ) {
$admin_email_addr = 'admin';
}
if ( $parameter eq 'unjunkhost' ) {
$unjunkhost = $value;
}
if ( $parameter eq 'useremails' ) {
$useremails = $value;
}
}
# The address from which the weekly summary comes from
my $spamfilter_addr = 'Admin Junk Summary <UnJunkManager>';
# Which stylesheet to use for the summary email
my $css_file = '/usr/local/unjunkmgr/unjunkmgr.css';
# Debug enabled? - will send all reports to $admin_email_addr
my $debug = 0;
#############################################################################
# Parameters for the Junkmail Summary functionality
my $root_url = sprintf( "https://%s/unjunkmgr", $unjunkhost );
my $path = "/home/e-smith/files/users/";
my $end_path_cur = "/Maildir/.junkmail/cur";
my $end_path_new = "/Maildir/.junkmail/new";
my $sa_dbase = '/home/e-smith/db/configuration';
my $dbh = esmith::ConfigDB->open($sa_dbase)
|| die "Unable to open spamassassin configuration dbase.";
my %sa_conf = $dbh->get('spamassassin')->props;
my $disabled = 0;
my $days_to_keep = 5;
my $spam_mark = 7;
my $spam_discard = 10;
my $parameter = "";
my $value = "";
while ( ( $parameter, $value ) = each(%sa_conf) ) {
if ( $parameter eq 'status' and not $value eq 'enabled' ) {
$disabled = 1;
}
if ( $parameter eq 'MessageRetentionTime' ) {
$days_to_keep = $value;
}
if ( $parameter eq 'TagLevel' ) {
$spam_mark = $value;
}
if ( $parameter eq 'RejectLevel' ) {
$spam_discard = $value;
}
}
#printf("Enabled = %s\n",$disabled);
#printf("Retention = %s\n",$days_to_keep);
#printf("TagLevel = %s\n",$spam_mark);
#printf("RejectLevel = %s\n",$spam_discard);
if ( uc($useremails) eq 'YES' or lc($admin_email_addr) eq 'admin' ) {
Junkmail_Reminder();
}
#All done
exit 0;
#############################################################################
# Subroutines ###############################################################
#############################################################################
########################################
# Process parms #
########################################
sub parse_arg {
my $startdate = shift;
my $enddate = shift;
my $secsinday = 86400;
my $time = 0;
my $start = UnixDate( $startdate, "%s" );
my $end = UnixDate( $enddate, "%s" );
if ( !$start && !$end ) {
$end = time;
$start = $end - $secsinday;
return ( $start, $end );
}
if ( !$start ) {
$start = $end - $secsinday;
return ( $start, $end );
}
if ( !$end ) {
$end = $start + $secsinday;
return ( $start, $end );
}
if ( $start > $end ) {
return ( $end, $start );
}
return ( $start, $end );
}
sub get_email_details {
my $entry = shift;
my $score;
my $spam;
my $spamlimit;
my $spam_string = 'Unknown';
my $subject = 'Unknown';
my $from = 'Unknown';
my $to = 'Unknown';
open( ORIGINAL, "$entry" ); #OPEN FILE FOR READING
my @original = <ORIGINAL>; #READ FILE INTO AN ARRAY
#PROCESS THE ARRAY
foreach my $x (@original) {
if ( $x =~ m/^Subject:/ ) {
($subject) = $x =~ m/^Subject: (.*)$/;
if ( not defined($subject) ) {
$subject = 'Unknown';
}
else {
$subject =~ s/^[ \t]//g;
}
}
if ( $x =~ m/^To:/ ) {
($to)
= $x
=~ m/([a-zA-Z0-9._\%\+\-]+\@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,4})/;
if ( not defined($to) ) {
$to = 'Unknown';
}
}
if ( $x =~ m/^From:/ ) {
($from)
= $x
=~ m/([a-zA-Z0-9._\%\+\-]+\@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,4})/;
if ( not defined($from) ) {
$from = 'Unknown';
}
}
if ( $x =~ m/^X-Spam-Status:/ ) {
( $spam, $score, $spamlimit )
= $x
=~ m/^X-Spam-Status: ([^\,]+)\, hits=([^\ ]+)\ required=(.*)/;
if ( defined($spamlimit) ) {
if ( $spam eq 'Yes'
and $score > $spamlimit
and $score < $spam_discard )
{
$spam_string = sprintf( "Likely Spam (%s)", $score );
}
else {
$spam_string = sprintf( "Spam (%s)", $score );
}
}
else {
$spam_string = 'Unknown';
}
}
}
close(ORIGINAL);
return ( $subject, $from, $to, $spam_string );
}
################################################################################
# The function serializes a complex datastructure into a safe and compact
# single line string.
################################################################################
sub serialize_cmd {
my $cmd = shift;
my $out = $cmd;
# replace all newlines, CR and % with CGI-style encoded sequences
$out =~ s/([\;%\r\n])/sprintf("%%%02X", ord($1))/ge;
return $out;
}
sub Junkmail_Reminder {
my $found;
my $entry;
my $subject;
my $to;
my $from;
my $real_name;
my $name;
my $to_email;
my $score;
my $spamlist;
my $spamcount = 0;
my ( $oneweekago, $noew ) = parse_arg( "last week", "" );
my $adb = esmith::AccountsDB->open_ro()
|| die "Couldnt' open AccountsDB\n";
my @accounts;
push @accounts, $adb->users;
foreach my $account (@accounts) {
$name = $account->key;
$spamcount = 0;
$spamlist = undef;
$found = 0;
$real_name = sprintf( "%s %s",
$account->prop('FirstName'),
$account->prop('LastName') );
if ( $debug == 1 ) {
printf( "User : %s (%s)\n", $real_name, $name );
}
my @junkmail_dirs;
push @junkmail_dirs, "$path$name$end_path_new";
push @junkmail_dirs, "$path$name$end_path_cur";
foreach my $junkmail_dir (@junkmail_dirs) {
# Now get the content list for the directory.
opendir( QDIR, "$junkmail_dir" )
or die "Couldn't read directory $junkmail_dir";
my @sorted_dates = map $_->[1], sort { $b->[0] <=> $a->[0] }
map -f "$junkmail_dir/$_" ? [ ( stat _ )[9], $_ ] : (),
readdir(QDIR);
closedir(QDIR);
foreach $entry (@sorted_dates) {
next if $entry =~ /^\./;
$entry = $junkmail_dir . '/' . $entry;
my $modifytime = ( stat($entry) )[9];
# Now only report new emails..
if ( -f $entry and ( $modifytime > $oneweekago ) ) {
$found++;
( $subject, $from, $to, $score )
= get_email_details($entry);
# printf("Found Spam email: %s with score %s (%s)",$from, $score,$spamcount);
$spamlist->[$spamcount]{'user'} = $name;
$spamlist->[$spamcount]{'realname'} = $real_name;
$spamlist->[$spamcount]{'file'} = $entry;
$spamlist->[$spamcount]{'subject'} = $subject;
$spamlist->[$spamcount]{'from'} = $from;
$spamlist->[$spamcount]{'to'} = $to;
$spamlist->[ $spamcount++ ]{'score'} = $score;
}
}
}
if ( $spamcount > 0 and not $disabled ) {
my $email_msg;
$email_msg .= "<HTML><HEAD><TITLE>UnJunk Manager</TITLE>";
if ( open( CSS, "$css_file" ) ) {
my @css = <CSS>;
$email_msg .= "<style type=\"text/css\">";
foreach my $cssline (@css) {
$email_msg .= $cssline;
}
$email_msg .= "</style>";
}
$email_msg .= "</HEAD>";
$email_msg
.= sprintf "<H1>Junk Emails Blocked for %s: %s</H1><br>",
$real_name, $spamcount;
$email_msg .= sprintf
"The emails listed below have been placed in your personal Junk Box since your last Junk Box Summary and will be<br>";
$email_msg .= sprintf
"deleted after $days_to_keep days. To receive any of these messages, click UnJunk and the message will be delivered to your inbox.<br>";
$email_msg
.= sprintf "<table border=\"1\" width=\"900\"><tr>";
$email_msg .= sprintf
'<td width="600" align="left" valign="top" bgcolor="#C0C0C0" colspan=4>';
$email_msg
.= sprintf
"<b><font face=\"Verdana\"><H2>Emails sent to %s</H2></font></b></td></tr>",
$real_name;
$email_msg .= sprintf
"<td bgcolor=\"#C0C0C0\"><font size=\"2\">Action</td><td bgcolor=\"#C0C0C0\"><font size=\"2\">From</td><td bgcolor=\"#C0C0C0\"><font size=\"2\">Subject</td><td bgcolor=\"#C0C0C0\"><font size=\"2\">Threat</td></tr>";
foreach my $email ( @{$spamlist} ) {
my $spamchanger = $root_url . '/spamchanger.pl';
my $url
= sprintf "%s?user=%s&email=%s&subject=%s&from=%s",
$spamchanger, $email->{'user'}, $email->{'file'},
$email->{'subject'}, $email->{'from'};
$email_msg
.= sprintf
"<td><font size=\"2\"><a href=\"%s\">UnJunk</a></td>",
serialize_cmd($url);
$email_msg
.= sprintf
"<td><font size=\"2\">%-40.40s</td><td><font size=\"2\">%-50.50s</td><td><font size=\"2\">%s</td></tr>",
$email->{'from'}, $email->{'subject'},
$email->{'score'};
}
$email_msg .= sprintf "</table>";
# create a new MIME Lite based email
my $msg = MIME::Lite->new(
Subject =>
sprintf(
"Summary of junk emails blocked - %s Junk Emails Blocked",
$spamcount ),
From => $spamfilter_addr,
To => uc($useremails) eq 'YES'
? $name
: $admin_email_addr,
# To => $debug == 1 ? $admin_email_addr : $name,
# No cc email if debug..
# Cc => $admin_email_addr eq 'admin' ? $admin_email_addr : '',
Cc => (
$admin_email_addr eq 'admin'
and uc($useremails) eq 'YES'
) ? $admin_email_addr : '',
Type => 'text/html',
Data => $email_msg
);
$msg->send();
}
}
}

View File

@ -0,0 +1,242 @@
BODY{
background: #e6ecf8;
color: #000000;
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 12px;
height: 100%;
margin: 5px;
padding: 0px;
text-align: left;
width: 100%;
}
a { color: #000000 }
DIV.menu_bg{ z-index: 0; position: fixed; top: 0px; left: 0px; width: 162px; height: 100%; background-color: #eeeeee; }
DIV.titre{ z-index: 1; position: absolute; top: 0px; left: 0px; width: 100%; height: 68px; background: #cccccc; margin: 0px 0px 1px 0px; }
DIV.t_logo{ z-index: 1; position: absolute; top: 0px; left: 0px; }
DIV.t_barre_orange{ z-index: 1; position: absolute; top: 41px; left: 0px; width: 100%; background: #ffc50a; border-color: #888888; border-style: solid; border-width: 1px 0px 1px 0px; }
DIV.t_email{ z-index: 1; position: absolute; top: 50px; left: 0px; width: 240px; }
DIV.t_liens{ z-index: 1; position: absolute; top: 50px; right: 0px; height: 14px; text-align: right; }
DIV.t_barre_grise{ z-index: 1; position: absolute; top: 64px; left: 0px; width: 100%; background: #888888;}
DIV.menu{ z-index: 1; position: absolute; top: 68px; left: 0px; width: 160px; padding: 0px 0px 0px 2px; }
DIV.page{ z-index: 0; position: absolute; top: 68px; left: 162px; bottom: -1px; }
DIV.contenu{ padding: 0px 20px 5px 5px; }
DIV.vert{ border-color: #006600; border-width: 1px; border-style: solid; color: #006600; padding: 2px; margin: 0px 20px 0px 20px; }
DIV.rouge{ border-color: #ff0000; border-width: 1px; border-style: solid; color: #ff0000; padding: 2px; margin: 0px 20px 0px 20px; }
DIV.vert a{ color: #006600; font-weight: bold; }
DIV.rouge a{ color: #ff0000; font-weight: bold; }
FIELDSET{
background-color: #eeeeee;
}
FONT.copyleft{
color: #777777;
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 11px;
}
FORM{
margin-top: 2px;
margin-bottom: 2px;
}
H1{
color: #333333;
font-size: 18px;
margin-bottom: 4px;
margin-top: 12px;
font-family: Verdana, Arial, Helvetica, sans-serif;
}
H2{
color: #333333;
font-size: 14px;
margin-bottom: 3px;
margin-top: 12px;
font-family: Verdana, Arial, Helvetica, sans-serif;
}
H3{
color: #333333;
font-size: 12px;
/* margin-bottom: 3px;
margin-top: 12px; */
font-family: Verdana, Arial, Helvetica, sans-serif;
}
H4{
color: #333333;
font-size: 12px;
margin-bottom: 3px;
margin-top: 12px;
font-family: Verdana, Arial, Helvetica, sans-serif;
}
H5{
color: #333333;
font-size: 10px;
/* margin-bottom: 3px;
margin-top: 12px; */
font-family: Verdana, Arial, Helvetica, sans-serif;
}
HR{
color: #666666;
background-color: #666666;
height: 1px;
width: 80%;
border: 0px;
}
HR.copyleft{
color: #dddddd;
background-color: #dddddd;
height: 1px;
width: 100%;
border: 0;
}
OL, UL, LI{
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 12px;
font-weight: normal;
}
UL{
list-style-type: circle;
}
P{
margin-bottom: 2px;
margin-top: 8px;
}
PRE{
margin: 3px 0px 3px 0px;
padding: 2px;
}
TABLE{
border-collapse : collapse;
empty-cells: show;
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 12px;
background-color: #e6ecf8;
}
TABLE.bordure{
border: 2px solid #cccccc;
margin: 5px 5px 5px 2px;
}
TD{
font-size: 11px;
margin: 0px;
padding: 0px;
}
TD.header{
border: 1px solid #cccccc;
padding: 3px 2px 3px 2px;
}
TH{
font-size: 11px;
vertical-align: middle;
}
TH.bordure{
background-color: #dddddd;
border: 1px solid #cccccc;
padding: 3px 2px 3px 2px;
text-align: center;
}
TH.presse{
text-align: right;
vertical-align: top;
}
TH.bouton{
border: 1px solid #dddddd;
background-color: #eeeeee;
padding: 4px;
text-align: right;
}
TR{
vertical-align: top;
}
.cmdline{
color: #00ffff;
}
.xterm{
background-color: #000000;
border: 3px inset #999999;
color: #ffffff;
font-size: 11px;
}
.section{
font-size: 11px;
font-weight: bold;
padding-bottom: 2px;
padding-top: 8px;
}
a1:link, a1:visited, a1:hover, a1:active{
border: 1px solid #000000;
# border: 1px solid #cccccc;
color: #000000;
# margin: 0px;
# padding: 0px 0px 0px 10px;
text-decoration: none;
}
a:hover{
background: #25bd25;
border-color: #C0C0C0;
}
a:active{
background: #000000;
border-color: #000000;
color: #ffffff;
}
a.choix, a.selection{
border: 1px solid #eeeeee;
color: #000000;
display: block;
# margin: 0px 0px 0px 0px;
# padding: 0px 10px 2px 10px;
text-align: left;
text-decoration: none;
}
a.choix:hover, a.selection:hover{
background: #cccccc;
border-color: #888888;
}
a.choix:active, a.selection:active{
background: #000000;
border-color: #000000;
color: #ffffff;
}
a.selection:link, a.selection:visited, a.selection:active, a.selection:hover{
border-color: #888888;
}
a.selection:hover{
background: #ffffff;
border-color: #888888;
}

136
smeserver-unjunkmgr.spec Normal file
View File

@ -0,0 +1,136 @@
# $Id: smeserver-unjunkmgr.spec,v 1.10 2022/08/02 01:49:38 jpp Exp $
# Authority: jesperknudsen
# Name: Jesper Knudsen
Summary: SME Server UnJunk Manager
%define name smeserver-unjunkmgr
%define version 3.1
%define release 7
Name: %{name}
Version: %{version}
Release: %{release}%{?dist}
License: GPL
Group: SME Server/addon
Source: %{name}-%{version}.tar.xz
#Patch1: smeserver-unjunkmgr-bzxxxxx-fix-stuff.patch
BuildRoot: /var/tmp/%{name}-%{version}-%{release}-buildroot
BuildArch: noarch
BuildRequires: e-smith-devtools
Requires: smeserver-release >= 10.0
Requires: e-smith-apache >= 2.6.0-19
Requires: perl-MIME-Lite => 3
Requires: perl-DateManip => 5.40
Requires: mrtg
Requires: perl-Email-Date-Format
Requires: jpgraph
Obsoletes: sme-unjunkmgr
%changelog
* Sat Sep 07 2024 cvs2git.sh aka Brian Read <brianr@koozali.org> 3.1-7.sme
- Roll up patches and move to git repo [SME: 12338]
* Sat Sep 07 2024 BogusDateBot
- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday,
by assuming the date is correct and changing the weekday.
* Mon Aug 01 2022 Jean-Philippe Pialasse <tests@pialasse.com> 3.1-6.sme
- update to httpd 2.4 access syntax [SME: 12063]
thanks to Vasarhelyi Zsolt
* Sat Nov 06 2021 Jean-Philippe Pialasse <tests@pialasse.com> 3.1-5.sme
- fix unjunk links failing [SME: 11737]
* Tue Jun 01 2021 Jean-Philippe Pialasse <tests@pialasse.com> 3.1-4.sme
- fix missing curly bracket and indentation [SME: 11178]
* Thu Apr 01 2021 Brian Read <brianr@bjsystems.co.uk> 3.1-3.sme
- Add Update event to createlinks [SME: 11178]
* Thu Apr 01 2021 BogusDateBot
- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday,
by assuming the date is correct and changing the weekday.
* Tue Nov 24 2020 John Crisp <jcrisp@safeandsoundit.co.uk> 3.1-2.sme
- Add cleaned up and fixed index.php that I forgot
* Tue Nov 24 2020 John Crisp <jcrisp@safeandsoundit.co.uk> 3.1-1.sme
- Remove jpgraph to its own rpm
- cleanup of various files
- remove references to old SME versions
* Wed Nov 18 2020 Brian Read <brianr@bjsystems.co.uk> 3.0-2.sme
- Fix SME Version check in perl stats prgram [SME: 11178]
* Wed Nov 18 2020 John Crisp <jcrisp@safeandsoundit.co.uk> 3.0-1.sme
- Completely update jpgraph to 4.3.4
- clean up various files and incorporate patches [SME: 11178]
* Tue Nov 17 2020 Brian Read <brianr@bjsystems.co.uk> 2.1-5.sme
- Update jpgraph and fix php references in httpd.conf [SME: 11178]
* Mon Nov 16 2020 Brian Read <brianr@bjsystems.co.uk> 2.1-4.sme
- Initial import to SME10 tree [SME: 11178]
* Fri Apr 4 2014 stephane de Labrusse <stephdl@de-labrusse.fr> 2.1-3.sme
- added a unjunkmgr menu entry in the server-manager [sme : 8307]
* Thu Jun 27 2013 JP Pialasse <tests@pialasse.com> 2.1-2.sme
- Add Update event to createlinks some php deprecated and PHP warning
- patch1
* Wed Jun 26 2013 JP Pialasse <tests@pialasse.com> 2.1-1.sme
- update JpGraph to fix deprecated messages
- added perl-Email-Date-Format requirement for sme8 as per wiki
- patch ttfpatch
* Tue Jun 25 2013 JP Pialasse <tests@pialasse.com> 2.0-4.sme
- Add Update event to createlinks db default [SME: 7701]
- patch0
- added createlink in spec file
* Wed Jun 02 2010 Shad L. Lords <slords@mail.com> 2.0-1.sme
- Clean up spec for importing into CVS
* Thu Jun 4 2009 Jesper Knudsen <jesper@swerts-knudsen.dk> 1.1.3-1
- Added top spammed email stats in GUI and updated CSS to show nicely in IE
- Fixed problem using unjunkhost confg parameter
* Fri Apr 3 2009 Jesper Knudsen <jesper@swerts-knudsen.dk> 1.1.0-2
- Fixed error in spamreminder.pl line 112
* Thu Apr 2 2009 Jesper Knudsen <jesper@swerts-knudsen.dk> 1.1.0-1
- Updated to submit statistics more randomly over the hour to avoid congestion on central
- Add pie charts on stats
- Add more configuration options
* Thu Aug 28 2008 Jesper Knudsen <jesper@swerts-knudsen.dk> 1.0.1-1
- Updated spamreminder.pl to allow non-exisitng Subject fields
- Made dependency of MRTG for RPM
- Make sure to cleanup during un-install
* Fri Aug 22 2008 Jesper Knudsen <jesper@swerts-knudsen.dk> 1.0.0-1
- First release
%description
SME Server UnJunk Manager which scan the individual users junkmail folders and
sends out a weekly summary where they can unjunk the emails
%prep
%setup
#%patch1 -p1
%build
perl createlinks
%install
rm -rf $RPM_BUILD_ROOT
(cd root ; find . -depth -print | cpio -dump $RPM_BUILD_ROOT)
/sbin/e-smith/genfilelist $RPM_BUILD_ROOT > %{name}-%{version}-filelist
echo "%doc " >> %{name}-%{version}-filelist
%clean
rm -rf $RPM_BUILD_ROOT
%files -f %{name}-%{version}-filelist
%defattr(-,root,root)