summaryrefslogtreecommitdiffstats
path: root/cgi/exilog_cgi_html.pm
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/exilog_cgi_html.pm
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/exilog_cgi_html.pm')
-rw-r--r--cgi/exilog_cgi_html.pm984
1 files changed, 984 insertions, 0 deletions
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;