#!/usr/bin/perl
#
# ***** BEGIN LICENSE BLOCK *****
# Zimbra Collaboration Suite Server
# Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
#
# This program 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,
# version 2 of the License.
#
# This program 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 this program.
# If not, see <https://www.gnu.org/licenses/>.
# ***** END LICENSE BLOCK *****
use strict;
use lib '/opt/zimbra/common/lib/perl5';
use Zimbra::Util::Common;

use File::Path;
use File::Copy qw/ cp mv /;
use File::Temp qw/ tempfile /;
#use File::Touch;
use Socket;
use Sys::Hostname;

my $id=getpwuid($<);

if ( $id ne "root" ) {
  print "Must be run as root!\n\n";
  exit 1;
}

my $platform=qx(/opt/zimbra/libexec/get_plat_tag.sh);
chomp($platform);

my $zimbra_tmp_directory=getLocalConfig("zimbra_tmp_directory");

my ($uid,$gid) = (getpwnam('zimbra'))[2,3];
if ( !-d $zimbra_tmp_directory ) {
  File::Path::mkpath("$zimbra_tmp_directory");
  chown $uid, $gid, $zimbra_tmp_directory;
}

my $logfile = "-/var/log/zimbra.log";
my $statsfile = "-/var/log/zimbra-stats.log";
my $LOCALHOST ||= (gethostbyname(Sys::Hostname::hostname))[0];

if ( $platform =~ /MACOSX/ ) {
  $logfile="/var/log/zimbra.log";
  $statsfile="/var/log/zimbra-stats.log";
}

my $junk;
my $TYPE;
my $LOGHOST=qx(su - zimbra -c '/opt/zimbra/bin/zmprov -m -l gacf zimbraLogHostname');

if ( $LOGHOST eq "" ) {
  $TYPE="local";
} else {
  ($junk,$LOGHOST) = split /: /, $LOGHOST, 2;
  chomp($LOGHOST);
  if ( lc($LOGHOST) eq lc($LOCALHOST) ) {
    $TYPE="local";
  } else {
    $TYPE="remote";
  }
}

my $rsyslog=0;

sub usage {
  print "\n";
  print "$0: set up syslog.conf for local or remote logging\n\n";
  print "Usage:\n";
  print "  $0\n\n";
  exit 1;
}

sub updateSyslogNG {

  my $syslogconf; 
  if ( -f "/etc/syslog-ng/syslog-ng.conf.in" ) {
    $syslogconf="/etc/syslog-ng/syslog-ng.conf.in";
  } elsif ( -f "/etc/syslog-ng/syslog-ng.conf" ) {
    $syslogconf="/etc/syslog-ng/syslog-ng.conf";
  } elsif ( -f "/etc/syslog-ng.conf" ) {
    $syslogconf="/etc/syslog-ng.conf";
  } else  {
    print "Unable to locate syslog-ng.conf\n";
    exit 1;
  }

  # Make a backup copy
  my $rc=cp($syslogconf,"$syslogconf.bak");
  if (!$rc) {
    print "Unable to make a backup of ".$syslogconf."\n";
    exit 1;
  }

  # create a safe temp file and make sure we have enough space to fill it
  my (undef, $tmpfile) = tempfile("syslog-ng.conf.XXXX", DIR=>"$zimbra_tmp_directory",  OPEN=>0);
  cp($syslogconf, $tmpfile);
  if ( -s $tmpfile ) {
    print "updateSyslogNG: Updating $syslogconf...";
  } else {
    print "updateSyslogNG: Unable to create temp file: $tmpfile\n";
    exit 1;
  }

  open (TMPFH, ">$tmpfile");
  open (SYSFH, "<$syslogconf");

  my $flocal=0;
  while (<SYSFH>) {
    if ($_ !~ /zimbra/ || $_ =~ /not filter\(zimbra/) {
      if( $platform =~ "SLES" || $platform eq "SuSEES10" ) {
        if ($flocal) {
          $flocal=0;
          if ($_ =~ /, local7\); };/) {
            $_ =~ s/, local7\); };/, local7) and not/;
            print TMPFH $_;
            print TMPFH "                               filter(f_iptables) and not filter(zimbra_local0) and not filter(zimbra_local1)\n";
            print TMPFH "                               and not filter(zimbra_auth) and not filter(zimbra_mail); };\n";
            next;
          }
        }
        if ($_ =~ /^filter f_local/) {
          $flocal=1;
        }
        if ($_ =~ /^filter f_messages/) {
          if ($_ =~ /not filter\(f_iptables\); };/) {
            $_ =~ s/filter\(f_iptables\); };/filter\(f_iptables) and/;
            print TMPFH $_;
            print TMPFH "                               not filter(zimbra_local0) and not filter(zimbra_local1) and not\n";
            print TMPFH "                               filter(zimbra_auth) and not filter(zimbra_mail); };\n";
            next;
          }
        }
      }
      print TMPFH $_;
    }
  }
  close(SYSFH);
  my $zsrc = "zimbra_src";
  if ( $platform eq "SLES11_64" || $platform eq "SLES11" || $platform eq "SuSEES10" || $platform eq "SLES10_64" ) {
    #use native src on SLES11_64
    $zsrc="src";
  } elsif ( $platform eq "RHEL5" || $platform eq "RHEL5_64" || $platform eq "CentOS5" || $platform eq "CentOS5_64") {
    $zsrc="s_sys";
  } else {
    print TMPFH "source zimbra_src {  unix-stream(\"/dev/log\" keep-alive(yes) max-connections(128)); }; # zimbra\n";
  }
  print TMPFH "filter zimbra_local0 { facility(local0); }; # zimbra\n";
  print TMPFH "filter zimbra_local1 { facility(local1); }; # zimbra\n";
  print TMPFH "filter zimbra_auth { facility(auth); }; # zimbra\n";
  print TMPFH "filter zimbra_mail { facility(mail); }; # zimbra\n";
  if ( $TYPE eq "local" ) {
    print TMPFH "destination zimbra_mail { file(\"/var/log/zimbra.log\" owner(\"zimbra\")); }; # zimbra\n";
    print TMPFH "destination zimbra_local1 { file(\"/var/log/zimbra-stats.log\" owner(\"zimbra\")); }; # zimbra\n";
    print TMPFH "destination zimbra_local0 { file(\"/var/log/zimbra.log\" owner(\"zimbra\")); }; # zimbra\n";
    print TMPFH "destination zimbra_auth { file(\"/var/log/zimbra.log\" owner(\"zimbra\")); }; # zimbra\n";
  } else {
    my $remoteIp=inet_ntoa(scalar(gethostbyname($LOGHOST)));
    print TMPFH "destination zimbra_mail { udp(\"$remoteIp\" port(514) ); file(\"/var/log/zimbra.log\" owner(\"zimbra\")); }; # zimbra\n";
    print TMPFH "destination zimbra_local1 { udp(\"$remoteIp\" port(514) ); file(\"/var/log/zimbra-stats.log\" owner(\"zimbra\")); }; # zimbra\n";
    print TMPFH "destination zimbra_local0 { udp(\"$remoteIp\" port(514) ); file(\"/var/log/zimbra.log\" owner(\"zimbra\")); }; # zimbra\n";
    print TMPFH "destination zimbra_auth { udp(\"$remoteIp\" port(514) ); file(\"/var/log/zimbra.log\" owner(\"zimbra\")); }; # zimbra\n";
  }
  print TMPFH "log { source($zsrc); filter(zimbra_mail); destination(zimbra_mail); }; # zimbra\n";
  print TMPFH "log { source($zsrc); filter(zimbra_local0); destination(zimbra_local0); }; # zimbra\n";
  print TMPFH "log { source($zsrc); filter(zimbra_local1); destination(zimbra_local1); }; # zimbra\n";
  print TMPFH "log { source($zsrc); filter(zimbra_auth); destination(zimbra_auth); }; # zimbra\n";

  close(TMPFH);

  # remove duplicate logging
  do {
    local $^I="~";
    local @ARGV="$tmpfile";
  
    while (<>) {
      s/(^destination mailinfo.*)/#$1/;
      s/(^log.*f_mailinfo)/#$1/;
      s/(^destination mailwarn.*)/#$1/;
      s/(^log.*f_mailwarn)/#$1/;
      s/(^destination mailerr.*)/#$1/;
      s/(^log.*f_mailerr)/#$1/;
      s|(^destination mail \{ file\("/var/log/mail"\); \};)|#$1|;
      s/(^log.*filter\(f_mail\); destination\(mail\); \};)/#$1/;
      print;
    }
  };
  mv($tmpfile, $syslogconf);
  if ( -f "/etc/logrotate.d/zimbra" ) {
    local $^I="~";
    local @ARGV="/etc/logrotate.d/zimbra";
    while (<>) {
      s/syslogd/syslog-ng/;
      s/USER/zimbra/;
      s/GROUP/zimbra/;
      if ($platform eq "SLES11_64") {
        s/#su zimbra zimbra/su zimbra zimbra/;
      }

      print;
    }
  }
  if ( !-f "/var/log/zimbra.log") {
    open(my $tlog, ">/var/log/zimbra.log");
  }
  if ( !-f "/var/log/zimbra-stats.log") {
    open(my $tlog, ">/var/log/zimbra-stats.log");
  }
  chown $uid,$gid,"/var/log/zimbra.log";
  chown $uid,$gid,"/var/log/zimbra-stats.log";
  chmod 0644, "/var/log/zimbra.log", "/var/log/zimbra-stats.log";
  if ( -f "/etc/logrotate.d/zimbra~" ){
    unlink("/etc/logrotate.d/zimbra~" );
  }
  print "done.\n"
}

sub updateSyslog {
  my $syslogconf="/etc/syslog.conf";
  if ( -f "/etc/rsyslog.conf" ) {
    $syslogconf="/etc/rsyslog.conf";
    $rsyslog=1 
  }

  # Make a backup copy
  my $rc=cp($syslogconf,"$syslogconf.bak");
  if (!$rc) {
    print "Unable to make a backup of ".$syslogconf."\n";
    exit 1;
  }

  # create a safe temp file and make sure we have enough space to fill it
  my (undef, $tmpfile) = tempfile("syslog.conf.XXXX", DIR=>"$zimbra_tmp_directory",  OPEN=>0);
  cp($syslogconf,$tmpfile);
  if ( -s $tmpfile ) {
    print "updateSyslog: Updating $syslogconf...";
  } else  {
    print "updateSyslog: Unable to create temp file: $tmpfile\n";
    exit 1;
  }

  open (TMPFH, ">$tmpfile");
  open (SYSFH, "<$syslogconf");
  # Remove existing entries that we may have added.
  while (<SYSFH>) {
    if ($_ !~ /^local0\.\*/ && $_ !~ /^local1\.\*/) {
      if ( $rsyslog == 1 && $_ =~ /^\tlocal0,local1.none;\\/ ) {
        next;
      }
      if ( $_ =~ /^auth\.\* / ) {
        next;
      }
      if ( $_ =~ /^mail.*($LOGHOST|zimbra)/ ) {
        next;
      }
      s/;local0.none//g;
      s/;local1.none//g;
      s/;auth.none//g;
      if ( $rsyslog == 0 ) {
        s/^\*\.info/\*\.info;local0.none;local1.none;auth.none/;
      }
      if ($rsyslog == 1 ) {
        s/;mail.none//g;
        if ($platform =~ /RHEL6/ || $platform =~ /RHEL7/ || $platform =~ /RHEL8/ || $platform =~ /RHEL9/) {
          s/^\*\.info/\*\.info;local0.none;local1.none;mail.none;auth.none/;
        }
        s/^\*\.\*;auth,authpriv.none/\*\.\*;auth,authpriv.none;local0.none;local1.none;mail.none/;
        s/^\*\.=debug;\\/\*.=debug;\\\n\tlocal0,local1.none;\\/;
        s/^\*\.=info;\*\.=notice;\*\.=warn;\\/\*.=info;\*.=notice;\*.=warn;\\\n\tlocal0,local1.none;\\/;
      }
      print TMPFH $_;
    }
  }
  close(SYSFH);

  if ( $TYPE eq "remote" ) {
    print TMPFH "local0.*                @".$LOGHOST."\n";
    print TMPFH "local1.*                @".$LOGHOST."\n";
    print TMPFH "auth.*                  @".$LOGHOST."\n";
  }
  print TMPFH "local0.*                $logfile\n";
  print TMPFH "local1.*                $statsfile\n";
  print TMPFH "auth.*                  $logfile\n";

  if ($TYPE eq "remote" ) {
    print TMPFH "mail.*                @".$LOGHOST."\n";
  }
  print TMPFH "mail.*                $logfile\n";
  close (TMPFH);

  cp($tmpfile,$syslogconf);

  if ( !-f "/var/log/zimbra.log") {
    open(my $tlog, ">/var/log/zimbra.log");
  }
  if ( !-f "/var/log/zimbra-stats.log") {
    open(my $tlog, ">/var/log/zimbra-stats.log");
  }
  chown $uid,$gid,"/var/log/zimbra.log";
  chown $uid,$gid,"/var/log/zimbra-stats.log";
  chmod 0644,"/var/log/zimbra.log","/var/log/zimbra-stats.log";

  if ( -f "/etc/logrotate.d/zimbra" ) {
    local $^I="~";
    local @ARGV=("/etc/logrotate.d/zimbra");
    while (<>) {
      if ( $rsyslog == 1 ) {
        s/syslogd\*.pid/rsyslogd.pid/g;
        if ($platform eq "RHEL7_64"  || $platform eq "RHEL8_64" || $platform eq "RHEL9_64") {
      		s/#su zimbra zimbra/su zimbra zimbra/;
      	}
	if ($platform eq "RHEL8_64" || $platform eq "RHEL9_64") {
		s/syslog\*.pid/rsyslogd.pid/g;
	}
      }
      s/USER/zimbra/;
      s/GROUP/zimbra/;
      print;
    }
  }
  if ( -f "/etc/logrotate.d/zimbra~" ){
    unlink("/etc/logrotate.d/zimbra~" );
  }
  print "done.\n";
}

sub updateRsyslogd {
  my $conf;
  if (-f "/etc/rsyslog.d/50-default.conf") {
    $conf="/etc/rsyslog.d/50-default.conf";
  } else {
     print "Error: No default configuration found, exiting...\n";
     exit(1);
  }

  # create a safe temp file and make sure we have enough space to fill it
  my (undef, $tmpfile) = tempfile("syslog.conf.XXXX", DIR=>"$zimbra_tmp_directory",  OPEN=>0);
  cp($conf,$tmpfile);
  if ( -s $tmpfile ) {
    print "updateRsyslogd: Updating $conf...";
  } else  {
    print "updateRsyslogd: Unable to create temp file: $tmpfile\n";
    exit 1;
  }

  open (TMPFH, ">$tmpfile");
  open (SYSFH, "<$conf");
  # Remove existing entries that we may have added.
  while (<SYSFH>) {
    if ($_ !~ /^local0\.\*/ && $_ !~ /^local1\.\*/) {
      if ( $_ =~ /^\tlocal0,local1.none;\\/ ) {
        next;
      }
      if ( $_ =~ /^auth\.\* / ) {
        next;
      }
      s/;local0.none//g;
      s/;local1.none//g;
      s/;auth.none//g;
      s/;mail.none//g;
      s/^\*\.\*;auth,authpriv.none/\*\.\*;auth,authpriv.none;local0.none;local1.none;mail.none/;
      s/^\*\.=debug;\\/\*.=debug;\\\n\tlocal0,local1.none;\\/;
      s/^\*\.=info;\*\.=notice;\*\.=warn;\\/\*.=info;\*.=notice;\*.=warn;\\\n\tlocal0,local1.none;\\/;
      print TMPFH $_;
    }
  }
  close(SYSFH);
  close (TMPFH);
  cp($tmpfile,$conf);

  if (-f "/etc/rsyslog.d/60-zimbra.conf") {
    unlink("/etc/rsyslog.d/60-zimbra.conf");
  }
  open(ZFH, ">/etc/rsyslog.d/60-zimbra.conf");
  if ( $TYPE eq "remote" ) {
    print ZFH "local0.*                @".$LOGHOST."\n";
    print ZFH "local1.*                @".$LOGHOST."\n";
    print ZFH "auth.*                  @".$LOGHOST."\n";
  }
  print ZFH "local0.*                $logfile\n";
  print ZFH "local1.*                $statsfile\n";
  print ZFH "auth.*                  $logfile\n";

  if ($TYPE eq "remote" ) {
    print ZFH "mail.*                @".$LOGHOST."\n";
  }
  print ZFH "mail.*                $logfile\n";
  close(ZFH);


  if ( !-f "/var/log/zimbra.log") {
    open(my $tlog, ">/var/log/zimbra.log");
  }
  if ( !-f "/var/log/zimbra-stats.log") {
    open(my $tlog, ">/var/log/zimbra-stats.log");
  }
  if ($platform eq "UBUNTU10_64" || $platform eq "UBUNTU12_64" || $platform eq "UBUNTU14_64" || $platform eq "UBUNTU16_64" || $platform eq "UBUNTU18_64" || $platform eq "UBUNTU20_64" || $platform eq "UBUNTU22_64" ) {
    my $junk;
    ($junk, $junk, $uid, $junk) = getpwnam("syslog");
    $gid = getgrnam("adm");
    chown $uid,$gid,"/var/log/zimbra.log";
    chown $uid,$gid,"/var/log/zimbra-stats.log";
  } else {
    chown $uid,$gid,"/var/log/zimbra.log";
    chown $uid,$gid,"/var/log/zimbra-stats.log";
  }
  chmod 0644,"/var/log/zimbra.log","/var/log/zimbra-stats.log";

  if ( -f "/etc/logrotate.d/zimbra" ) {
    local $^I="~";
    local @ARGV=("/etc/logrotate.d/zimbra");
    while (<>) {
      s/kill\s+\-HUP\s+.+syslog\*\.pid.+\/dev\/null/\/usr\/sbin\/service rsyslog restart >\/dev\/null/g;
      if ($platform eq "UBUNTU10_64" || $platform eq "UBUNTU12_64" || $platform eq "UBUNTU14_64" || $platform eq "UBUNTU16_64" || $platform eq "UBUNTU18_64" || $platform eq "UBUNTU20_64" || $platform eq "UBUNTU22_64" ) {
        s/USER/syslog/;
        s/GROUP/adm/;
      } else {
        s/USER/zimbra/;
        s/GROUP/zimbra/;
      }
      if ($platform eq "UBUNTU14_64" || $platform eq "UBUNTU16_64" || $platform eq "UBUNTU18_64" || $platform eq "UBUNTU20_64" || $platform eq "UBUNTU22_64" ) {
	      s/#su root adm/su root adm/;
	      s/#su zimbra zimbra/su zimbra zimbra/;
      }
      print;
    }
  }
  if ( -f "/etc/logrotate.d/zimbra~" ){
    unlink("/etc/logrotate.d/zimbra~" );
  }
  print "done.\n";
}

my $edited=0;

if ( -f "/etc/syslog-ng/syslog-ng.conf" ||
     -f "/etc/syslog-ng/syslog-ng.conf.in" ||
     -f "/etc/syslog-ng.conf" ) {
  &updateSyslogNG;
  $edited=1;
}

if ( (-f "/etc/rsyslog.conf" && -d "/etc/rsyslog.d") && $edited == 0 && ($platform eq "UBUNTU10_64" || $platform eq "UBUNTU12_64" || $platform eq "UBUNTU14_64" || $platform eq "UBUNTU16_64" || $platform eq "UBUNTU18_64" || $platform eq "UBUNTU20_64" || $platform eq "UBUNTU22_64" )) {
  &updateRsyslogd;
  $edited=1;
}

if ( (-f "/etc/syslog.conf" || -f "/etc/rsyslog.conf") && $edited == 0 ) {
  &updateSyslog;
  $edited=1;
}

if ( !$edited ) {
  print "ERROR: No syslog configuration edited\n";
  exit 1;
}

if ( $platform eq "SLES11_64" ) {
  qx(/etc/init.d/syslog restart > /dev/null 2>&1);
  exit 0;
}

if ($platform =~ /UBUNTU/ && -f "/etc/rsyslog.conf" ) {
  if ( -e "/usr/sbin/service" ) {
    qx(/usr/sbin/service rsyslog restart);
    exit 0;
  } else {
    print "Unable to restart syslog.  Please do it manually.\n";
    exit 1;
  }
}
if ( $platform =~ /RHEL6/ ) {
  if ( -e "/etc/init.d/rsyslog" ) {
    qx(/etc/init.d/rsyslog restart> /dev/null 2>&1);
    exit 0;
  } else {
    print "Unable to restart rsyslog.  Please do it manually.\n";
    exit 1;
  }
} elsif ( $platform =~ /RHEL7/ || $platform =~ /RHEL8/ || $platform =~ /RHEL9/ ) {
  if ( -e "/usr/lib/systemd/system/rsyslog.service" ) {
    qx(/usr/bin/systemctl restart rsyslog.service >/dev/null 2>&1);
    exit 0;
  } else {
    print "Unable to restart rsyslog.  Please do it manually.\n";
    exit 1;
  }
} else {
  my $syslog_PID=qx(cat /var/run/*syslog*.pid);
  qx(kill -HUP $syslog_PID);
}

sub getLocalConfig {
  my $key = shift;

  return $main::loaded{lc}{$key}
    if (exists $main::loaded{lc}{$key});

  my $val = qx(/opt/zimbra/bin/zmlocalconfig -x -s -m nokey ${key} 2> /dev/null);
  chomp $val;
  $main::loaded{lc}{$key} = $val;
  return $val;
}

