package Kolab::Conf;

##
##  Copyright (c) 2003  Code Fusion cc
##
##    Writen by Stuart Bingë  <s.binge@codefusion.co.za>
##    Portions based on work by the following people:
##
##      (c) 2003  Tassilo Erlewein  <tassilo.erlewein@erfrakon.de>
##      (c) 2003  Martin Konold     <martin.konold@erfrakon.de>
##      (c) 2003  Achim Frank       <achim.frank@erfrakon.de>
##
##
##  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; either version 2, or
##  (at your option) any later version.
##
##  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 can view the  GNU General Public License, online, at the GNU
##  Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
##

use 5.008;
use strict;
use warnings;
use IO::File;
use File::Copy;
use Kolab;
use Kolab::Util;
use Kolab::LDAP;

require Exporter;

our @ISA = qw(Exporter);

our %EXPORT_TAGS = (
    'all' => [ qw(
        &buildPostfixTransportMap
        &buildCyrusConfig
        &buildCyrusGroups
        &rebuildTemplates
        %haschanged
    ) ]
);

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(

);

our $VERSION = sprintf('%d.%02d', q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/);

sub build {
    my $tmpl = shift;
    my $cfg = shift;
    my $prefix = $Kolab::config{'prefix'};

    my $TEMPLATE;
    if (!($TEMPLATE = IO::File->new($tmpl, 'r'))) {
        Kolab::log('T', "Unable to open template file `$tmpl'", KOLAB_ERROR);
        exit(1);
    }
    my @tempfile = ();

    while (<$TEMPLATE>) {
        if (/\@{3}(\S+)\@{3}/) {
            if ($Kolab::config{$1}) {
                s/\@{3}(\S+)\@{3}/$Kolab::config{$1}/g;
            } else {
                Kolab::log('T', "No configuration variable corresponding to `$1' exists", KOLAB_WARN);
                s/\@{3}(\S+)\@{3}//g;
            }
        }
        push (@tempfile, $_);
    }
    $TEMPLATE->close;

    my $configchanged = 0;
    my $CONFIGFILE;
    if (!($CONFIGFILE = IO::File->new($cfg, 'r'))) {
        Kolab::log('T', "Unable to open config file `$cfg'", KOLAB_ERROR);
        $configchanged = 1;	
    } else {
     
       my @configfile = <$CONFIGFILE>;
       $CONFIGFILE->close;
       undef $CONFIGFILE;

       if ($#configfile == $#tempfile) {
          foreach my $line (@tempfile) {
            my $configline = shift(@configfile);
            $configchanged = 1 if ($configline ne $line)
          }
       } else {
          $configchanged = 1;
       }
    }

    return 0 if ($configchanged ne 1);

    if ($configchanged) {
            Kolab::log('T', "`$cfg' change detected.", KOLAB_DEBUG);

	    if ($cfg =~ /postfix/) {
                $Kolab::haschanged{'postfix'} = 1;
            } elsif ($cfg =~ /saslauthd/) {
                $Kolab::haschanged{'saslauthd'} = 1;
            } elsif ($cfg =~ /apache/) {
                $Kolab::haschanged{'apache'} = 1;
            } elsif ($cfg =~ /proftpd/) {
                $Kolab::haschanged{'proftpd'} = 1;
            } elsif ($cfg =~ /openldap/) {
                $Kolab::haschanged{'slapd'} = 1;
            } elsif ($cfg =~ /imapd/) {
                $Kolab::haschanged{'imapd'} = 1;
            }
    }
    Kolab::log('T', "Creating new configuration file `$cfg' from template `$tmpl'");

    my $tmpfile = $prefix . '/etc/kolab/.tmp';
    unlink($tmpfile);
    my $NEWFILE;
    if ($cfg =~ /openldap/) {
      if (!($NEWFILE = IO::File->new($tmpfile, O_RDWR|O_CREAT|O_EXCL, 0600))) {
        Kolab::log('T', "Unable to open configuration file `$tmpfile'", KOLAB_ERROR);
        exit(1);
      }
    } else {
      if (!($NEWFILE = IO::File->new($tmpfile, O_RDWR|O_CREAT|O_EXCL, 0644))) {
        Kolab::log('T', "Unable to open configuration file `$tmpfile'", KOLAB_ERROR);
        exit(1);
      }
    }

    foreach my $line (@tempfile) {
       print $NEWFILE $line;
    }
    $NEWFILE->close;
    unlink($cfg);
    link($tmpfile,$cfg);
    unlink($tmpfile);
    chown($Kolab::config{'kolab_uid'}, $Kolab::config{'kolab_gid'}, $cfg);


    Kolab::log('T', "Finished creating configuration file `$cfg'");
}

sub buildPostfixTransportMap
{
    Kolab::log('T', 'Building Postfix transport map');

    my $prefix = $Kolab::config{'prefix'};
    my $cfg = "$prefix/etc/postfix/transport";
    my $oldcfg = $cfg . '.old';
    copy($cfg, $oldcfg);
    chown($Kolab::config{'kolab_uid'}, $Kolab::config{'kolab_gid'}, $oldcfg);
    copy("$prefix/etc/kolab/transport.template", $cfg);

    my $transport;
    if (!($transport = IO::File->new($cfg, 'a'))) {
        Kolab::log('T', 'Unable to create Postfix transport map', KOLAB_ERROR);
        exit(1);
    }

    my $ldap = Kolab::LDAP::create(
        $Kolab::config{'ldap_ip'},
        $Kolab::config{'ldap_port'},
        $Kolab::config{'bind_dn'},
        $Kolab::config{'bind_pw'}
    );

    my $mesg = $ldap->search(
        base    => $Kolab::config{'base_dn'},
        scope   => 'sub',
        filter  => '(objectclass=*)'
    );
    if ($mesg->code) {
        Kolab::log('T', 'Unable to locate Postfix transport map entries in LDAP', KOLAB_ERROR);
        exit(1);
    }

    my $ldapobject;
    if ($mesg->code <= 0) {
        foreach $ldapobject ($mesg->entries) {
            my $routes = $ldapobject->get_value('postfix-transport', asref => 1);
            foreach (@$routes) {
                $_ = trim($_);
                Kolab::log('T', "Adding SMTP route `$_'");
                print $transport $_ . "\n";
            }
        }
    } else {
        Kolab::log('T', 'No Postfix transport map entries found');
    }

    Kolab::LDAP::destroy($ldap);
    $transport->close;

    # FIXME: bad way of doing things...
    system("chown root.root $prefix/etc/postfix/*");
    system("$prefix/sbin/postmap $prefix/etc/postfix/transport");

    if (-f $oldcfg) {
        my $rc = `diff -q $cfg $oldcfg`;
        chomp($rc);
        if ($rc) {
           Kolab::log('T', "`$cfg' change detected: $rc", KOLAB_DEBUG);
           $Kolab::haschanged{'postfix'} = 1;
        }
    } else {
        $Kolab::haschanged{'postfix'} = 1;
    }

    Kolab::log('T', 'Finished building Postfix transport map');
}

sub buildCyrusConfig
{
    Kolab::log('T', 'Building Cyrus config');

    my $prefix = $Kolab::config{'prefix'};
    my $cyrustemplate;
    if (!($cyrustemplate = IO::File->new("$prefix/etc/kolab/cyrus.conf.template", 'r')))  {
        Kolab::log('T', "Unable to open template file `cyrus.conf.template'", KOLAB_ERROR);
        exit(1);
    }

    my $cfg = "$prefix/etc/imapd/cyrus.conf";
    my $oldcfg = $cfg . '.old';
    copy($cfg, $oldcfg);
    chown($Kolab::config{'kolab_uid'}, $Kolab::config{'kolab_gid'}, $oldcfg);

    my $cyrusconf;
    if (!($cyrusconf = IO::File->new($cfg, 'w'))) {
        Kolab::log('T', "Unable to open configuration file `$cfg'", KOLAB_ERROR);
        exit(1);
    }

    while (<$cyrustemplate>) {
        if (/\@{3}cyrus-imap\@{3}/ && ($Kolab::config{"cyrus-imap"} =~ /true/i)) {
            $_ = "imap cmd=\"imapd -C $prefix/etc/imapd/imapd.conf\" listen=\"143\" prefork=0\n";
        }
        elsif (/\@{3}cyrus-pop3\@{3}/ && ($Kolab::config{"cyrus-pop3"} =~ /true/i)) {
            $_ = "pop3 cmd=\"pop3d -C $prefix/etc/imapd/imapd.conf\" listen=\"110\" prefork=0\n";
        }
        elsif (/\@{3}cyrus-imaps\@{3}/ && ($Kolab::config{"cyrus-imaps"} =~ /true/i)) {
            $_ = "imaps cmd=\"imapd -s -C $prefix/etc/imapd/imapd.conf\" listen=\"993\" prefork=0\n";
        }
        elsif (/\@{3}cyrus-pop3s\@{3}/ && ($Kolab::config{"cyrus-pop3s"} =~ /true/i)) {
            $_ = "pop3s cmd=\"pop3d -s -C $prefix/etc/imapd/imapd.conf\" listen=\"995\" prefork=0\n";
        }
        elsif (/\@{3}cyrus-sieve\@{3}/ && ($Kolab::config{"cyrus-sieve"} =~ /true/i)) {
            $_ = "sieve cmd=\"timsieved -C $prefix/etc/imapd/imapd.conf\" listen=\"2000\" prefork=0\n";
        }
        $_ =~ s/\@{3}.*\@{3}//;
        print $cyrusconf $_;
    }

    $cyrustemplate->close;
    $cyrusconf->close;

    chown($Kolab::config{'kolab_uid'}, $Kolab::config{'kolab_gid'}, $cfg);

    if (-f $oldcfg) {
        my $rc = `diff -q $cfg $oldcfg`;
        chomp($rc);
        if ($rc) {
           Kolab::log('T', "`$cfg' change detected: $rc", KOLAB_DEBUG);
           $Kolab::haschanged{'imapd'} = 1;
        }
    } else {
        $Kolab::haschanged{'imapd'} = 1;
    }

    Kolab::log('T', 'Finished building Cyrus config');
}

sub buildCyrusGroups
{
    Kolab::log('T', 'Building Cyrus groups');

    my $prefix = $Kolab::config{'prefix'};
    my $cfg = "$prefix/etc/imapd/imapd.group";
    my $oldcfg = $cfg . '.old';
    copy($cfg, $oldcfg);
    chown($Kolab::config{'kolab_uid'}, $Kolab::config{'kolab_gid'}, $oldcfg);
    copy("$prefix/etc/kolab/imapd.group.template", $cfg);
    my $groupconf;
    if (!($groupconf = IO::File->new($cfg, 'a'))) {
        Kolab::log('T', "Unable to open configuration file `$cfg'", KOLAB_ERROR);
        exit(1);
    }

    my $ldap = Kolab::LDAP::create(
        $Kolab::config{'ldap_ip'},
        $Kolab::config{'ldap_port'},
        $Kolab::config{'bind_dn'},
        $Kolab::config{'bind_pw'}
    );

    my $mesg = $ldap->search(
        base    => $Kolab::config{'base_dn'},
        scope   => 'sub',
        filter  => '(objectclass=groupofnames)'
    );
    if ($mesg->code) {
        Kolab::log('T', 'Unable to locate Cyrus groups in LDAP', KOLAB_ERROR);
        exit(1);
    }

    my $ldapobject;
    my $count = 60000;
    if ($mesg->code <= 0) {
        foreach $ldapobject ($mesg->entries) {
            my $group = $ldapobject->get_value('cn') . ":*:$count:";
            my $userlist = $ldapobject->get_value('uid', asref => 1);
            foreach (@$userlist) { $group .= "$_,"; }
            $group =~ s/,$//;
            print $groupconf $group . "\n";
            Kolab::log('T', "Adding cyrus group `$group'");
            $count++;
        }
    } else {
        Kolab::log('T', 'No Cyrus groups found');
    }

    $groupconf->close;
    Kolab::LDAP::destroy($ldap);

    chown($Kolab::config{'kolab_uid'}, $Kolab::config{'kolab_gid'}, $cfg);

    if (-f $oldcfg) {
        my $rc = `diff -q $cfg $oldcfg`;
        chomp($rc);
        if ($rc) {
           Kolab::log('T', "`$cfg' change detected: $rc", KOLAB_DEBUG);
           $Kolab::haschanged{'imapd'} = 1;
        }
    } else {
        $Kolab::haschanged{'imapd'} = 1;
    }

    Kolab::log('T', 'Finished building Cyrus groups');
}

sub rebuildTemplates
{
    my $key;
    my $value;
    my $section="";

    my $prefix = $Kolab::config{'prefix'};

    my %templates = (
        "$prefix/etc/kolab/session_vars.php.template" => "$prefix/var/kolab/www/admin/include/session_vars.php",
        "$prefix/etc/kolab/main.cf.template" => "$prefix/etc/postfix/main.cf",
        "$prefix/etc/kolab/master.cf.template" => "$prefix/etc/postfix/master.cf",
        "$prefix/etc/kolab/saslauthd.conf.template" => "$prefix/etc/sasl/saslauthd.conf",
        "$prefix/etc/kolab/imapd.conf.template" => "$prefix/etc/imapd/imapd.conf",
        "$prefix/etc/kolab/httpd.conf.template" => "$prefix/etc/apache/apache.conf",
        "$prefix/etc/kolab/legacy.conf.template" => "$prefix/etc/apache/legacy.conf",
        "$prefix/etc/kolab/php.ini.template" => "$prefix/etc/apache/php.ini",
        "$prefix/etc/kolab/proftpd.conf.template" => "$prefix/etc/proftpd/proftpd.conf",
        "$prefix/etc/kolab/slapd.conf.template" => "$prefix/etc/openldap/slapd.conf"
    );

    Kolab::log('T', 'Regenerating configuration files');

    foreach $key (keys %templates) {
        build($key, $templates{$key});
    }

    buildPostfixTransportMap;
    buildCyrusConfig;
    buildCyrusGroups;

    Kolab::log('T', 'Finished regenerating configuration files');
}

1;
__END__
# Below is stub documentation for your module. You'd better edit it!

=head1 NAME

Kolab::Conf - Perl extension for Kolab template generation

=head1 ABSTRACT

  Kolab::Conf handles the generation of template files, used by
  kolabconf.

=head1 AUTHOR

Stuart Bingë, E<lt>s.binge@codefusion.co.zaE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2003  Code Fusion cc

Portions based on work by the following people:

  (c) 2003  Tassilo Erlewein  <tassilo.erlewein@erfrakon.de>
  (c) 2003  Martin Konold     <martin.konold@erfrakon.de>
  (c) 2003  Achim Frank       <achim.frank@erfrakon.de>

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; either version 2, or
(at your option) any later version.

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 can view the  GNU General Public License, online, at the GNU
Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.

=cut