#!/usr/bin/perl
# 
# ***** BEGIN LICENSE BLOCK *****
# Zimbra Collaboration Suite Server
# Copyright (C) 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 Net::LDAP;
use XML::Simple;
use Getopt::Long;
use File::Copy qw/ cp /;
use File::Path;

if ( ! -d "/opt/zimbra/common/etc/openldap/schema" ) {
  print "ERROR: openldap does not appear to be installed - exiting\n";
  exit(1);
}

my $id = getpwuid($<);
chomp $id;
if ($id ne "zimbra") {
    print STDERR "Error: must be run as zimbra user\n";
    exit (1);
}
my ($help,$sid);
$sid=0;

my $zmlocalconfig="/opt/zimbra/bin/zmlocalconfig";

my $opts_good = GetOptions(
        'h|help' => \$help,
        's|sid=i' => \$sid,
);

if (!$opts_good) {
        print STDERR "\n";
        usage();
}
if ($help) {
        usage(0);
}

if ($sid <=0) {
   usage(0);
}

my $localxml = XMLin("/opt/zimbra/conf/localconfig.xml");
my $ldap_root_password = $localxml->{key}->{ldap_root_password}->{value};
chomp($ldap_root_password);
my $ldap_is_master = $localxml->{key}->{ldap_is_master}->{value};
chomp($ldap_is_master);
my $ldap_replication_password = $localxml->{key}->{ldap_replication_password}->{value};
my $ldap_starttls_supported = $localxml->{key}->{ldap_starttls_supported}->{value};
my $zimbra_require_interprocess_security = $localxml->{key}->{zimbra_require_interprocess_security}->{value};

if(lc($ldap_is_master) eq "true") {
  print "Cannot run on a master.\n";
  exit(1);
} else {
  my $ldap = Net::LDAP->new('ldapi://%2fopt%2fzimbra%2fdata%2fldap%2fstate%2frun%2fldapi/') or die "$@";
  my $mesg = $ldap->bind("cn=config", password=>"$ldap_root_password");
  $mesg->code && die "Bind: ". $mesg->error . "\n"; 

# Check to see if olcServerID is already configured.  If not, add it.

  my $dn="cn=config";
  $mesg = $ldap ->search(
                    base=>"$dn",
                    filter=>"(&(objectClass=olcGlobal)(olcServerID=*))",
                    scope=>"sub",
                    attrs => ['olcServerID'],
                );
  my $size = $mesg->count;
  if ($size == 0) {
    $mesg = $ldap->modify(
      $dn,
      add=>{olcServerID =>"$sid"},
    );
  }

  my $bdn="olcDatabase={2}mdb,cn=config";

#Check if master is already enabled for replication.  If so, steps are simpler.  If not, more to do.
  $mesg = $ldap->search(
                        base=> "cn=accesslog",
                        filter=>"(objectClass=*)",
                        scope => "base",
                        attrs => ['1.1'],
                 );
  $size = $mesg->count;
  if ($size != 0) {
    print "This is not a replica.\n";
    exit(1);
  }
  # Create accesslog db, add syncprov overlay, etc.
  File::Path::mkpath("/opt/zimbra/data/ldap/accesslog/db");
  $mesg=$ldap->add(
    $bdn,
    attr => [
      objectClass=>["olcDatabaseConfig","olcMdbConfig"],
      olcDatabase=>"{2}mdb",
      olcDbDirectory=>"/opt/zimbra/data/ldap/accesslog/db",
      olcSuffix=>"cn=accesslog",
      olcAccess=>'{0}to dn.subtree="cn=accesslog"  by dn.exact="uid=zimbra,cn=admins,cn=zimbra" read  by dn.exact="cn=config" read  by dn.exact="uid=zmreplica,cn=admins,cn=zimbra" read',
      olcLastMod=>"TRUE",
      olcMaxDerefDepth=>"15",
      olcReadOnly=>"FALSE",
      olcRootDN=>"cn=config",
      olcSizeLimit=>"unlimited",
      olcTimeLimit=>"unlimited",
      olcMonitoring=>"TRUE",
      olcDbCheckpoint=>"0 0",
      olcDbEnvFlags=>["writemap","nometasync"],
      olcDbNoSync=>"TRUE",
      olcDbIndex=>["entryCSN eq", "objectClass eq", "reqEnd eq", "reqResult eq", "reqStart eq"],
      olcDbMode=>"0600",
      olcDbSearchStack=>"16",
      olcDbMaxsize=>"85899345920",
    ],
  );
  $mesg=$ldap->add(
    'olcOverlay=syncprov,olcDatabase={2}mdb,cn=config',
    attr => [
      objectClass=>['olcOverlayConfig','olcSyncProvConfig'],
      olcOverlay=>'syncprov',
      olcSpNoPresent=>"TRUE",
      olcSpReloadHint=>"TRUE",
    ],
  );
  $mesg=>$ldap->modify(
    'olcOverlay=syncprov,olcDatabase={3}mdb,cn=config',
    add => [
      olcSpCheckpoint=>'20 10',
      olcSpSessionlog=>'10000000',
    ],
  );
  $mesg=>$ldap->add(
    'olcOverlay={1}accesslog,olcDatabase={3}mdb,cn=config',
    attr => [
      objectClass=>['olcOverlayConfig','olcAccessLogConfig'],
      olcOverlay=>'{1}accesslog',
      olcAccessLogDB=>'cn=accesslog',
      olcAccessLogOps=>'writes',
      olcAccessLogSuccess=>'TRUE',
      olcAccessLogPurge=>'01+00:00  00+04:00',
    ],
  );
  $bdn="olcDatabase={3}mdb,cn=config";
  $mesg = $ldap->modify(
    $bdn,
    replace=>{olcMirrorMode=>"TRUE"},
  );
  $mesg = $ldap->modify(
    $bdn,
    delete=> {olcUpdateRef=>[]},
  );
  setLocalConfig("ldap_is_master", "true");
}

sub usage {

        my ($msg) = (@_);

        $msg && print STDERR "\nERROR: $msg\n";
        print STDERR <<USAGE;
  zmldapenable-mmr -s SID

  Where:
  SID is an unique Integer Server ID for THIS LDAP Master.  It CANNOT be in use by any other LDAP master.

USAGE
        exit (1);
}

sub setLocalConfig {
  my $key = shift;
  my $val = shift;
  print "Setting local config $key=$val\n";
  runCommand("$zmlocalconfig -f -e ${key}=\'${val}\' 2> /dev/null");
}

sub runCommand {
  my $cmd = shift;
  my $rc;
  $rc = 0xffff & system("$cmd > /dev/null 2>&1 ");
  return $rc;
}
