diff --git a/src/statusmail/plugins/graphs.pm b/src/statusmail/plugins/graphs.pm
new file mode 100644
index 000000000..5b72c6e1a
--- /dev/null
+++ b/src/statusmail/plugins/graphs.pm
@@ -0,0 +1,697 @@
+#!/usr/bin/perl
+
+############################################################################
+#                                                                          #
+# Send log and status emails for IPFire                                    #
+#                                                                          #
+# This is free software; you can redistribute it and/or modify             #
+# it under the terms of the GNU General Public License as published by     #
+# the Free Software Foundation; either version 3 of the License, or        #
+# (at your option) any later version.                                      #
+#                                                                          #
+# This is distributed in the hope that it will be useful,                  #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+# GNU General Public License for more details.                             #
+#                                                                          #
+# You should have received a copy of the GNU General Public License        #
+# along with IPFire; if not, write to the Free Software                    #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
+#                                                                          #
+# Copyright (C) 2018 - 2019 The IPFire Team                                #
+#                                                                          #
+############################################################################
+
+use strict;
+use warnings;
+
+require "${General::swroot}/lang.pl";
+require "${General::swroot}/graphs.pl";
+
+package Graphs;
+
+############################################################################
+# Function prototypes
+############################################################################
+
+sub add_graph( $$$$@ );
+
+############################################################################
+# BEGIN Block
+#
+# Register the graphs available in this file.
+#
+# Note that some graphs are only available under certain circumstances, so
+# it's necessary to check the circumstances apply.
+############################################################################
+
+sub BEGIN
+{
+  my %netsettings;
+  my %mainsettings;
+
+  &General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
+  &General::readhash("${General::swroot}/main/settings", \%mainsettings);
+
+  my $config_type = $netsettings{'CONFIG_TYPE'};
+
+  my %common_options = ( 'section'    => $Lang::tr{'graph'},
+                         'format'     => 'html' );
+
+  #----------------------------------------------------------------------------
+  # Network
+
+  if ($netsettings{'RED_TYPE'} ne 'PPPOE')
+  {
+    if ($netsettings{'RED_DEV'} ne $netsettings{'GREEN_DEV'})
+    {
+      if ($netsettings{'RED_DEV'} eq 'red0')
+      {
+        main::add_mail_item( %common_options,
+                            'ident'       => 'graph-network-red0',
+                            'subsection'  => $Lang::tr{'interfaces'},
+                            'item'        => 'red0',
+                            'function'    => \&red0 );
+      }
+      else
+      {
+        main::add_mail_item( %common_options,
+                            'ident'       => 'graph-network-ppp0',
+                            'subsection'  => $Lang::tr{'interfaces'},
+                            'item'        => 'ppp0',
+                            'function'    => \&ppp0 );
+      }
+    }
+  }
+  else
+  {
+    main::add_mail_item( %common_options,
+                        'ident'       => 'graph-network-ppp0',
+                        'subsection'  => $Lang::tr{'interfaces'},
+                        'item'        => 'ppp0',
+                        'function'    => \&ppp0 );
+  }
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-network-green0',
+                       'subsection'  => $Lang::tr{'interfaces'},
+                       'item'        => 'green0',
+                       'function'    => \&green0 );
+
+  if ($config_type == 3 or $config_type == 4)
+  {
+    # BLUE
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-network-blue0',
+                         'subsection'  => $Lang::tr{'interfaces'},
+                         'item'        => 'blue0',
+                         'function'    => \&blue0 );
+  }
+
+  if ($config_type == 2 or $config_type == 4)
+  {
+    # ORANGE
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-network-orange0',
+                         'subsection'  => $Lang::tr{'interfaces'},
+                         'item'        => 'orange0',
+                         'function'    => \&orange0 );
+  }
+
+
+  if (-e "/var/log/rrd/collectd/localhost/interface/if_octets-ipsec0.rrd")
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-network-ipsec0',
+                         'subsection'  => $Lang::tr{'network'},
+                         'item'        => 'ipsec0',
+                         'function'    => \&ipsec0 );
+  }
+
+  if (-e "/var/log/rrd/collectd/localhost/interface/if_octets-tun0.rrd")
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-network-tun0',
+                         'subsection'  => $Lang::tr{'network'},
+                         'item'        => 'tun0',
+                         'function'    => \&tun0 );
+  }
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-network-fwhits',
+                       'subsection'  => $Lang::tr{'network'},
+                       'item'        => $Lang::tr{'firewallhits'},
+                       'function'    => \&fw_hits );
+
+  #----------------------------------------------------------------------------
+  # System
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-system-cpu-usage',
+                       'subsection'  => $Lang::tr{'system'},
+                       'item'        => "CPU $Lang::tr{'graph'}",
+                       'function'    => \&cpu_usage );
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-system-cpu-load',
+                       'subsection'  => $Lang::tr{'system'},
+                       'item'        => "Load $Lang::tr{'graph'}",
+                       'function'    => \&cpu_load );
+
+  if ( -e "$mainsettings{'RRDLOG'}/collectd/localhost/cpufreq/cpufreq-0.rrd")
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-system-cpu-frequency',
+                         'subsection'  => $Lang::tr{'system'},
+                         'item'        => "CPU $Lang::tr{'frequency'}",
+                         'function'    => \&cpu_freq );
+  }
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-system-entropy',
+                       'subsection'  => $Lang::tr{'system'},
+                       'item'        => $Lang::tr{'entropy'},
+                       'function'    => \&entropy );
+
+  #----------------------------------------------------------------------------
+  # Hardware
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-hardware-cpu-load',
+                       'subsection'  => $Lang::tr{'hardware graphs'},
+                       'item'        => "Load $Lang::tr{'graph'}",
+                       'function'    => \&cpu_load );
+
+  if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/thermal-thermal_zone* 2>/dev/null` )
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-hardware-acpi-zone-temp',
+                         'subsection'  => $Lang::tr{'hardware graphs'},
+                         'item'        => "ACPI Thermal-Zone Temp",
+                         'function'    => \&therm );
+  }
+
+  if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/sensors-*/temperature-* 2>/dev/null` )
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-hardware-temp',
+                         'subsection'  => $Lang::tr{'hardware graphs'},
+                         'item'        => "hwtemp",
+                         'function'    => \&hwtemp );
+  }
+
+  if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/sensors-*/fanspeed-* 2>/dev/null` )
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-hardware-fan',
+                         'subsection'  => $Lang::tr{'hardware graphs'},
+                         'item'        => "hwfan",
+                         'function'    => \&hwfan );
+  }
+
+  if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/sensors-*/voltage-* 2>/dev/null` )
+  {
+    main::add_mail_item( %common_options,
+                         'ident'       => 'graph-hardware-volt',
+                         'subsection'  => $Lang::tr{'hardware graphs'},
+                         'item'        => "hwvolt",
+                         'function'    => \&hwvolt );
+  }
+
+  #----------------------------------------------------------------------------
+  # Memory
+
+  main::add_mail_item( %common_options,
+                      'ident'       => 'graph-memory-memory',
+                      'subsection'  => $Lang::tr{'memory'},
+                      'item'        => $Lang::tr{'memory'},
+                      'function'    => \&memory );
+
+  main::add_mail_item( %common_options,
+                       'ident'       => 'graph-memory-swap',
+                       'subsection'  => $Lang::tr{'memory'},
+                       'item'        => $Lang::tr{'swap'},
+                       'function'    => \&swap );
+
+  #----------------------------------------------------------------------------
+  # Disks
+
+  foreach my $path (glob '/var/log/rrd/collectd/localhost/disk*')
+  {
+    my ($name) = $path =~ m/disk\-(\w+)/;
+
+    main::add_mail_item( %common_options,
+                         'ident'       => "graph-disk-access-$name",
+                         'subsection'  => $Lang::tr{'statusmail disk access'},
+                         'item'        => $name,
+                         'function'    => sub { my ($this) = @_; diskaccess( $this, $name ); } );
+
+    main::add_mail_item( %common_options,
+                         'ident'       => "graph-disk-temp-$name",
+                         'subsection'  => $Lang::tr{'statusmail disk temperature'},
+                         'item'        => $name,
+                         'function'    => sub { my ($this) = @_; disktemp( $this, $name ); } );
+  }
+
+# Other graphs that aren't available.
+# 	updatepinggraph( host, period )                                     : netother.cgi
+# 	updateprocessescpugraph( period )
+# 	updateprocessesmemorygraph( period )
+# 	updateqosgraph( device, period )        red0 | ppp0 | imq0          : qos.cgi
+# 	updatevpngraph( interface, period )                                 : netovpnrw.cgi
+# 	updatevpnn2ngraph( interface, period )                              : netovpnsrv.cgi
+# 	updatewirelessgraph( interface, period )
+}
+
+############################################################################
+# Code
+############################################################################
+
+#------------------------------------------------------------------------------
+# sub add_graph( object, function, name, alternate[, params...] )
+#
+# Adds a graph to the mail message.  This runs a sub-process to capture the
+# output from running the standard WUI's graphing function, which is sent to
+# stdout.
+#
+# Parameters:
+#   this       message object
+#   function   function producing graph
+#   name       name of graph file
+#   alternate  alternate text for image
+#   params     parameters to be passed to graph function
+#------------------------------------------------------------------------------
+
+sub add_graph( $$$$@ )
+{
+  my ($this, $function, $name, $alternate, @params) = @_;
+
+  my $from_child;
+
+  my $pid = open( $from_child, "-|" );
+
+  if ($pid)
+  {   # parent
+    binmode $from_child;
+
+    $this->add_image( fh   => $from_child,
+                      alt  => $alternate,
+                      type => 'image/png',
+                      name => $name );
+
+    waitpid( $pid, 0 );
+    close $from_child;
+  }
+  else
+  {      # child
+    binmode( STDOUT );
+
+    my $period = $this->get_period();
+
+    &$function( @params, $period );
+
+    exit;
+  }
+}
+
+
+#------------------------------------------------------------------------------
+# sub ppp0( this )
+#
+# Adds a graph of the ppp0 interface throughput
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub ppp0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'ppp0_if.png', 'ppp0 interface throughput', 'ppp0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub red0( this )
+#
+# Adds a graph of the red0 interface throughput
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub red0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'red0_if.png', 'red0 interface throughput', 'red0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub green0( this )
+#
+# Adds a graph of the green0 interface throughput
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub green0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'green0_if.png', 'green0 interface throughput', 'green0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub blue0( this )
+#
+# Adds a graph of the blue0 interface throughput
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub blue0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'blue0_if.png', 'blue0 interface throughput', 'blue0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub orange0( this )
+#
+# Adds a graph of the orange0 interface throughput
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub orange0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'orange0_if.png', 'orange0 interface throughput', 'orange0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub ipsec0( this )
+#
+# Adds a graph of the ipsec0 interface
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub ipsec0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'ipsec0_if.png', 'ipsec0 interface throughput', 'ipsec0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub tun0( this )
+#
+# Adds a graph of the tun0 interface
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub tun0( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateifgraph, 'tun0_if.png', 'tun0 interface throughput', 'tun0' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub cpu_usage( this )
+#
+# Adds a graph of the CPU usage
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub cpu_usage( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatecpugraph, 'cpu_usage.png', "CPU $Lang::tr{'graph'}" );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub cpu_freq( this )
+#
+# Adds a graph of the CPU frequency
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub cpu_freq( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatecpufreqgraph, 'cpu_freq.png', "CPU $Lang::tr{'frequency'}" );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub cpu_load( this )
+#
+# Adds a graph of the CPU load
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub cpu_load( $$ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateloadgraph,, 'cpu_load.png', "Load $Lang::tr{'graph'}" );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub fw_hits( this )
+#
+# Adds a graph of the Firewall hits
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub fw_hits( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatefwhitsgraph, 'fw_hits.png', $Lang::tr{'firewallhits'} );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub therm( this )
+#
+# Adds a graph of the ACPI Thermal zone temperatures
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub therm( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatethermaltempgraph, 'therm.png', "ACPI Thermal-Zone Temp" );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub hwtemp( this )
+#
+# Adds a graph of the Hardware Temperatures
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub hwtemp( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatehwtempgraph, 'hw_temp.png', 'hwtemp' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub hwfan( this )
+#
+# Adds a graph of the Fan Speeds
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub hwfan( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatehwfangraph, 'hw_fan.png', 'hwfan' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub hwvolt( this )
+#
+# Adds a graph of the Hardware voltages
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub hwvolt( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatehwvoltgraph, 'hw_volt.png', 'hw volt' );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub entropy( this )
+#
+# Adds a graph of the Entropy
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub entropy( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateentropygraph, 'entropy.png', $Lang::tr{'entropy'} );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub memory( this )
+#
+# Adds a graph of the memory usage
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub memory( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updatememorygraph, 'memory.png', $Lang::tr{'memory'} );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub swap( this )
+#
+# Adds a graph of the swapfile usage
+#
+# Parameters:
+#   this  message object
+#------------------------------------------------------------------------------
+
+sub swap( $ )
+{
+  my ($this) = @_;
+
+  add_graph( $this, \&Graphs::updateswapgraph, 'swap.png', $Lang::tr{'swap'} );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub diskaccess( this, name )
+#
+# Adds a graph of the disk access rate
+#
+# Parameters:
+#   this  message object
+#   name  disk name
+#------------------------------------------------------------------------------
+
+sub diskaccess( $$ )
+{
+  my ($this, $name) = @_;
+
+  add_graph( $this, \&Graphs::updatediskgraph, "disk_access_$name.png", $name, $name );
+
+  return 1;
+}
+
+
+#------------------------------------------------------------------------------
+# sub updatehddgraph( this, name )
+#
+# Adds a graph of the disk temperature
+#
+# Parameters:
+#   this  message object
+#   name  disk name
+#------------------------------------------------------------------------------
+
+sub disktemp( $$ )
+{
+  my ($this, $name) = @_;
+
+  add_graph( $this, \&Graphs::updatehddgraph, "disk_temp_$name.png", $name, $name );
+
+  return 1;
+}
diff --git a/src/statusmail/plugins/hardware_media_space.pm b/src/statusmail/plugins/hardware_media_space.pm
new file mode 100644
index 000000000..ce3db2def
--- /dev/null
+++ b/src/statusmail/plugins/hardware_media_space.pm
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+
+############################################################################
+#                                                                          #
+# Send log and status emails for IPFire                                    #
+#                                                                          #
+# This is free software; you can redistribute it and/or modify             #
+# it under the terms of the GNU General Public License as published by     #
+# the Free Software Foundation; either version 3 of the License, or        #
+# (at your option) any later version.                                      #
+#                                                                          #
+# This is distributed in the hope that it will be useful,                  #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+# GNU General Public License for more details.                             #
+#                                                                          #
+# You should have received a copy of the GNU General Public License        #
+# along with IPFire; if not, write to the Free Software                    #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
+#                                                                          #
+# Copyright (C) 2018 - 2019 The IPFire Team                                #
+#                                                                          #
+############################################################################
+
+use strict;
+use warnings;
+
+require "${General::swroot}/lang.pl";
+
+package Hardware_Media_Space;
+
+############################################################################
+# Function prototypes
+############################################################################
+
+sub space( $$ );
+sub inodes( $$ );
+
+############################################################################
+# BEGIN Block
+#
+# Register the log items available in this file
+############################################################################
+
+sub BEGIN
+{
+  main::add_mail_item( 'ident'      => 'hardware-media-space',
+                       'section'    => $Lang::tr{'statusmail hardware'},
+                       'subsection' => $Lang::tr{'media'},
+                       'item'       => $Lang::tr{'disk usage'},
+                       'function'   => \&space,
+                       'option'     => { 'type'   => 'integer',
+                                         'name'   => $Lang::tr{'statusmail max free percent'},
+                                         'min'    => 0,
+                                         'max'    => 100 } );
+
+  main::add_mail_item( 'ident'      => 'hardware-media-inodes',
+                       'section'    => $Lang::tr{'statusmail hardware'},
+                       'subsection' => $Lang::tr{'media'},
+                       'item'       => 'inodes',
+                       'function'   => \&inodes,
+                       'option'     => { 'type'   => 'integer',
+                                         'name'   => $Lang::tr{'statusmail max free percent'},
+                                         'min'    => 0,
+                                         'max'    => 100 } );
+}
+
+############################################################################
+# Code
+############################################################################
+
+#------------------------------------------------------------------------------
+# sub space( this, min_percent )
+#
+# Adds the disk usage in terms of space used.
+#
+# Parameters:
+#   this         message object
+#   min_percent  Only display information if this amount of space or less is
+#                free
+#------------------------------------------------------------------------------
+
+sub space( $$ )
+{
+  my $message     = shift;
+  my $min_percent = 100 - shift;
+  my @lines;
+
+  # Get the process information
+
+  foreach my $line (`df -BM`)
+  {
+    my @fields = split /\s+/, $line, 6;
+    if ($fields[4] =~ m/\d+\%/)
+    {
+      my ($percent) = $fields[4] =~ m/(\d+)\%/;
+      next if ($percent <= $min_percent);
+    }
+    push @lines, [ @fields ];
+  }
+
+  if (@lines > 1)
+  {
+    $message->add_table( @lines );
+
+    return 1;
+  }
+
+  return 0;
+}
+
+
+#------------------------------------------------------------------------------
+# sub inodes( this, min_percent )
+#
+# Adds the disk usage in terms of inodes used.
+#
+# Parameters:
+#   this         message object
+#   min_percent  Only display information if this number of inodes or less is
+#                free
+#------------------------------------------------------------------------------
+
+sub inodes( $$ )
+{
+  my $message     = shift;
+  my $min_percent = 100 - shift;
+  my @lines;
+
+  # Get the process information
+
+  foreach my $line (`df -i`)
+  {
+    my @fields = split /\s+/, $line, 6;
+    next if ($fields[1] == 0);
+    if ($fields[4] =~ m/\d+\%/)
+    {
+      my ($percent) = $fields[4] =~ m/(\d+)\%/;
+      next if ($percent <= $min_percent);
+    }
+    push @lines, [ @fields ];
+  }
+
+  if (@lines > 1)
+  {
+    $message->add_table( @lines );
+
+    return 1;
+  }
+
+  return 0;
+}
+
+1;
diff --git a/src/statusmail/plugins/network_firewall.pm b/src/statusmail/plugins/network_firewall.pm
new file mode 100644
index 000000000..1abe4e482
--- /dev/null
+++ b/src/statusmail/plugins/network_firewall.pm
@@ -0,0 +1,357 @@
+#!/usr/bin/perl
+
+############################################################################
+#                                                                          #
+# Send log and status emails for IPFire                                    #
+#                                                                          #
+# This is free software; you can redistribute it and/or modify             #
+# it under the terms of the GNU General Public License as published by     #
+# the Free Software Foundation; either version 3 of the License, or        #
+# (at your option) any later version.                                      #
+#                                                                          #
+# This is distributed in the hope that it will be useful,                  #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+# GNU General Public License for more details.                             #
+#                                                                          #
+# You should have received a copy of the GNU General Public License        #
+# along with IPFire; if not, write to the Free Software                    #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
+#                                                                          #
+# Copyright (C) 2018 - 2019 The IPFire Team                                #
+#                                                                          #
+############################################################################
+
+require "${General::swroot}/lang.pl";
+
+use strict;
+use warnings;
+
+package Network_Firewall;
+
+use Time::Local;
+
+require "${General::swroot}/geoip-functions.pl";
+
+############################################################################
+# BEGIN Block
+#
+# Register the log items available in this file
+############################################################################
+
+sub BEGIN
+{
+  main::add_mail_item( 'ident'      => 'network-firewall-ipaddresses',
+                       'section'    => $Lang::tr{'network'},
+                       'subsection' => $Lang::tr{'firewall'},
+                       'item'       => $Lang::tr{'ip address'},
+                       'function'   => \&addresses,
+                       'option'     => { 'type'   => 'integer',
+                                         'name'   => $Lang::tr{'statusmail firewall min count'},
+                                         'min'    => 1,
+                                         'max'    => 1000 } );
+
+  main::add_mail_item( 'ident'      => 'network-firewall-ports',
+                       'section'    => $Lang::tr{'network'},
+                       'subsection' => $Lang::tr{'firewall'},
+                       'item'       => $Lang::tr{port},
+                       'function'   => \&ports,
+                       'option'     => { 'type'   => 'integer',
+                                         'name'   => $Lang::tr{'statusmail firewall min count'},
+                                         'min'    => 1,
+                                         'max'    => 1000 } );
+
+  main::add_mail_item( 'ident'      => 'network-firewall-countries',
+                       'section'    => $Lang::tr{'network'},
+                       'subsection' => $Lang::tr{'firewall'},
+                       'item'       => $Lang::tr{country},
+                       'function'   => \&countries,
+                       'option'     => { 'type'   => 'integer',
+                                         'name'   => $Lang::tr{'statusmail firewall min count'},
+                                         'min'    => 1,
+                                         'max'    => 1000 } );
+
+  main::add_mail_item( 'ident'      => 'network-firewall-reason',
+                       'section'    => $Lang::tr{'network'},
+                       'subsection' => $Lang::tr{'firewall'},
+                       'item'       => $Lang::tr{'statusmail firewall reason'},
+                       'function'   => \&reasons,
+                       'option'     => { 'type'   => 'integer',
+                                         'name'   => $Lang::tr{'statusmail firewall min count'},
+                                         'min'    => 1,
+                                         'max'    => 1000 } );
+}
+
+
+############################################################################
+# Functions
+############################################################################
+
+sub get_log( $ );
+sub addresses( $$ );
+
+#------------------------------------------------------------------------------
+# sub get_log( this )
+#
+# Gets information on blocked packets from the system log and caches it.
+#
+# Parameters:
+#   this  message object
+#
+# Returns:
+#   reference to hash of information
+#------------------------------------------------------------------------------
+
+sub get_log( $ )
+{
+  my ($this, $name) = @_;
+
+  my $data = $this->cache( 'network-firewall' );
+
+  return $data if (defined $data);
+
+  my %info;
+  my $line;
+
+  while ($line = $this->get_message_log_line)
+  {
+    next unless ($line);
+    next unless ($line =~ m/kernel: DROP/);
+
+    my ($time, $rule, $interface, $src_addrs, $dst_port) =
+        $line =~ m/(\w+\s+\d+\s+\d+:\d+:\d+).*DROP_(\w+?)\s*IN=(\w+).*SRC=(\d+\.\d+\.\d+\.\d+).*(?:DPT=(\d*))/;
+# mmm dd hh:mm:dd ipfire kernel: DROP_SPAMHAUS_EDROPIN=ppp0 OUT= MAC= SRC=999.999.999.999 DST=888.888.888.888 LEN=40 TOS=0x00 PREC=0x00 TTL=248 ID=35549 PROTO=TCP SPT=47851 DPT=28672 WINDOW=1024 RES=0x00 SYN URGP=0 MARK=0xd2
+
+    next unless ($src_addrs);
+
+    my $country = GeoIP::lookup( $src_addrs ) || $src_addrs;
+
+    $info{'by_address'}{$src_addrs}{'count'}++;
+    $info{'by_address'}{$src_addrs}{'first'} = $time unless ($info{'by_address'}{$src_addrs}{'first'});
+    $info{'by_address'}{$src_addrs}{'last'}  = $time;
+
+    if ($dst_port)
+    {
+      $info{'by_port'}{$dst_port}{'count'}++ ;
+      $info{'by_port'}{$dst_port}{'first'} = $time unless ($info{'by_port'}{$dst_port}{'first'});
+      $info{'by_port'}{$dst_port}{'last'}  = $time;
+    }
+
+    if ($country)
+    {
+      $info{'by_country'}{$country}{'count'}++;
+      $info{'by_country'}{$country}{'first'} = $time unless ($info{'by_country'}{$country}{'first'});
+      $info{'by_country'}{$country}{'last'}  = $time;
+    }
+
+    $info{'by_rule'}{$rule}{'count'}++;
+    $info{'by_rule'}{$rule}{'first'} = $time unless ($info{'by_rule'}{$rule}{'first'});
+    $info{'by_rule'}{$rule}{'last'}  = $time;
+
+    $info{'total'}++;
+  };
+
+  $this->cache( 'network-firewall', \%info );
+
+  return \%info;
+}
+
+
+#------------------------------------------------------------------------------
+# sub addresses( this, min_count )
+#
+# Output information on blocked addresses.
+#
+# Parameters:
+#   this       message object
+#   min_count  only output blocked addresses occurring at least this number of
+#              times
+#------------------------------------------------------------------------------
+
+sub addresses( $$ )
+{
+  my ($self, $min_count) = @_;
+  my @table;
+
+  use Sort::Naturally;
+
+  push @table, ['|', '|', '|', '|', '|', '|'];
+  push @table, [ $Lang::tr{'ip address'}, $Lang::tr{'country'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ];
+
+  my $stats = get_log( $self );
+
+  foreach my $address (sort { $$stats{'by_address'}{$b}{'count'} <=> $$stats{'by_address'}{$a}{'count'} ||
+                              ncmp( $b, $a ) } keys %{ $$stats{'by_address'} } )
+  {
+    my $count   = $$stats{'by_address'}{$address}{'count'};
+    my $country = GeoIP::lookup( $address );
+    my $first   = $$stats{'by_address'}{$address}{'first'};
+    my $last    = $$stats{'by_address'}{$address}{'last'};
+    my $percent = int( 100 * $count / $$stats{'total'} + 0.5);
+
+    last if ($count < $min_count);
+
+    my $name = $self->lookup_ip_address( $address );
+
+    $address = "$address\n$name" if ($name);
+
+    if ($country)
+    {
+      $country = GeoIP::get_full_country_name( $country) || $address;
+    }
+    else
+    {
+      $country = $Lang::tr{'unknown'};
+    }
+
+    push @table, [ $address, $country, $count, $percent, $first, $last ];
+
+    last if (@table > $self->get_max_lines_per_item + 2)
+  }
+
+  if (@table > 2)
+  {
+    $self->add_table( @table );
+
+    return 1;
+  }
+
+  return 0;
+}
+
+
+#------------------------------------------------------------------------------
+# sub ports( this, min_count )
+#
+# Output information on blocked ports.
+#
+# Parameters:
+#   this       message object
+#   min_count  only output blocked ports occurring at least this number of
+#              times
+#------------------------------------------------------------------------------
+
+sub ports( $$ )
+{
+  my ($self, $min_count) = @_;
+  my @table;
+
+  push @table, ['|', '|', '|', '|', '|'];
+  push @table, [ $Lang::tr{'port'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ];
+
+  my $stats = get_log( $self );
+
+  foreach my $port (sort { $$stats{'by_port'}{$b}{'count'} <=> $$stats{'by_port'}{$a}{'count'} ||
+                           ncmp( $b, $a ) } keys %{ $$stats{'by_port'} } )
+  {
+    my $count   = $$stats{'by_port'}{$port}{'count'};
+    my $first   = $$stats{'by_port'}{$port}{'first'};
+    my $last    = $$stats{'by_port'}{$port}{'last'};
+    my $percent = int( 100 * $count / $$stats{'total'} + 0.5);
+
+    last if ($count < $min_count);
+
+    push @table, [ $port, $count, $percent, $first, $last ];
+  }
+
+  if (@table > 2)
+  {
+    $self->add_table( @table );
+
+    return 1;
+  }
+
+  return 0;
+}
+
+
+#------------------------------------------------------------------------------
+# sub countries( this, min_count )
+#
+# Output information on blocked countries.
+#
+# Parameters:
+#   this       message object
+#   min_count  only output blocked countries occurring at least this number of
+#              times
+#------------------------------------------------------------------------------
+
+sub countries( $$ )
+{
+  my ($self, $min_count) = @_;
+  my @table;
+
+  push @table, ['<', '|', '|', '|', '|'];
+  push @table, [ $Lang::tr{'country'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ];
+
+  my $stats = get_log( $self );
+
+  foreach my $country (sort { $$stats{'by_country'}{$b}{'count'} <=> $$stats{'by_country'}{$a}{'count'} } keys %{ $$stats{'by_country'} } )
+  {
+    my $count   = $$stats{'by_country'}{$country}{'count'};
+    my $first   = $$stats{'by_country'}{$country}{'first'};
+    my $last    = $$stats{'by_country'}{$country}{'last'};
+    my $percent = int( 100 * $count / $$stats{'total'} + 0.5);
+
+    last if ($count < $min_count);
+
+    my $full_country = GeoIP::get_full_country_name( $country) || $country;
+
+    push @table, [ $full_country, $count, $percent, $first, $last ];
+  }
+
+  if (@table > 2)
+  {
+    $self->add_table( @table );
+
+    return 1;
+  }
+
+  return 0;
+}
+
+
+#------------------------------------------------------------------------------
+# sub reasons( this, min_count )
+#
+# Output information on blocked reasons (the IPtable blocking the packet).
+#
+# Parameters:
+#   this       message object
+#   min_count  only output blocked reasons occurring at least this number of
+#              times
+#------------------------------------------------------------------------------
+
+sub reasons( $$ )
+{
+  my ($self, $min_count) = @_;
+  my @table;
+
+  push @table, ['<', '|', '|', '|', '|'];
+  push @table, [ $Lang::tr{'statusmail firewall reason'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ];
+
+  my $stats = get_log( $self );
+
+  foreach my $reason (sort { $$stats{'by_rule'}{$b}{'count'} <=> $$stats{'by_rule'}{$a}{'count'} } keys %{ $$stats{'by_rule'} } )
+  {
+    my $count   = $$stats{'by_rule'}{$reason}{'count'};
+    my $first   = $$stats{'by_rule'}{$reason}{'first'};
+    my $last    = $$stats{'by_rule'}{$reason}{'last'};
+    my $percent = int( 100 * $count / $$stats{'total'} + 0.5);
+
+    last if ($count < $min_count);
+
+    push @table, [ $reason, $count, $percent, $first, $last ];
+  }
+
+  if (@table > 2)
+  {
+    $self->add_table( @table );
+
+    return 1;
+  }
+
+  return 0;
+}
+
+1;
