summaryrefslogtreecommitdiffstats
path: root/cgi
diff options
context:
space:
mode:
authorAndreas Unterkircher <unki@netshadow.at>2008-12-03 20:37:13 +0100
committerAndreas Unterkircher <unki@netshadow.at>2008-12-12 18:36:55 +0100
commit0a6e4fae2c79d5f9da1033e0a51abfc69e10b8b2 (patch)
tree041b13746bede1eeceec181a8a00405e26d9db36 /cgi
parent226ad0a3c764c0606048acf7371b02765eee60d2 (diff)
downloadexilog-0a6e4fae2c79d5f9da1033e0a51abfc69e10b8b2.zip
exilog-0a6e4fae2c79d5f9da1033e0a51abfc69e10b8b2.tar.gz
exilog-0a6e4fae2c79d5f9da1033e0a51abfc69e10b8b2.tar.bz2
sort files into their directories. move agent- and cleanup-script into 'agents', all static www-content (icons, stylesheet, javascript, ...) into 'htdocs'. cgi-stuff into 'cgi' and all reused code into 'lib'.
Signed-off-by: Andreas Unterkircher <unki@netshadow.at>
Diffstat (limited to 'cgi')
-rwxr-xr-xcgi/exilog_cgi.pl124
-rw-r--r--cgi/exilog_cgi_html.pm984
-rw-r--r--cgi/exilog_cgi_messages.pm816
-rw-r--r--cgi/exilog_cgi_param.pm74
-rw-r--r--cgi/exilog_cgi_queues.pm131
-rw-r--r--cgi/exilog_cgi_servers.pm102
6 files changed, 2231 insertions, 0 deletions
diff --git a/cgi/exilog_cgi.pl b/cgi/exilog_cgi.pl
new file mode 100755
index 0000000..50344e6
--- /dev/null
+++ b/cgi/exilog_cgi.pl
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+#
+# This file is part of the exilog suite.
+#
+# http://duncanthrax.net/exilog/
+#
+# (c) Tom Kistner 2004
+#
+# See LICENSE for licensing information.
+#
+
+use strict;
+use exilog_config;
+use exilog_util;
+use exilog_cgi_html;
+use exilog_cgi_param;
+use exilog_sql;
+
+# Put user name into global variable
+my $user = $ENV{'REMOTE_USER'} || 'anonymous';
+
+_print_cgi_headers();
+_print_html_header();
+_print_html_tabs();
+_do_global_actions();
+
+print '<div class="display" align="center">';
+if ($param->{tab} eq 'queues') {
+ use exilog_cgi_queues;
+ queues();
+}
+elsif ($param->{tab} eq 'messages') {
+ use exilog_cgi_messages;
+ messages();
+}
+elsif ($param->{tab} eq 'servers') {
+ use exilog_cgi_servers;
+ servers();
+};
+print '</div>';
+
+_print_html_footer();
+
+
+# -- Private functions ---------------------------------------------------------
+
+sub _do_global_actions {
+
+ # queue actions
+ my $valid_actions = [ 'deliver', 'cancel', 'delete' ];
+ my $restricted_actions = [ 'cancel', 'delete' ];
+ foreach my $p (keys %{ $param }) {
+ if ($p =~ /^ac_([A-Za-z0-9_.-]+?)_([A-Za-z0-9]{6}\-[A-Za-z0-9]{6}-[A-Za-z0-9]{2})$/) {
+ my $server = $1;
+ my $message_id = $2;
+ my $action = $param->{$p};
+ if (ina($valid_actions,$action)) {
+ next if (ina($restricted_actions,$action) && ina($config->{web}->{restricted_users},$main::user));
+ sql_queue_set_action($server,$message_id,$action);
+ }
+ }
+ }
+
+};
+
+
+sub _print_cgi_headers {
+ print $q->header(-expires=>'Thursday, 01-Jan-1970 00:00:01 GMT',
+ -Expires=>'now',
+ -Cache-Control=>'no-cache',
+ -Cache-Control=>'no-store',
+ -Pragma=>'no-cache');
+};
+
+
+sub _print_html_header {
+ print $q->start_html({-title=>"Exilog ".$version,
+ -style=>{-src=>"exilog_stylesheet.css"},
+ -script=>[
+ {-language=>'JAVASCRIPT',
+ -src=>"exilog_jscript.js"},
+ "document.write(getCalendarStyles());"
+ ],
+ -meta=>{'http-equiv' => 'pragma', 'content' => 'no-cache'}});
+ # global "centering" div
+ print $q->start_form({-name=>"exilogform",-method=>"GET"});
+ print '<div align="center">';
+ print '<div align="center" class="body">';
+};
+
+sub _print_html_tabs {
+ my $tabs = { 'servers' => "Servers",
+ 'messages' => "Messages",
+ #'queues' => "Queues", # Queue manager is still unfinished ...
+ 'messages' => "Messages" };
+
+ my $html;
+
+ foreach my $tab (sort keys %{ $tabs }) {
+ $html .= $q->td({-class=>"tabs_spacer"},"&nbsp;").
+ (($param->{"tab"} eq $tab) ?
+ $q->td({-class=>"tabs_active"},$tabs->{$tab})
+ :
+ $q->td({-class=>"tabs_click", -onClick=>"javascript:load_tab('$tab');"},$tabs->{$tab}));
+ };
+
+ print $q->table({-class=>"tabs",-cellpadding=>2,-cellspacing=>0},
+ $q->Tr(
+ $q->td({-align=>"center",-class=>"tabs_static",-style=>"font-size: 16px; font-weight: bold;" }, "Exilog").
+ $html.
+ $q->td({-class=>"tabs_spacer"},"&nbsp;").
+ $q->td({-align=>"center",-class=>"tabs_static",-style=>"font-size: 12px; width: 240px; white-space: nowrap;" }, "&nbsp;&nbsp;Server time".(($config->{web}->{timestamps} eq 'gmt') ? " (GMT)":"").": ".stamp_to_date(time())."&nbsp;&nbsp;")
+ )
+ );
+
+ print $q->input({-type=>"hidden",-name=>"tab",-id=>"tab",-override=>1,-value=>$param->{"tab"}});
+};
+
+
+sub _print_html_footer {
+ print '</div></div>';
+ print $q->end_form();
+ print $q->end_html();
+};
diff --git a/cgi/exilog_cgi_html.pm b/cgi/exilog_cgi_html.pm
new file mode 100644
index 0000000..f42b795
--- /dev/null
+++ b/cgi/exilog_cgi_html.pm
@@ -0,0 +1,984 @@
+#!/usr/bin/perl -w
+#
+# This file is part of the exilog suite.
+#
+# http://duncanthrax.net/exilog/
+#
+# (c) Tom Kistner 2004
+#
+# See LICENSE for licensing information.
+#
+
+package exilog_cgi_html;
+use exilog_config;
+use exilog_util;
+use CGI;
+use strict;
+use Data::Dumper;
+
+BEGIN {
+ use Exporter;
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+ $VERSION = 0.1;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &render_message
+ &render_reject
+ &render_queue
+ &render_header
+ &render_server
+ &render_queue_table
+ $q
+
+ );
+
+ %EXPORT_TAGS = ();
+ @EXPORT_OK = qw();
+
+ use vars qw( $q );
+}
+
+$q = new CGI;
+
+# Renders server statistics
+# $stats->{ID}->{order}
+# ->{desc}
+# ->{icon}
+# ->{text}
+
+sub render_server {
+ my $server = shift;
+ my $num_queued = shift;
+ my $h24_stats = shift;
+
+ $q->div({-class=>"top_spacer"},
+ $q->table({-class=>"stats", -cellspacing=>1, -cellpadding=>2, -border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>2,-class=>"table_stats",-style=>"width: 300px;"},
+ $q->table({-cellpadding=>0, -cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-class=>"large_icon"},
+ $q->img({-src=>"icons/server_normal.png",-border=>0})
+ ),
+ $q->td({-class=>"large_text"},
+ $server
+ )
+ )
+ )
+ ),
+ $q->td({-class=>"table_stats"},
+ $q->table({-cellpadding=>0, -cellspacing=>0, -border=>0, -width=>"1%"},
+ $q->Tr(
+ $q->td({-rowspan=>2,-class=>"large_icon"},
+ $q->img({-src=>"icons/queue_normal.png",-border=>0,-title=>"Queue Status"})
+ ),
+ $q->td({-rowspan=>2,-class=>"large_icon"},
+ "<b>Queue Status</b>"
+ ),
+ $q->td({-class=>"stats"},
+ _item( { 'icon' => "icons/queued.png" },
+ {
+ #'link' => { 'tab' => 'queues' },
+ 'text' => ($num_queued->{deferred}+$num_queued->{frozen})." queued (".($num_queued->{deferred_bounce}+$num_queued->{frozen_bounce})." bounces)" } )
+
+ ),
+ $q->td({-class=>"stats"},
+ ( $num_queued->{deferred} ?
+ _item( { 'icon' => "icons/deferred.png" },
+ { 'text' => $num_queued->{deferred}." deferred (".$num_queued->{deferred_bounce}." bounces)" } )
+ :
+ "&nbsp;"
+ )
+ )
+ ),
+ $q->Tr(
+ $q->td({-class=>"stats"},
+ ( $num_queued->{frozen} ?
+ _item( { 'icon' => "icons/frozen.png" },
+ { 'text' => $num_queued->{frozen}." frozen (".$num_queued->{frozen_bounce}." bounces)" } )
+ :
+ "&nbsp;"
+ )
+ )
+ )
+ )
+ )
+ ),
+ $q->Tr(
+ $q->td({-class=>"table_stats"},
+ $q->table({-cellpadding=>0, -cellspacing=>0, -border=>0, -width=>"1%"},
+ $q->Tr(
+ $q->td({-rowspan=>2,-class=>"large_icon"},
+ $q->img({-src=>"icons/stats_h24.png",-border=>0,-title=>"Usage Statistics"})
+ ),
+ $q->td({-rowspan=>2,-class=>"large_icon"},
+ "<b>Last 24h stats</b>"
+ ),
+ $q->td({-class=>"stats"},
+ _item( { 'icon' => "icons/arrival.png", 'title' => "Arrivals" },
+ { 'text' => $h24_stats->{arrivals}." arrivals" } )
+ ),
+ $q->td({-class=>"stats"},
+ _item( { 'icon' => "icons/size.png", 'title' => "Average message size" },
+ { 'text' => "Average message size: ".human_size($h24_stats->{avg_msg_size}) } )
+ )
+ ),
+ $q->Tr(
+ $q->td({-class=>"stats"},
+ _item( { 'icon' => "icons/delivery.png" },
+ { 'text' => $h24_stats->{deliveries}." deliveries" } )
+ ),
+ $q->td({-class=>"stats"},
+ _item( { 'icon' => "icons/error.png" },
+ { 'text' => $h24_stats->{errors}." errors" } )
+ )
+ )
+ )
+ )
+ )
+ )
+ );
+
+};
+
+
+# Renders messages and post-DATA rejects.
+
+sub render_message {
+ my $h = shift; # main message context
+
+ # Subclass list with references to HTML generation code.
+ my $subclasses = { 'rejects' => \&_reject_html,
+ 'deferrals' => \&_deferral_html,
+ 'errors' => \&_error_html,
+ 'deliveries' => \&_delivery_html,
+ 'unknown' => \&_unknown_html,
+ 'queue' => \&_queue_html };
+
+ my $sort_pref = { 'rejects' => 5,
+ 'deferrals' => 4,
+ 'errors' => 3,
+ 'deliveries' => 6,
+ 'unknown' => 2,
+ 'queue' => 1 };
+
+ my @dde = (); # holds list of subclass hashrefs
+ # --->{html} (HTML code generated by )
+ # \->{timestamp} (timestamp for sorting later)
+
+ # Now loop through the subclass list and call HTML
+ # generation code for each entry in all subclasses.
+ # Push the stuff onto dde where we can sort it later.
+ # Remember the timestamp of each entry so we can sort
+ # by it later to display the message events in the
+ # right order.
+ foreach my $subclass (keys %{ $subclasses }) {
+ foreach my $obj (@{ $h->{$subclass} }) {
+ my $tmp = {};
+ $tmp->{timestamp} = $obj->{timestamp};
+ $tmp->{sort_pref} = $sort_pref->{$subclass};
+ # pass in "master sort" timestamp too
+ $tmp->{html} = &{$subclasses->{$subclass}}($obj,$h->{sort_timestamp});
+ push @dde, $tmp;
+ };
+ };
+
+ $q->div({-class=>"top_spacer"},
+ $q->table({-class=>"message", -cellspacing=>1, -cellpadding=>2, -border=>0},
+ _titlebar_html($h),
+ (exists($h->{mailfrom}) ? _message_html($h) : ""),
+ eval {
+ my $event_html = "";
+ foreach my $event (sort by_event_order @dde) {
+ $event_html .= $event->{html};
+ };
+ $event_html;
+ }
+ )
+ );
+};
+sub by_event_order {
+ if ($a->{timestamp} == $b->{timestamp}) {
+ ($a->{sort_pref} <=> $b->{sort_pref});
+ }
+ else {
+ ($a->{timestamp} <=> $b->{timestamp});
+ };
+};
+
+
+# This function is used to render pre-DATA rejects.
+# Since those don't have any other associated events
+# it is useless to go through all other tables like
+# _render_message does.
+
+sub render_reject {
+ my $h = shift;
+ $q->div({-class=>"top_spacer"},
+ $q->table({-class=>"message", -cellspacing=>1, -cellpadding=>2, -border=>0},
+ _titlebar_html($h).
+ _reject_html($h,$h->{timestamp})
+ )
+ );
+};
+
+
+# renders a small "page header"
+
+sub render_header {
+ my $text = shift || "";
+
+ $q->div({-class=>"top_spacer"},
+ $q->table({-class=>"header", -cellspacing=>1, -cellpadding=>2, -border=>0},
+ $q->Tr(
+ $q->td({-class=>"header"},
+ $text
+ )
+ )
+ )
+ );
+}
+
+
+
+sub _titlebar_html {
+ my $h = shift || {};
+
+ my $actions = [ 0, 'deliver' ];
+ unless (ina($config->{web}->{restricted_users}, $main::user)) {
+ if ($h->{mailfrom} ne '<>') {
+ push @{$actions}, 'cancel';
+ }
+ push @{$actions}, 'delete';
+ }
+
+ $q->Tr(
+ $q->td({-class=>"table_titlebar"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-class=>"message_wide"},
+ _item( { 'text' => $h->{server} },
+ ( (edv($h,'message_id') && ($h->{message_id} =~ /^.{6}\-.{6}-.{2}$/) ) ?
+ { 'text' => '&middot;' } : undef ),
+ ( (edv($h,'message_id') && ($h->{message_id} =~ /^.{6}\-.{6}-.{2}$/) ) ?
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'message_id',
+ 'qs' => $h->{message_id} },
+ 'text' => $h->{message_id} } : undef ),
+ ( edv($h,'msgid') ? { 'text' => '&middot;' } : undef ),
+ ( edv($h,'msgid') ? ({ 'link' => { 'tab' => 'messages',
+ 'qt' => 'msgid',
+ 'tr' => '0',
+ 'qs' => $h->{msgid} },
+ 'text' => "Track MSGID" }) : undef ),
+ ( (edv($h,'queue') &&
+ defined(@{$h->{queue}}[0])) ? { 'text' => '&middot;' } : undef ),
+ ( (edv($h,'queue') &&
+ defined(@{$h->{queue}}[0])) ? { 'html' => $q->popup_menu({
+ -name => 'ac_'.$h->{server}.'_'.$h->{'message_id'},
+ -values => $actions,
+ -default => 0,
+ -labels => { 0 => ':: Please select action ::',
+ 'deliver' => 'Force delivery',
+ 'cancel' => 'Cancel (bounce)',
+ 'delete' => 'Delete' },
+ -override => 1
+ }).$q->submit({-name=>'Go'})
+ } : undef ) )
+ ),
+ (exists($h->{size}) ?
+ $q->td({-class=>"message"},
+ _item( { 'icon' => "icons/size.png" },
+ { 'text' => human_size($h->{size})} )
+ ) : ""),
+ (exists($h->{completed}) ?
+ $q->td({-class=>"message"},
+ _item( { 'icon' => "icons/stopwatch.png"},
+ { 'text' => _timespan((defined($h->{completed}) ? $h->{completed} : time()) - $h->{timestamp} ) } )
+ ) : "")
+ )
+ )
+ )
+ );
+};
+
+
+sub _message_html {
+ my $h = shift || {};
+
+ $q->Tr(
+ $q->td({-class=>"table_arrival"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>2,-valign=>"top",-align=>"center",-class=>"large_icon"},
+ ( ($h->{proto} =~ /local/i) ?
+ # local
+ $q->img({-src=>"icons/arrival_local.png",-border=>0,-title=>uc($h->{proto})." | ".$h->{user}})
+ :
+ ( ( ($h->{proto} eq "asmtp") || ($h->{proto} =~ /a$/) ) ?
+ ( defined($h->{tls_cipher}) ?
+ # Auth w/ TLS
+ $q->img({-src=>"icons/arrival_tls_auth.png",-border=>0,-title=>uc($h->{proto})." | ".$h->{user}." | ".$h->{tls_cipher}})
+ :
+ # Auth w/o TLS
+ $q->img({-src=>"icons/arrival_auth.png",-border=>0,-title=>uc($h->{proto})." | ".$h->{user}})
+ )
+ :
+ ( defined($h->{tls_cipher}) ?
+ # TLS
+ $q->img({-src=>"icons/arrival_tls.png",-border=>0,-title=>uc($h->{proto})." | ".$h->{tls_cipher}})
+ :
+ # nothing special
+ $q->img({-src=>"icons/arrival_normal.png",-border=>0,-title=>uc($h->{proto})})
+ )
+ )
+ )
+ ),
+ $q->td(
+ _item( { 'style' => "font-weight: bold;",
+ ( ($h->{mailfrom} eq '<>') ?
+ ( (defined($h->{bounce_parent}) ?
+ ('link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'message_id',
+ 'qs' => $h->{bounce_parent} })
+ : () ),
+ 'text' => "Bounce".
+ (defined($h->{bounce_parent}) ?
+ " of ".$h->{bounce_parent}
+ :
+ ""
+ )
+ )
+ :
+ ('link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $h->{mailfrom} },
+ 'text' => $h->{mailfrom})
+ ) }
+ )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'style' => (($h->{timestamp} == $h->{sort_timestamp}) ? "text-decoration: underline;" : undef) , 'text' => stamp_to_date($h->{timestamp}) },
+ (defined($h->{host_addr}) ? (
+ { 'icon' => "icons/server.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $h->{host_addr} },
+ 'text' => $h->{host_addr} },
+ (edt($h,'host_rdns') ?
+ ( { 'icon' => "icons/dns.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $h->{host_rdns} },
+ 'text' => $h->{host_rdns} } )
+ :
+ ()
+ ),
+ (edt($h,'host_helo') ?
+ ( { 'icon' => "icons/helo.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $h->{host_helo} },
+ 'text' => $h->{host_helo} } )
+ :
+ ()
+ ),
+ (defined($h->{host_ident}) ? (
+ { 'icon' => "icons/ident.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'ident',
+ 'qs' => $h->{host_ident} },
+ 'text' => $h->{host_ident} } )
+ : () )
+ )
+ : () ) )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub _deferral_html {
+ my $deferral = shift || {};
+ my $sort_timestamp = shift || 0;
+
+ $q->Tr(
+ $q->td({-class=>"table_deferral"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>3,-valign=>"top",-align=>"center",-class=>"large_icon"},
+ ( defined($deferral->{tls_cipher}) ?
+ # w/ TLS
+ $q->img({-src=>"icons/deferral_tls.png",-border=>0,-title=>$deferral->{tls_cipher}})
+ :
+ # w/o TLS
+ $q->img({-src=>"icons/deferral_normal.png",-border=>0})
+ )
+ ),
+ $q->td(
+ _item( { 'style' => "font-weight: bold;",
+ 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $deferral->{rcpt} },
+ 'text' => $deferral->{rcpt} },
+ (edt($deferral,'rcpt_intermediate') ?
+ ({ 'text' => '-> '.$deferral->{rcpt_intermediate} })
+ :
+ ()
+ ),
+ ((lc($deferral->{rcpt}) ne lc($deferral->{rcpt_final})) ?
+ ({ 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $deferral->{rcpt_final} },
+ 'text' => '-> '.$deferral->{rcpt_final} })
+ :
+ ()
+ )
+ )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'style' => (($deferral->{timestamp} == $sort_timestamp) ? "text-decoration: underline;" : undef) , 'text' => stamp_to_date($deferral->{timestamp}) },
+ { 'icon' => "icons/router_transport.png" },
+ { 'text' => $deferral->{router}.
+ ( defined($deferral->{transport}) ?
+ "->".$deferral->{transport}.(defined($deferral->{shadow_transport}) ? " [".$deferral->{shadow_transport}."]" : "")
+ :
+ "") },
+ ( defined($deferral->{host_addr}) ?
+ ( { 'icon' => "icons/server.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $deferral->{host_addr} },
+ 'text' => $deferral->{host_addr} },
+ { 'icon' => "icons/dns.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $deferral->{host_dns} },
+ 'text' => $deferral->{host_dns} } )
+ :
+ ()
+ ) )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'icon' => "icons/errmsg.png" },
+ { 'text' => $deferral->{errmsg} } )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub _reject_html {
+ my $reject = shift || {};
+ my $sort_timestamp = shift || 0;
+
+ $q->Tr(
+ $q->td({-class=>"table_reject"},
+ $q->table({-cellpadding=>0,-cellspacing=>0,-border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>2,-valign=>"top",-align=>"center",-class=>"large_icon"},
+ (edv($reject,'message_id') ?
+ # post-DATA
+ $q->img({-src=>"icons/reject_postdata.png",-border=>0})
+ :
+ # pre-DATA
+ $q->img({-src=>"icons/reject_predata.png",-border=>0})
+ )
+ ),
+ $q->td(
+ _item( (edv($reject,'mailfrom') ?
+ (($reject->{mailfrom} eq '<>') ?
+ { 'style' => "font-weight: bold;",
+ 'text' => "Bounce" }
+ :
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $reject->{mailfrom} },
+ 'style' => "font-weight: bold;",
+ 'text' => $reject->{mailfrom} }
+ )
+ :
+ () ),
+ { 'icon' => "icons/server.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $reject->{host_addr} },
+ 'text' => $reject->{host_addr} },
+ (edt($reject,'host_rdns') ?
+ ( { 'icon' => "icons/dns.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $reject->{host_rdns} },
+ 'text' => $reject->{host_rdns} } )
+ :
+ ()
+ ),
+ (edt($reject,'host_helo') ?
+ ( { 'icon' => "icons/helo.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $reject->{host_helo} },
+ 'text' => $reject->{host_helo} } )
+ :
+ ()
+ ),
+ (defined($reject->{host_ident}) ?
+ ( { 'icon' => "icons/ident.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'ident',
+ 'qs' => $reject->{host_ident} },
+ 'text' => $reject->{host_ident} } ) : ()
+ ) )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'style' => (($reject->{timestamp} == $sort_timestamp) ? "text-decoration: underline;" : undef) , 'text' => stamp_to_date($reject->{timestamp}) },
+ { 'icon' => "icons/errmsg.png" },
+ { 'text' => $reject->{errmsg} } )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub _error_html {
+ my $error = shift || {};
+ my $sort_timestamp = shift || 0;
+
+ $q->Tr(
+ $q->td({-class=>"table_error"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>3,-valign=>"top",-align=>"center",-class=>"large_icon"},
+ ( defined($error->{tls_cipher}) ?
+ # w/ TLS
+ $q->img({-src=>"icons/error_tls.png",-border=>0,-title=>$error->{tls_cipher}})
+ :
+ # w/o TLS
+ $q->img({-src=>"icons/error_normal.png",-border=>0})
+ )
+ ),
+ $q->td(
+ _item( { 'style' => "font-weight: bold;",
+ 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $error->{rcpt} },
+ 'text' => $error->{rcpt} },
+ (edt($error,'rcpt_intermediate') ?
+ ({ 'text' => '-> '.$error->{rcpt_intermediate} })
+ :
+ ()
+ ),
+ ((lc($error->{rcpt}) ne lc($error->{rcpt_final})) ?
+ ({ 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $error->{rcpt_final} },
+ 'text' => '-> '.$error->{rcpt_final} })
+ :
+ ()
+ )
+ )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'style' => (($error->{timestamp} == $sort_timestamp) ? "text-decoration: underline;" : undef) , 'text' => stamp_to_date($error->{timestamp}) },
+ ( edv($error,'router') ? (
+ { 'icon' => "icons/router_transport.png" },
+ { 'text' => $error->{router}.
+ ( defined($error->{transport}) ?
+ "->".$error->{transport}.(defined($error->{shadow_transport}) ? " [".$error->{shadow_transport}."]" : "")
+ :
+ "") },
+ ( defined($error->{host_addr}) ?
+ ( { 'icon' => "icons/server.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $error->{host_addr} },
+ 'text' => $error->{host_addr} },
+ { 'icon' => "icons/dns.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $error->{host_dns} },
+ 'text' => $error->{host_dns} } )
+ :
+ ()
+ ) ) : () ) )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'icon' => "icons/errmsg.png" },
+ { 'text' => $error->{errmsg} } )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub _delivery_html {
+ my $delivery = shift || {};
+ my $sort_timestamp = shift || 0;
+
+ $q->Tr(
+ $q->td({-class=>"table_delivery"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>2,-valign=>"top",-align=>"center",-class=>"large_icon"},
+ ( defined($delivery->{tls_cipher}) ?
+ # w/ TLS
+ $q->img({-src=>"icons/delivery_tls.png",-border=>0,-title=>$delivery->{tls_cipher}})
+ :
+ # w/o TLS
+ $q->img({-src=>"icons/delivery_normal.png",-border=>0})
+ )
+ ),
+ $q->td(
+ _item( { 'style' => "font-weight: bold;",
+ 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $delivery->{rcpt} },
+ 'text' => $delivery->{rcpt} },
+ (edt($delivery,'rcpt_intermediate') ?
+ ({ 'text' => '-> '.$delivery->{rcpt_intermediate} })
+ :
+ ()
+ ),
+ ((lc($delivery->{rcpt}) ne lc($delivery->{rcpt_final})) ?
+ ({ 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'addr-all',
+ 'qs' => $delivery->{rcpt_final} },
+ 'text' => '-> '.$delivery->{rcpt_final} })
+ :
+ ()
+ )
+ )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'style' => (($delivery->{timestamp} == $sort_timestamp) ? "text-decoration: underline;" : undef) , 'text' => stamp_to_date($delivery->{timestamp}) },
+ { 'icon' => "icons/router_transport.png" },
+ { 'text' => $delivery->{router}.
+ ( defined($delivery->{transport}) ?
+ "->".$delivery->{transport}.(defined($delivery->{shadow_transport}) ? " [".$delivery->{shadow_transport}."]" : "")
+ :
+ "") },
+ ( defined($delivery->{host_addr}) ?
+ ( { 'icon' => "icons/server.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $delivery->{host_addr} },
+ 'text' => $delivery->{host_addr} },
+ { 'icon' => "icons/dns.png" },
+ { 'link' => { 'tab' => 'messages',
+ 'tr' => '0',
+ 'qt' => 'host-all',
+ 'qs' => $delivery->{host_dns} },
+ 'text' => $delivery->{host_dns} } )
+ :
+ ()
+ ) )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub _unknown_html {
+ my $unknown = shift || {};
+ my $sort_timestamp = shift || 0;
+
+ $q->Tr(
+ $q->td({-class=>"table_unknown"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-valign=>"top",-align=>"center",-class=>"large_icon"},
+ $q->img({-src=>"icons/unknown.png",-border=>0})
+ ),
+ $q->td(
+ _item( { 'style' => (($unknown->{timestamp} == $sort_timestamp) ? "text-decoration: underline;" : undef) , 'text' => stamp_to_date($unknown->{timestamp}) },
+ { 'text' => $unknown->{line} } )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub _queue_html {
+ my $queue = shift || {};
+ my $sort_timestamp = shift || 0;
+
+ my @recipients_delivered = split / /, $queue->{recipients_delivered};
+ my @recipients_pending = split / /, $queue->{recipients_pending};
+
+ $q->Tr(
+ $q->td({-class=>"table_queue"},
+ $q->table({-cellpadding=>0,-cellspacing=>0, -border=>0},
+ $q->Tr(
+ $q->td({-rowspan=>2,-valign=>"top",-align=>"center",-class=>"large_icon"},
+ ( edt($queue,'frozen') ?
+ # frozen
+ $q->img({-src=>"icons/queue_frozen.png",-border=>0,-title=>"Frozen at ".stamp_to_date($queue->{frozen})})
+ :
+ # normal
+ $q->img({-src=>"icons/queue_deferred.png",-border=>0})
+ )
+ ),
+ $q->td(
+ _item( { #'style' => "font-family: Arial, Helvetica, Sans-Serif;",
+ 'text' => $queue->{subject} } )
+ )
+ ),
+ $q->Tr(
+ $q->td(
+ _item( { 'icon' => "icons/delivered.png",
+ ( (scalar @recipients_delivered) ?
+ ( 'title' => join("\n",@recipients_delivered) )
+ :
+ ()
+ ) },
+ { 'text' => scalar @recipients_delivered },
+ { 'text' => '&nbsp;' },
+ { 'icon' => "icons/deferred.png",
+ 'title' => join("\n",@recipients_pending) },
+ { 'text' => scalar @recipients_pending },
+ { 'text' => '&nbsp;' },
+ { 'icon' => "icons/dsn_warning.png",
+ 'title' => "Number of DSNs sent" },
+ { 'text' => $queue->{num_dsn} } )
+ )
+ )
+ )
+ )
+ );
+};
+
+sub render_queue_table {
+ my $messages = shift;
+ my $now = time();
+
+ my $rows = "";
+ foreach my $message (@{ $messages }) {
+ my $row_id = $message->{server}.'_'.$message->{message_id};
+ my @rcpts_delivered = split / /,$message->{recipients_delivered};
+ my @rcpts_pending = split / /,$message->{recipients_pending};
+ $rows .=
+ $q->Tr(
+ $q->td({-class=>"queue"},
+ # Actions
+ ),
+ $q->td({-class=>"queue"},
+ $message->{server}
+ ),
+ $q->td({-class=>"queue"},
+ edt($message,'timestamp') ?
+ _timespan($now - $message->{timestamp})
+ :
+ '?'
+ ),
+ $q->td({-class=>"queue"},
+ _shorten_addr($message->{mailfrom},40)
+ ),
+ $q->td({-class=>"queue",
+ -onMouseOver=>"javascript:document.getElementById('$row_id' + '_pending').style.visibility = 'visible';",
+ -onMouseOut=>"javascript:document.getElementById('$row_id' + '_pending').style.visibility = 'hidden';"},
+ _shorten_addr($rcpts_pending[0],40)
+ ),
+ $q->td({-class=>"queue"},
+ (defined($rcpts_pending[0]) ?
+ $q->div({-id=>$row_id.'_pending', -class=>"rcpts_pending_popup"},"Test<br>Test2<br>Test3")
+ :
+ '').
+ _shorten_string($message->{subject},60)
+ )
+ );
+ };
+
+ $q->div({-class=>"top_spacer"},
+ $q->table({-class=>"queue_table",-cellpadding=>0,-cellspacing=>1,border=>0},
+ $q->Tr(
+ # Table header
+ $q->td({-class=>"queue_header",-width=>"1%"},
+ "&nbsp;"
+ ),
+ $q->td({-class=>"queue_header",-width=>"1%"},
+ "Server"
+ ),
+ $q->td({-class=>"queue_header",-width=>"1%"},
+ "Age"
+ ),
+ $q->td({-class=>"queue_header",-width=>"1%"},
+ "Sender"
+ ),
+ $q->td({-class=>"queue_header",-width=>"1%"},
+ "Recipient(s)"
+ ),
+ $q->td({-class=>"queue_header"},
+ "Subject"
+ )
+ ),
+ $rows
+ )
+ );
+};
+
+
+# -- Private functions -------------------------------------
+
+sub _item {
+ my $html = "";
+
+ # Loop through all parts and build the table TDs
+ while (scalar @_) {
+ my $part = shift @_;
+ next unless $part;
+
+ my $link = "";
+ if (exists($part->{'link'})) {
+ # this item has a link
+ $link = 'exilog_cgi.pl?';
+ foreach my $var (keys %{ $part->{'link'} }) {
+ $link .= $var.'='._url_encode($part->{'link'}->{$var}).'&';
+ }
+ chop($link);
+ }
+
+ if (exists($part->{icon})) {
+ $html .=
+ $q->td({-class=>"item_icon",-style=>(exists($part->{style}) ? $part->{style} : "")},
+ $q->img({ -src=>$part->{icon},
+ -title=>(exists($part->{title}) ? $part->{title} : "" ),
+ -border=>0 })
+ );
+ next;
+ }
+ elsif (exists($part->{html})) {
+ $html .= $q->td({-class=>"item_text"}, $part->{html});
+ }
+ elsif (exists($part->{text})) {
+ # HTML-quote angle brackets
+ $part->{text} =~ s/\>/\&gt\;/g;
+ $part->{text} =~ s/\</\&lt\;/g;
+
+ # break long text at colons or blanks
+ $part->{text} =~ s/([^<>]{80,}?)([: ])/$1$2\<br\>/g;
+
+ $html .=
+ $q->td({-class=>"item_text",
+ ($link ? ( -onClick=>"javascript:document.location.href='$link';",
+ -style=>(exists($part->{style}) ? $part->{style} : "")."cursor:pointer;cursor:hand;",
+ -onMouseOver=>"javascript:link_on(this);",
+ -onMouseOut=>"javascript:link_off(this);" )
+ : (
+ -style=>(exists($part->{style}) ? $part->{style} : "")
+ ) ) },
+ $part->{text}
+ );
+ };
+ };
+
+ # Wrap everything in the surrounding table.
+ return
+ $q->table({-class=>"item",-cellspacing=>0,-cellpadding=>0,-border=>0},
+ $q->Tr(
+ $html
+ )
+ );
+};
+
+
+sub _shorten_addr {
+ my $addr = shift;
+ my $max = shift;
+ return $addr if (length($addr) <= $max);
+
+ my ($localpart,$domain) = split /\@/, $addr, 2;
+
+ if (length($addr) > (int($max/2))) {
+ # shorten local part first
+ $localpart = substr($localpart,0,int($max/4)).'...';
+ };
+ # return if that suffices
+ return $localpart.'@'.$domain if (length($localpart.'@'.$domain) <= $max);
+
+ # shorten domain
+ my @domainparts = split /\./, $domain;
+ while ((scalar @domainparts) > 1) {
+ shift @domainparts;
+ last if (length($localpart.'@'.'...'.join('.',@domainparts)) <= $max);
+ };
+
+ return $localpart.'@'.'...'.join('.',@domainparts);
+};
+
+
+sub _shorten_string {
+ my $string = shift;
+ my $max = shift;
+ return $string if (length($string) <= $max);
+ return substr($string,0,($max-3)).'...';
+};
+
+
+sub _timespan {
+ my $amnt = shift;
+ my @steps = (1,60,60,24,7,999999999);
+ my @units = ('s','m','h','d','wk');
+
+ my $str = "";
+ while ($amnt > $steps[1]) {
+ my $rest = $amnt % $steps[1];
+ $str = $rest.$units[0]." ".$str;
+ $amnt = int($amnt/$steps[1]);
+ shift @units;
+ shift @steps;
+ };
+ $str = $amnt.$units[0]." ".$str;
+ return $str;
+};
+
+sub _url_encode {
+ my $subj = shift;
+ $subj =~ s/([^A-Za-z0-9])/sprintf("%%%02x",ord($1))/eg;
+ return $subj;
+};
+
+1;
diff --git a/cgi/exilog_cgi_messages.pm b/cgi/exilog_cgi_messages.pm
new file mode 100644
index 0000000..9b36aed
--- /dev/null
+++ b/cgi/exilog_cgi_messages.pm
@@ -0,0 +1,816 @@
+#!/usr/bin/perl -w
+#
+# This file is part of the exilog suite.
+#
+# http://duncanthrax.net/exilog/
+#
+# (c) Tom Kistner 2004
+#
+# See LICENSE for licensing information.
+#
+
+package exilog_cgi_messages;
+use strict;
+use exilog_config;
+use exilog_cgi_html;
+use exilog_cgi_param;
+use exilog_sql;
+use exilog_util;
+use Net::Netmask;
+use Time::Local;
+
+use Data::Dumper;
+
+BEGIN {
+ use Exporter;
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+ # set the version for version checking
+ $VERSION = 0.1;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &messages
+ );
+
+ %EXPORT_TAGS = ();
+
+ # your exported package globals go here,
+ # as well as any optionally exported functions
+ @EXPORT_OK = qw();
+}
+
+sub _select_all {
+ # All tables
+ my @tables = ( 'deliveries','errors','unknown','deferrals','messages','rejects','queue' );
+ # Only server and timestamp as criteria.
+ # Since these are present on every table
+ # the queries are all the same ...
+ my $criteria = { 'timestamp' => (edt($param,'tr') ? _make_tr() : undef),
+ 'server' => (edt($param,'sr') ? $param->{'sr'} : undef ) };
+
+ my @results = ();
+ foreach my $table (@tables) {
+ next unless (ina($param->{'qw'},$table));
+ push @results, @{ sql_select( $table, [ 'server','message_id','timestamp' ], $criteria ) };
+ };
+ return \@results;
+};
+
+sub _select_ident {
+ if (!edt($param,'qs')) {
+ return [];
+ }
+ my $criteria = { 'timestamp' => (edt($param,'tr') ? _make_tr() : undef),
+ 'server' => (edt($param,'sr') ? $param->{'sr'} : undef ),
+ 'host_ident' => $param->{'qs'} };
+ # Only messages table
+ return sql_select( 'messages', [ 'server','message_id','timestamp' ], $criteria );
+};
+
+sub _select_msgid {
+ if (!edt($param,'qs')) {
+ return [];
+ }
+ # Only messages table
+ return sql_select( 'messages', [ 'server','message_id','timestamp' ], { 'msgid' => $param->{'qs'} } );
+};
+
+sub _select_message_id {
+ if (!edt($param,'qs')) {
+ return [];
+ }
+
+ my @results = ();
+ my @tables = ( 'deliveries','errors','unknown','deferrals','messages','rejects','queue' );
+ my $criteria = { 'message_id' => $param->{'qs'} };
+ foreach my $table (@tables) {
+ push @results, @{ sql_select( $table, [ 'server','message_id','timestamp' ], $criteria ) };
+ };
+
+ # check bounce parent field too
+ push @results, @{ sql_select( 'messages', [ 'server','message_id','timestamp' ], { 'bounce_parent' => $param->{'qs'} } ) };
+
+ return \@results;
+};
+
+sub _select_addr {
+ my $p = shift || 'all';
+
+ if (!edt($param,'qs')) {
+ return [];
+ }
+
+ my @queries;
+ push @queries, { 'table' => 'messages',
+ 'criteria' => { 'mailfrom' => $param->{'qs'} } },
+ { 'table' => 'rejects',
+ 'criteria' => { 'mailfrom' => $param->{'qs'} } }
+ if (($p eq 'sender') || ($p eq 'all'));
+
+ push @queries, { 'table' => 'rejects',
+ 'criteria' => { 'rcpt' => $param->{'qs'} } },
+ { 'table' => 'deliveries',
+ 'criteria' => { 'rcpt' => $param->{'qs'} } },
+ { 'table' => 'deliveries',
+ 'criteria' => { 'rcpt_final' => $param->{'qs'} } },
+ { 'table' => 'deferrals',
+ 'criteria' => { 'rcpt' => $param->{'qs'} } },
+ { 'table' => 'deferrals',
+ 'criteria' => { 'rcpt_final' => $param->{'qs'} } },
+ { 'table' => 'errors',
+ 'criteria' => { 'rcpt' => $param->{'qs'} } },
+ { 'table' => 'errors',
+ 'criteria' => { 'rcpt_final' => $param->{'qs'} } }
+ if (($p eq 'rcpt') || ($p eq 'all'));
+
+
+ my @results = ();
+ foreach my $query (@queries) {
+ next unless (ina($param->{'qw'},$query->{table}));
+ # add standard criteria
+ $query->{criteria}->{'timestamp'} = (edt($param,'tr') ? _make_tr() : undef);
+ $query->{criteria}->{'server'} = (edt($param,'sr') ? $param->{'sr'} : undef );
+ push @results, @{ sql_select( $query->{table}, [ 'server','message_id','timestamp' ], $query->{criteria} ) };
+ };
+
+ return \@results;
+};
+
+
+sub _select_host {
+ my $p = shift || 'all';
+
+ if (!edt($param,'qs')) {
+ return [];
+ }
+
+ my @queries;
+ if ($param->{'qs'} =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
+ # IPv4 address
+ push @queries, { 'table' => 'messages',
+ 'criteria' => { 'host_addr' => $param->{'qs'} } },
+ { 'table' => 'rejects',
+ 'criteria' => { 'host_addr' => $param->{'qs'} } }
+ if (($p eq 'incoming') || ($p eq 'all'));
+
+ push @queries, { 'table' => 'deliveries',
+ 'criteria' => { 'host_addr' => $param->{'qs'} } },
+ { 'table' => 'deferrals',
+ 'criteria' => { 'host_addr' => $param->{'qs'} } },
+ { 'table' => 'errors',
+ 'criteria' => { 'host_addr' => $param->{'qs'} } },
+ { 'table' => 'unknown',
+ 'criteria' => { 'line' => '%'.$param->{'qs'}.'%' } }
+ if (($p eq 'outgoing') || ($p eq 'all'));
+
+ }
+ elsif ($param->{'qs'} =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$/) {
+ # check if we can make a valid Net::Netmask object out of this
+ my $block = new2 Net::Netmask($param->{'qs'});
+
+ if (!defined($block)) {
+ return "Invalid CIDR specification";
+ };
+
+ # Network specification
+ push @queries, { 'table' => 'messages' },
+ { 'table' => 'rejects' }
+ if (($p eq 'incoming') || ($p eq 'all'));
+
+ push @queries, { 'table' => 'deliveries' },
+ { 'table' => 'deferrals' },
+ { 'table' => 'errors' }
+ if (($p eq 'outgoing') || ($p eq 'all'));
+
+ my @results = ();
+ foreach my $query (@queries) {
+ next unless (ina($param->{'qw'},$query->{table}));
+ # add standard criteria
+ $query->{criteria}->{'timestamp'} = (edt($param,'tr') ? _make_tr() : undef);
+ $query->{criteria}->{'server'} = (edt($param,'sr') ? $param->{'sr'} : undef );
+ push @results, @{ sql_select( $query->{table}, [ 'server','message_id','timestamp','host_addr' ], $query->{criteria} ) };
+ };
+
+ # now weed out those that don't match the CIDR specification
+ my @valid = ();
+ foreach my $result (@results) {
+ if ($block->match($result->{host_addr})) {
+ delete $result->{host_addr};
+ push @valid, $result;
+ };
+ };
+
+ return \@valid;
+ }
+ else {
+ # assume hostname
+ my $prefix_wc = "";
+ my $suffix_wc = "";
+ $prefix_wc = '%' if ($param->{'qs'} !~ /^\%/);
+ $suffix_wc = '%' if ($param->{'qs'} !~ /\%$/);
+
+ push @queries, { 'table' => 'messages',
+ 'criteria' => { 'host_helo' => $param->{'qs'} } },
+ { 'table' => 'messages',
+ 'criteria' => { 'host_rdns' => $param->{'qs'} } },
+ { 'table' => 'rejects',
+ 'criteria' => { 'host_helo' => $param->{'qs'} } },
+ { 'table' => 'rejects',
+ 'criteria' => { 'host_rdns' => $param->{'qs'} } }
+ if (($p eq 'incoming') || ($p eq 'all'));
+
+ push @queries, { 'table' => 'deliveries',
+ 'criteria' => { 'host_dns' => $param->{'qs'} } },
+ { 'table' => 'deferrals',
+ 'criteria' => { 'host_dns' => $param->{'qs'} } },
+ { 'table' => 'errors',
+ 'criteria' => { 'host_dns' => $param->{'qs'} } },
+ { 'table' => 'unknown',
+ # the blank makes sure that we do not match domains in addresses
+ 'criteria' => { 'line' => $prefix_wc.' '.$param->{'qs'}.$suffix_wc } }
+ if (($p eq 'outgoing') || ($p eq 'all'));
+ };
+
+ my @results = ();
+ foreach my $query (@queries) {
+ next unless (ina($param->{'qw'},$query->{table}));
+ # add standard criteria
+ $query->{criteria}->{'timestamp'} = (edt($param,'tr') ? _make_tr() : undef);
+ $query->{criteria}->{'server'} = (edt($param,'sr') ? $param->{'sr'} : undef );
+ push @results, @{ sql_select( $query->{table}, [ 'server','message_id','timestamp' ], $query->{criteria} ) };
+ };
+
+ return \@results;
+};
+
+
+
+sub messages {
+
+ _print_Messages_selector();
+
+ # Check CGI input for event selection.
+ # We need at least a query type ('qt'),
+ # otherwise we only display the selector.
+ my $selected = [];
+ if (edt($param,'qt')) {
+ # Call event selection function for this query type.
+ _print_progress_bar("Collecting message IDs ...");
+ # cut off parameter part (separated with dash)
+ my ($function,$parameter) = split /\-/, $param->{'qt'};
+ no strict "refs";
+ $selected = &{ "_select_".$function }($parameter);
+ if (ref($selected) ne 'ARRAY') {
+ # error
+ _update_progress_bar($selected);
+ return;
+ };
+ }
+ else {
+ # no query type ('qt'), just return
+ return;
+ };
+
+ # Now we have a set of selected messages in an array:
+ #
+ # [0]-->{server}
+ # |->{timestamp}
+ # \->{message_id}
+ # [1]-->{server}
+ # |->{timestamp}
+ # \->{message_id}
+ # ...
+
+ # Perform dupe check. We may have a lot of duplicate IDs
+ # in the list. It is faster to weed them out this way ...
+ _update_progress_bar("Performing dupe check ...");
+ my $dupe = {};
+ my @duped = ();
+ foreach my $message (@{ $selected }) {
+ if (exists($dupe->{$message->{server}}->{$message->{message_id}})) {
+ # Make sure we use the largest timestamp we can find
+ if ($dupe->{$message->{server}}->{$message->{message_id}}->{timestamp} < $message->{timestamp}) {
+ $dupe->{$message->{server}}->{$message->{message_id}}->{timestamp} = $message->{timestamp};
+ };
+ next;
+ };
+ $dupe->{$message->{server}}->{$message->{message_id}} = $message;
+ push @duped, $message;
+ };
+ undef $dupe;
+ undef $selected;
+
+ if ((scalar @duped) == 0) {
+ _update_progress_bar("No matching events found.");
+ return;
+ };
+
+ if (((scalar @duped) > 500) && ($param->{'sm'} !~ /^Confirm/)) {
+ _update_progress_bar("Warning: ".(scalar @duped)." messages/events found. Narrow down your selection or submit the query again.");
+ print '
+ <script language="Javascript">
+ document.forms[0].sm.value = "Confirm Query";
+ </script>
+ ';
+ return;
+ };
+
+ # Initialize stats counters
+ my $stats = {
+ 'num_messages' => { 'desc' => "Messages",
+ 'order' => 1,
+ 'num' => 0 },
+ 'num_rejects' => { 'desc' => "Rejects",
+ 'order' => 5,
+ 'num' => 0 },
+ 'num_deliveries' => { 'desc' => "Deliveries",
+ 'order' => 2,
+ 'num' => 0 },
+ 'num_errors' => { 'desc' => "Errors",
+ 'order' => 3,
+ 'num' => 0 },
+ 'total_turnover' => { 'desc' => "Total Turnover",
+ 'order' => 4,
+ 'size' => 0 }
+ };
+
+ # Now we need to build the complete message set.
+ # This requires a large number of SELECTs.
+ _update_progress_bar("Sorting ...");
+ my $c = 0;
+ foreach my $message (sort { $b->{timestamp} <=> $a->{timestamp} } @duped) {
+
+ # Update the progress bar every 50 entries
+ if (($c % 50) == 0) {
+ _update_progress_bar("Grabbing event data (".$c." of ".scalar @duped." events done) ...");
+ };
+ $c++;
+
+ # Remove timestamp, we'll re-add it later for marking
+ # the "sort" timestamp.
+ my $sort_timestamp = $message->{timestamp};
+ delete $message->{timestamp};
+
+ # Check the message ID.
+ if ($message->{message_id} !~ /^.{6}\-.{6}\-.{2}$/) {
+ # This is a pre-DATA reject/warning.
+ # Render it as a reject.
+ my $complete = @{ sql_select( 'rejects', ['*'], $message ) }[0];
+ $complete->{sort_timestamp} = $sort_timestamp;
+ print render_reject($complete);
+ $stats->{num_rejects}->{num}++;
+ }
+ else {
+ # Try to grab complete arrival ('messages' table)
+ my $complete = @{ sql_select( 'messages', ['*'], $message ) }[0];
+
+ # If there is an arrival, this set has a "real"
+ # message ID. Scan other tables for events.
+ if (defined($complete)) {
+ $complete->{rejects} = sql_select( 'rejects', ['*'], $message );
+ $complete->{deliveries} = sql_select( 'deliveries', ['*'], $message );
+ $complete->{errors} = sql_select( 'errors', ['*'], $message );
+ $complete->{deferrals} = sql_select( 'deferrals', ['*'], $message );
+ $complete->{unknown} = sql_select( 'unknown', ['*'], $message );
+ $complete->{queue} = sql_select( 'queue', ['*'], $message );
+ $complete->{sort_timestamp} = $sort_timestamp;
+ print render_message($complete);
+ $stats->{num_messages}->{num}++;
+ $stats->{total_turnover}->{size} += $complete->{size};
+ $stats->{num_rejects}->{num} += (scalar @{ $complete->{rejects} });
+ $stats->{num_deliveries}->{num} += (scalar @{ $complete->{deliveries} });
+ $stats->{total_turnover}->{size} += ($complete->{size} * (scalar @{ $complete->{deliveries} }));
+ $stats->{num_errors}->{num} += (scalar @{ $complete->{errors} });
+ }
+ # If there is no associated arrival, this is either
+ # a POST-DATA reject (in rejects table) or another
+ # post-DATA warning (in unknown table). Since both
+ # can occur, we render this as a message.
+ else {
+ $complete->{server} = $message->{server};
+ $complete->{message_id} = $message->{message_id};
+ $complete->{rejects} = sql_select( 'rejects', ['*'], $message );
+ $complete->{unknown} = sql_select( 'unknown', ['*'], $message );
+ $complete->{sort_timestamp} = $sort_timestamp;
+ print render_message($complete);
+ $stats->{num_rejects}->{num}++;
+ };
+ };
+ };
+
+ _update_progress_bar(_render_stats($stats));
+};
+
+
+sub _make_tr {
+
+ my $str = $q->param('tr') || 0;
+
+ unless ($str eq 'custom') {
+ my $unit = chop $str;
+ my $now = time();
+ my $units = { '0' => 0,
+ 'm' => 60,
+ 'h' => 3600,
+ 'd' => 86400 };
+ my $then = $now + $units->{$unit}*$str;
+ unless ($now == $then) { # The "unlimited" case
+ $param->{'tds'} = stamp_to_date($then,1);
+ $param->{'tde'} = stamp_to_date($now,1);
+ }
+ return $then;
+ }
+ else {
+ $param->{'tds'} =~ s/ +$//;
+ $param->{'tds'} =~ s/^ +//;
+ $param->{'tde'} =~ s/ +$//;
+ $param->{'tde'} =~ s/^ +//;
+
+ my ($sd,$st) = split / +/, $param->{'tds'};
+ my ($ed,$et) = split / +/, $param->{'tde'};
+
+ if (!$st && $sd =~ /\:/) {
+ $st = $sd;
+ $sd = '';
+ }
+ if (!$et && $ed =~ /\:/) {
+ $et = $ed;
+ $ed = '';
+ }
+
+ $ed = $sd unless($ed);
+
+ my $fsd = _parse_date($sd, $st || '00:00:00');
+ my $fed = _parse_date($ed, $et || '23:59:59');
+
+ $param->{'tds'} = stamp_to_date($fsd);
+ $param->{'tde'} = stamp_to_date($fed);
+
+ return $fsd." ".$fed;
+ }
+}
+
+
+sub _parse_date {
+ my $d = shift;
+ my $t = shift;
+
+ my ($dn,$tn) = split / /, stamp_to_date(time,1);
+ my ($year,$month,$day) = split /\-/, $dn;
+ my ($hour,$minute,$second) = split /\:/, $tn;
+
+ if ($d =~ /^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$/) {
+ $year = $1;
+ $month = $2;
+ $day = $3;
+ }
+ elsif ($d =~ /^([0-9]{2})\-([0-9]{2})$/) {
+ $month = $1;
+ $day = $2;
+ }
+
+ if ($t =~ /^([0-9]{2})\:([0-9]{2})\:([0-9]{2})$/) {
+ $hour = $1;
+ $minute = $2;
+ $second = $3;
+ }
+ elsif ($t =~ /^([0-9]{2})\:([0-9]{2})$/) {
+ $hour = $1;
+ $minute = $2;
+ }
+
+ return date_to_stamp($year.'-'.$month.'-'.$day, $hour.':'.$minute.':'.$second);
+}
+
+sub _render_stats {
+ my $stats = shift || {};
+
+ my @items = ();
+ foreach (sort {$stats->{$a}->{order} <=> $stats->{$b}->{order}} keys %{ $stats }) {
+ if (exists($stats->{$_}->{num}) && $stats->{$_}->{num}) {
+ push @items, $stats->{$_}->{desc}.": ".$stats->{$_}->{num};
+ }
+ elsif (exists($stats->{$_}->{size}) && $stats->{$_}->{size}) {
+ push @items, $stats->{$_}->{desc}.": ".human_size($stats->{$_}->{size});
+ };
+ };
+
+ return join("&nbsp;&nbsp;<b>|</b>&nbsp;&nbsp;",@items);
+};
+
+
+sub _print_progress_bar {
+ my $str = shift || "";
+ print render_header(
+ $q->div({-name=>"progress",-id=>"progress", -align=>"center"},
+ $str
+ )
+ );
+ print "\n<!-- Block filler follows - ".("xxxx" x 1024)." -->\n";
+};
+
+
+sub _update_progress_bar {
+ my $str = shift || "";
+ print '
+ <script language="JavaScript">
+ document.getElementById("progress").innerHTML = "'.$str.'";
+ </script>
+ ';
+ print "\n<!-- Block filler follows - ".("xxxx" x 1024)." -->\n";
+};
+
+
+sub _print_Messages_selector {
+
+ _make_tr();
+
+ print
+ $q->div({-class=>"top_spacer"},
+ $q->div({-align=>"left",-style=>"padding: 10px; border: 1px solid black; background: #eeeeee;"},
+
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0},
+ $q->Tr(
+ $q->td({-align=>"left",-style=>"width: 16px;"},
+ $q->img({-src=>"icons/event_type.png"})
+ ),
+ $q->td({-align=>"left",-style=>"width: 100px;"},
+ "Search Type"
+ ),
+ $q->td({-align=>"left"},
+ $q->popup_menu({ -name=>"qt",
+ -id=>"qt",
+ -style=>"width: 400px;",
+ -values=>[ 'all',
+ 'addr-all',
+ 'addr-sender',
+ 'addr-rcpt',
+ 'host-all',
+ 'host-incoming',
+ 'host-outgoing',
+ 'msgid',
+ 'ident',
+ 'message_id' ],
+ -labels=>{ 'all' => "Show everything",
+ 'addr-all' => "Address (All)",
+ 'addr-sender' => "Address (Sender)",
+ 'addr-rcpt' => "Address (Recipient)",
+ 'host-all' => "Host (all)",
+ 'host-incoming' => "Host (incoming)",
+ 'host-outgoing' => "Host (outgoing)",
+ 'msgid' => "Message-ID (Header)",
+ 'ident' => "Ident String (incoming messages)",
+ 'message_id' => "Message-ID (Exim)"
+ },
+ -default=>(exists($param->{'qt'}) ? ($param->{'qt'} || 'all') : 'all'),
+ -onChange=>"javascript:switch_controls(document.getElementById('qt').options[document.getElementById('qt').selectedIndex].value);",
+ -override=>1})
+ )
+ )
+ )
+ .
+ $q->span({-id=>"term"},'<!-- Dynamic content target DIV -->').
+ $q->div({-id=>"term_hidden",-style=>"visibility: hidden; position: absolute;"},
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0},
+ $q->Tr(
+ $q->td({-align=>"left",-style=>"width: 16px;"},
+ $q->img({-src=>"icons/find.png"})
+ ),
+ $q->td({-align=>"left",-style=>"width: 100px;"},
+ "Search Term"
+ ),
+ $q->td({-align=>"left"},
+ $q->textfield( { -name=>"qs",
+ -style=>"width: 400px;",
+ -value=>(exists($param->{'qs'}) ? ($param->{'qs'} || '') : ''),
+ -override=>1 } )
+ )
+ )
+ )
+ )
+ .
+ $q->span({-id=>"events"},'<!-- Dynamic content target DIV -->').
+ $q->div({-id=>"events_hidden",-style=>"visibility: hidden; position: absolute;"},
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0},
+ $q->Tr(
+ $q->td({-align=>"left",-valign=>"top",-style=>"width: 16px;"},
+ $q->img({-src=>"icons/address.png"})
+ ),
+ $q->td({-align=>"left",-valign=>"top",-style=>"width: 100px;"},
+ "Event types"
+ ),
+ $q->td({-align=>"left",-style=>"padding:2px 4px 4px 4px;"},
+ eval {
+ my @where = ( 'messages',
+ 'errors',
+ 'deliveries',
+ 'deferrals',
+ 'rejects',
+ 'queue'
+ );
+
+ my $labels = { 'messages' => 'Arrivals',
+ 'errors' => 'Errors',
+ 'deliveries' => 'Deliveries',
+ 'deferrals' => 'Deferrals',
+ 'rejects' => 'Rejects',
+ 'queue' => 'Queued' };
+
+ my $html = "";
+ my $num = 0;
+ foreach my $w (@where) {
+ if (($num % 3) == 0) {
+ $html .= '<tr>';
+ };
+ $html .= $q->td({-width=>"1%",-style=>"padding-right: 4px;"},
+ $q->checkbox( { -name=>"qw",
+ -label=>"",
+ -checked=>(ina($param->{'qw'},$w) ? 'checked' : undef),
+ -onDblClick=>"javascript:qw_off_except(this);",
+ -override=>1,
+ -value=>$w } )
+ ).
+ $q->td({-style=>"padding-right: 10px;"},
+ $labels->{$w}
+ );
+ if (($num % 3) == 2) {
+ $html .= '</tr>';
+ };
+ $num++;
+ }
+ $q->table({-border=>0,-cellpadding=>0,-cellspacing=>0,-width=>"1%"},
+ $html
+ );
+ }
+ )
+ )
+ )
+ )
+ .
+ $q->span({-id=>"server"},'<!-- Dynamic content target DIV -->').
+ $q->div({-id=>"server_hidden",-style=>"visibility: hidden; position: absolute;"},
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0},
+ $q->Tr(
+ $q->td({-align=>"left",-valign=>"top",-style=>"width: 16px;"},
+ $q->img({-src=>"icons/server.png"})
+ ),
+ $q->td({-align=>"left",-valign=>"top",-style=>"width: 100px;"},
+ "Servers"
+ ),
+ $q->td({-align=>"left"},
+ eval {
+ my $html ="";
+ my $num = 0;
+ my $groups = {};
+ foreach my $server (sort {$a cmp $b} keys %{ $config->{servers} }) {
+ if (($num % 4) == 0) {
+ $html .= '<tr>';
+ };
+ $html .= $q->td({-width=>"1%",-style=>"padding-right: 4px;"},
+ $q->checkbox( { -name=>"sr",
+ -label=>"",
+ -id=>(edt($config->{servers}->{$server},'group') ? $config->{servers}->{$server}->{group} : "-XXX"),
+ -checked=>(ina($param->{'sr'},$server) ? 'checked' : undef),
+ -override=>1,
+ -onDblClick=>"javascript:sr_off_except(this);",
+ -onChange=>"javascript:sr_changed();",
+ -value=>$server } )
+ ).
+ $q->td({-width=>"1%",-style=>"padding-right: 10px;"},
+ $server
+ );
+ if (($num % 4) == 3) {
+ $html .= '<td>&nbsp;</td></tr>';
+ };
+ $num++;
+ if (edt($config->{servers}->{$server},'group')) {
+ $groups->{$config->{servers}->{$server}->{group}} = '{'.$config->{servers}->{$server}->{group}.'}';
+ };
+ };
+ if (($num % 4) != 0) {
+ $html .= '<td>&nbsp;</td>' x ((4-($num % 4))*2);
+ $html .= '<td>&nbsp;</td></tr>';
+ };
+ $groups->{'-all'} = 'All servers';
+ $groups->{'-custom'} = 'Custom selection';
+ $q->table({-border=>0,-cellpadding=>0,-cellspacing=>0,-width=>"1%"},
+ $q->Tr(
+ $q->td({-colspan=>9,-align=>"left",-style=>"padding-bottom: 4px;"},
+ $q->popup_menu({ -name=>"ss",
+ -id=>"ss",
+ -style=>"width: 400px;",
+ -values=>[ sort {$a cmp $b} keys(%{$groups}) ],
+ -labels=>$groups,
+ -onChange=>"javascript:ss_changed();",
+ -default=>(exists($param->{'ss'}) ? ($param->{'ss'} || '-all') : '-all'),
+ -override=>1})
+ )
+ ),
+ $html
+ );
+ }
+ )
+ )
+ )
+ )
+ .
+ $q->span({-id=>"time"},'<!-- Dynamic content target DIV -->').
+ $q->div({-id=>"time_hidden",-style=>"visibility: hidden; position: absolute;"},
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0},
+ $q->Tr(
+ $q->td({-align=>"left",-style=>"width: 16px;"},
+ $q->img({-src=>"icons/timerange.png"})
+ ),
+ $q->td({-align=>"left",-style=>"width: 100px;"},
+ "Time Range"
+ ),
+ $q->td({-align=>"left"},
+ $q->popup_menu({ -name=>"tr",
+ -id=>"tr",
+ -style=>"width: 125px;",
+ -values=>[ 'custom',
+ '-1m',
+ '-5m',
+ '-10m',
+ '-30m',
+ '-1h',
+ '-6h',
+ '-12h',
+ '-1d',
+ '-2d',
+ '-3d',
+ '-7d',
+ '0' ],
+ -labels=>{ 'custom' => 'Custom',
+ '-1m' => 'Last minute',
+ '-5m' => 'Last 5 minutes',
+ '-10m' => 'Last 10 minutes',
+ '-30m' => 'Last 30 minutes',
+ '-1h' => 'Last hour',
+ '-6h' => 'Last 6 hours',
+ '-12h' => 'Last 12 hours',
+ '-1d' => 'Last 24 hours',
+ '-2d' => 'Last 2 days',
+ '-3d' => 'Last 3 days',
+ '-7d' => 'Last 7 days',
+ '0' => 'Unlimited' },
+ -onChange=>"javascript:document.getElementById('tds').value='';document.getElementById('tde').value='';",
+ -default=>(exists($param->{'tr'}) ? $param->{'tr'} : '-1h'),
+ -override=>1})
+ ),
+ $q->td({-align=>"left",-style=>"padding-right:0px;"},
+ $q->input({-name=>"tds",
+ -style=>"width: 105px;",
+ -value=>(exists($param->{'tds'}) ? $param->{'tds'} : ''),
+ -override=>1,
+ -onFocus=>"javascript:document.getElementById('tr').selectedIndex = 0; document.getElementById('tde').value='';",
+ -id=>"tds" }).
+ $q->button({ -onClick=>"javascript:document.getElementById('tr').selectedIndex = 0; document.getElementById('tde').value=''; cal1x.select(document.forms[0].tds,'anchor1x','yyyy-MM-dd'); return false;",
+ -name=>"X",
+ -style=>"height: 18px; width: 18px;",
+ -id=>"anchor1x" })."&nbsp;-&nbsp;".
+ $q->div({ -id=>'caldiv1x',
+ -style=>"position:absolute;visibility:hidden;background-color:white;layer-background-color:white;" })
+ ),
+ $q->td({-align=>"left",-style=>"padding-left:0px;"},
+ $q->input({-name=>"tde",
+ -style=>"width: 105px;",
+ -value=>(exists($param->{'tde'}) ? $param->{'tde'} : ''),
+ -override=>1,
+ -onFocus=>"javascript:document.getElementById('tr').selectedIndex = 0",
+ -id=>"tde" }).
+ $q->button({ -onClick=>"javascript:document.getElementById('tr').selectedIndex = 0; cal2x.select(document.forms[0].tde,'anchor2x','yyyy-MM-dd'); return false;",
+ -name=>"X",
+ -style=>"height: 18px; width: 18px;",
+ -id=>"anchor2x" }).
+ $q->div({ -id=>'caldiv2x',
+ -style=>"position:absolute;visibility:hidden;background-color:white;layer-background-color:white;" })
+ )
+ )
+ )
+ )
+ .
+ '<hr>'
+ .
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0,-align=>"center"},
+ $q->Tr(
+ $q->td({-align=>"center"},
+ $q->submit({-name=>"sm",-value=>"Start Query"})
+ )
+ )
+ )
+
+ )
+ );
+
+ print "\n".
+ '
+ <script language="JavaScript">
+ init_controls();
+ switch_controls(document.getElementById("qt").options[document.getElementById("qt").selectedIndex].value);
+ </script>
+ '
+ ."\n";
+};
+
+1;
diff --git a/cgi/exilog_cgi_param.pm b/cgi/exilog_cgi_param.pm
new file mode 100644
index 0000000..5096ee3
--- /dev/null
+++ b/cgi/exilog_cgi_param.pm
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+#
+# This file is part of the exilog suite.
+#
+# http://duncanthrax.net/exilog/
+#
+# (c) Tom Kistner 2004
+#
+# See LICENSE for licensing information.
+#
+
+package exilog_cgi_param;
+use strict;
+use exilog_cgi_html;
+use exilog_config;
+
+use Data::Dumper;
+
+BEGIN {
+ use Exporter;
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+ $VERSION = 0.1;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ $param
+ );
+
+ %EXPORT_TAGS = ();
+ @EXPORT_OK = qw();
+
+ use vars qw( $param );
+}
+
+$param = _init_cgi_params();
+
+sub _init_cgi_params {
+ my $param = {};
+
+ foreach ($q->param) {
+ my @test = $q->param($_);
+
+ if ((scalar @test) > 1) {
+ $param->{$_} = \@test;
+ }
+ else {
+ $param->{$_} = $test[0];
+ };
+ };
+
+ # defaults
+ my $defaults = {
+ 'tab' => 'messages',
+ 'qw' => [ 'messages',
+ 'errors',
+ 'deliveries',
+ 'deferrals',
+ 'rejects',
+ 'queue' ],
+ 'ss' => '-all',
+ 'tr' => '-10m',
+ #'qt' => 'all',
+ 'qs' => "",
+ 'sr' => [ keys %{ $config->{servers} } ]
+ };
+
+ foreach (keys %{ $defaults }) {
+ $param->{$_} = $defaults->{$_} unless exists($param->{$_});
+ };
+ return $param;
+};
+
+
+1;
diff --git a/cgi/exilog_cgi_queues.pm b/cgi/exilog_cgi_queues.pm
new file mode 100644
index 0000000..ec018a9
--- /dev/null
+++ b/cgi/exilog_cgi_queues.pm
@@ -0,0 +1,131 @@
+#!/usr/bin/perl -w
+#
+# This file is part of the exilog suite.
+#
+# http://duncanthrax.net/exilog/
+#
+# (c) Tom Kistner 2004
+#
+# See LICENSE for licensing information.
+#
+
+package exilog_cgi_queues;
+use exilog_config;
+use exilog_cgi_html;
+use exilog_cgi_param;
+use exilog_sql;
+use exilog_util;
+use strict;
+
+BEGIN {
+ use Exporter;
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+ # set the version for version checking
+ $VERSION = 0.1;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &queues
+ );
+
+ %EXPORT_TAGS = ();
+
+ # your exported package globals go here,
+ # as well as any optionally exported functions
+ @EXPORT_OK = qw();
+}
+
+
+sub queues {
+ _print_Queue_selector();
+ my $messages = sql_select('queue',[ '*' ]);
+ print render_queue_table($messages);
+};
+
+
+sub _print_Queue_selector {
+
+ print
+ $q->div({-class=>"top_spacer"},
+ $q->div({-align=>"left",-style=>"padding: 10px; border: 1px solid black; background: #eeeeee;"},
+
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0},
+ $q->Tr(
+ $q->td({-align=>"left",-valign=>"top",-style=>"width: 16px;"},
+ $q->img({-src=>"icons/server.png"})
+ ),
+ $q->td({-align=>"left",-valign=>"top",-style=>"width: 100px;"},
+ "Servers"
+ ),
+ $q->td({-align=>"left"},
+ eval {
+ my $html ="";
+ my $num = 0;
+ my $groups = {};
+ foreach my $server (sort {$a cmp $b} keys %{ $config->{servers} }) {
+ if (($num % 4) == 0) {
+ $html .= '<tr>';
+ };
+ $html .= $q->td({-width=>"1%",-style=>"padding-right: 4px;"},
+ $q->checkbox( { -name=>"sr",
+ -label=>"",
+ -id=>(edt($config->{servers}->{$server},'group') ? $config->{servers}->{$server}->{group} : "-XXX"),
+ -checked=>(ina($param->{'sr'},$server) ? 'checked' : undef),
+ -override=>1,
+ -onDblClick=>"javascript:sr_off_except(this);",
+ -onChange=>"javascript:sr_changed();",
+ -value=>$server } )
+ ).
+ $q->td({-width=>"1%",-style=>"padding-right: 10px;"},
+ $server
+ );
+ if (($num % 4) == 3) {
+ $html .= '<td>&nbsp;</td></tr>';
+ };
+ $num++;
+ if (edt($config->{servers}->{$server},'group')) {
+ $groups->{$config->{servers}->{$server}->{group}} = '{'.$config->{servers}->{$server}->{group}.'}';
+ };
+ };
+ if (($num % 4) != 0) {
+ $html .= '<td>&nbsp;</td>' x ((4-($num % 4))*2);
+ $html .= '<td>&nbsp;</td></tr>';
+ };
+ $groups->{'-all'} = 'All servers';
+ $groups->{'-custom'} = 'Custom selection';
+ $q->table({-border=>0,-cellpadding=>0,-cellspacing=>0,-width=>"1%"},
+ $q->Tr(
+ $q->td({-colspan=>9,-align=>"left",-style=>"padding-bottom: 4px;"},
+ $q->popup_menu({ -name=>"ss",
+ -id=>"ss",
+ -style=>"width: 400px;",
+ -values=>[ sort {$a cmp $b} keys(%{$groups}) ],
+ -labels=>$groups,
+ -onChange=>"javascript:ss_changed();",
+ -default=>(exists($param->{'ss'}) ? ($param->{'ss'} || '-all') : '-all'),
+ -override=>1})
+ )
+ ),
+ $html
+ );
+ }.($@ ? $@ : "")
+ )
+ )
+ )
+ .
+ '<hr>'
+ .
+ $q->table({-cellspacing=>0,-cellpadding=>4,-border=>0,-align=>"center"},
+ $q->Tr(
+ $q->td({-align=>"center"},
+ $q->submit({-name=>"sm",-value=>"Start Query"})
+ )
+ )
+ )
+
+ )
+ );
+};
+
+1;
+
diff --git a/cgi/exilog_cgi_servers.pm b/cgi/exilog_cgi_servers.pm
new file mode 100644
index 0000000..424abf1
--- /dev/null
+++ b/cgi/exilog_cgi_servers.pm
@@ -0,0 +1,102 @@
+#!/usr/bin/perl -w
+#
+# This file is part of the exilog suite.
+#
+# http://duncanthrax.net/exilog/
+#
+# (c) Tom Kistner 2004
+#
+# See LICENSE for licensing information.
+#
+
+package exilog_cgi_servers;
+use exilog_config;
+use exilog_cgi_html;
+use exilog_cgi_param;
+use exilog_sql;
+use exilog_util;
+use strict;
+
+BEGIN {
+ use Exporter;
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+ # set the version for version checking
+ $VERSION = 0.1;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &servers
+ );
+
+ %EXPORT_TAGS = ();
+
+ # your exported package globals go here,
+ # as well as any optionally exported functions
+ @EXPORT_OK = qw();
+}
+
+
+sub _get_num_queued {
+ my $server = shift;
+ my $h = {};
+
+ $h->{queued} = sql_count('queue',{ 'server' => $server });
+
+ $h->{frozen} = sql_count('queue',{ 'server' => $server,
+ 'frozen' => '1' } );
+
+ $h->{frozen_bounce} = sql_count('queue',{ 'server' => $server,
+ 'mailfrom' => '<>',
+ 'frozen' => '1' } );
+
+ my $tmp = sql_count('queue',{ 'server' => $server,
+ 'mailfrom' => '<>' } );
+
+ $h->{deferred} = $h->{queued} - $h->{frozen};
+ $h->{deferred_bounce} = $tmp - $h->{frozen_bounce};
+
+ return $h;
+}
+
+sub _get_h24_stats {
+ my $server = shift;
+ my $now = time();
+ my $h = {};
+
+ $h->{arrivals} = int( sql_count( 'messages',
+ { 'server' => $server,
+ 'timestamp' => $now-86400 } ) / 1 );
+
+ $h->{deliveries} = int( sql_count( 'deliveries',
+ { 'server' => $server,
+ 'timestamp' => $now-86400 } ) / 1 );
+
+ $h->{errors} = int( sql_count( 'errors',
+ { 'server' => $server,
+ 'timestamp' => $now-86400 } ) / 1 );
+
+ my $sizes = sql_select( 'messages', [ 'size' ], { 'server' => $server,
+ 'timestamp' => $now-86400 } );
+
+ my $total = 0;
+ foreach (@{ $sizes }) { $total+=$_->{size}; };
+ if ((scalar @{ $sizes }) > 0) {
+ $h->{avg_msg_size} = int($total/(scalar @{ $sizes }));
+ }
+ else {
+ $h->{avg_msg_size} = 0;
+ };
+
+ return $h;
+}
+
+sub servers {
+
+ #print $q->div({-style=>"font-size: 28px; font-weight: bold;"},"Basic statictics for all servers");
+
+ foreach my $server (sort {$a cmp $b} keys %{ $config->{servers} }) {
+ print render_server($server,_get_num_queued($server),_get_h24_stats($server));
+ };
+}
+
+1;