#!/usr/bin/perl -w
# $Id$
# This script (sophomorix-mail) is maintained by Rüdiger Beck
# It is Free Software (License GPLv3)
# If you find errors, contact the author
# jeffbeck@web.de  or  jeffbeck@gmx.de


# ===========================================================================
# Bibliotheken
# ===========================================================================
use strict;
use Getopt::Long;
Getopt::Long::Configure ("bundling");

use Sophomorix::SophomorixConfig;
use Sophomorix::SophomorixBase;
use Sophomorix::SophomorixPgLdap qw(
                                  show_modulename
                                  db_connect
                                  db_disconnect
                                  check_connections
                                  fetchusers_from_project
                                  fetchinfo_from_project
                                  fetchusers_from_adminclass
                                   );
use DBI;
use Net::LDAP;
use IMAP::Admin;

my @arguments = @ARGV;

# ===========================================================================
# Optionen verarbeiten
# ==========================================================================
#my $gruppe="";
my $file="/etc/aliases";
my $file_tmp="/etc/aliases-tmp";
my $new_aliases="/usr/bin/newaliases";
my %seen_aliases=();
my $cross;

# Variablen für Optionen
$Conf::log_level=1;
my $help=0;
my $info=0;
my $show_mailbox=0;
my $show_redirect=0;
my $show_all_redirect=0;
my $skiplock=0;
my $show_type=0;
my $kill_mailbox="";
my $add_mailbox="";

# Parsen der Optionen
my $testopt=GetOptions(
           "verbose|v+" => \$Conf::log_level,
           "help|h" => \$help,
           "showmailboxes|showmailbox" => \$show_mailbox,
           "showredirect|showforward" => \$show_redirect,
           "showallredirect|showallforward" => \$show_all_redirect,
           "skiplock" => \$skiplock,
           "showtype" => \$show_type,
           "kill-mailbox=s" => \$kill_mailbox,
           "add-mailbox=s" => \$add_mailbox,
           "info|i" => \$info
          );


# Prüfen, ob Optionen erkannt wurden
&check_options($testopt);


# --help
if ($help==1) {
   # Scriptname ermitteln
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   # Befehlbeschreibung
   print "\n$scriptname modifies $file and runs newaliases.\n\n";
   print "There will be added:\n";
   print "    - mailaliases (firstname.surname)\n";
   print "    - maillists (common mailadress named like group)\n";
   print "\n";
   print "The following groups are possible:\n";
   print "    - adminclass (update db with sophomorix-class ...)\n";
   print "    - projects   (update db with sophomorix-project ...)\n";
   print "    - subclass   (todo)\n";
   print "\n... then run sophomorix-mail\n";
   print('
Options
  -h  / --help
  -v  / --verbose
  -vv / --verbose --verbose
  -i  / --info
  --skiplock
  
Imap-Server Options

  --showredirect --showforward
  --showmailboxes
  --showmailboxes --showtype   (slow)
  --showforward/--showredirect
  --showallforward/--showallredirect

  --kill-mailbox user1,user2,...
  --add-mailbox user1,user2,...

Please see the sophomorix-mail(8) man pages for full documentation
');
   print "\n";
   exit;
}

&check_connections();


# classes with mailalias vorname.nachname
my @mail_klassen=&get_mail_alias_classes();
# classes with maillist
my @mail_liste_klassen=&get_mail_list_classes();


# projects with mailalias vorname.nachname
my @mail_projects=&get_mail_alias_projects();
# projects with maillist
my @mail_list_projects=&get_mail_list_projects();



# --info
if ($info==1) {
   print "\n";
   print "Groups, which members will have a mailalias:\n";
   print "  @mail_klassen\n\n";

   print "Groups, which have a maillist\n";
   print "  @mail_liste_klassen\n\n";

   exit;
}

&log_script_start(@arguments);

system("mkdir -p ${DevelConf::mailconf_pfad}");


# --showredirect/showforward
if ($show_redirect==1 or $show_all_redirect==1){
    my @teachers = &Sophomorix::SophomorixPgLdap::fetchstudents_from_adminclass(${DevelConf::teacher});
    my @users=();

    if ($show_all_redirect==1){
        my @students=&Sophomorix::SophomorixAPI::fetchstudents_from_school();
        @users = (@teachers,@students);
    } elsif ($show_redirect==1){
        @users = (@teachers);
    }

    print "+----------+--------------------------------+",
          "--------------------------------+-+\n";
    print "| Login    | .forward                       |",
          " /var/spool/sieve/*/ingo.script |n|\n";

    foreach my $user (@users){
        my @column_user=($user);
        my @column_forward=();
        my @column_cyrus=();
        $cross=0;
        my ($home) = &Sophomorix::SophomorixPgLdap::fetchdata_from_account($user);
        if (-e "$home/.forward"){
            $cross++;
            # open file and read .forward
            open(FORWARD, "<$home/.forward");
            while (<FORWARD>){
                chomp();
                push @column_forward, $_;
            }
            close(FORWARD);
        }

        @column_cyrus=&Sophomorix::SophomorixBase::read_cyrus_redirect($user);

        # look for longest column
        my $max_index=$#column_user;
        if ($#column_forward>$max_index){
            $max_index=$#column_forward
        }
        if ($#column_cyrus>$max_index){
            $max_index=$#column_cyrus
        }

        # fill other columns with empty string
        while ($#column_user<$max_index){
            push @column_user, " $user";
        }
        while ($#column_forward<$max_index){
            push @column_forward, "";
        }
        while ($#column_cyrus<$max_index){
            push @column_cyrus, "";
        }

        if ($column_cyrus[0] ne "" or $column_forward[0] ne ""){
           # separator for new users
           print "+----------+--------------------------------+",
                 "--------------------------------+-+\n";
           for (my $i=0;$i<=$max_index;$i++){
               printf "| %-9s| %-31s| %-31s|%-1s|\n",$column_user[$i],
                                                     $column_forward[$i],
                                                     $column_cyrus[$i],
                                                     $cross;
           }
        }

    }
    print "+----------+--------------------------------+",
          "--------------------------------+-+\n";
    print "n: number of existing files (.forward, ingo.script)\n";
    &log_script_end(@arguments);
}



if ($show_mailbox==1) {
    my $imap=&imap_connect("localhost",${DevelConf::imap_admin});
    # fetch list of mailboxes
    if ($show_type==1){
        &imap_show_mailbox_info($imap,"showtype");
    } else {
        &imap_show_mailbox_info($imap);
    }
    &imap_disconnect($imap);
    &log_script_end(@arguments);
}





# --killmailbox
if ($kill_mailbox ne "") {
    my $login="";
    my $mailbox="";
    my $string="";

    my @mailboxes=split(/,/,$kill_mailbox);

    foreach my $k_mailbox (@mailboxes){
        if ($k_mailbox =~m/^user\./){
            # user.login given
            ($string,$login)=split(/\./, $k_mailbox);
	    $mailbox=$k_mailbox;
        } else {
            # login given
	    $login=$k_mailbox;
            $mailbox="user.".$k_mailbox; 
        }
        print "Trying to kill mailbox $mailbox ($login) ...\n";

        # checks
        my ($home,$group)=
            &Sophomorix::SophomorixPgLdap::fetchdata_from_account($login);
        if ($group eq ""){
            # no unix account exists
            print "\nUser $login has no unix account.",
                  " I try to delete its maibox ...\n\n";
            my $imap=&Sophomorix::SophomorixBase::imap_connect("localhost",
                                                 ${DevelConf::imap_admin});
            &Sophomorix::SophomorixBase::imap_kill_mailbox($imap,$login);
            &Sophomorix::SophomorixBase::imap_disconnect($imap);
        } else {
            # its a unix account
            print "\nUser $login has a unix account.",
                  " I will not delete its maibox!\n\n";
        }
    }
    &log_script_end(@arguments);
}



# --add-mailbox
if ($add_mailbox ne "") {
    my $login="";
    my $mailbox="";
    my $string="";

    my @mailboxes=split(/,/,$add_mailbox);

    foreach my $k_mailbox (@mailboxes){
        if ($k_mailbox =~m/^user\./){
            # user.login given
            ($string,$login)=split(/\./, $k_mailbox);
	    $mailbox=$k_mailbox;
        } else {
            # login given
	    $login=$k_mailbox;
            $mailbox="user.".$k_mailbox; 
        }

        # checks
        my ($home,$group)=
            &Sophomorix::SophomorixPgLdap::fetchdata_from_account($login);
        if ($group eq ""){
            # no unix account exists
            print "\nUser $login has no unix account.",
                  " I will not add a mailbox ...\n\n";
        } else {
            # its a unix account
            print "\nUser $login has a unix account.",
                  " I will try to add its mailbox!\n\n";
            my $imap=&Sophomorix::SophomorixBase::imap_connect("localhost",
                                                 ${DevelConf::imap_admin});
            &Sophomorix::SophomorixBase::imap_create_mailbox($imap,$login);
            &Sophomorix::SophomorixBase::imap_disconnect($imap);
        }
    }
    # set quota
    if ($Conf::use_quota eq "yes"){ 
    system("${DevelConf::executable_pfad}/sophomorix-quota --skiplock --users $add_mailbox --noninteractive --set");
    }
    # kein nscd_stop da exit folgt.
    &log_script_end(@arguments);
}





################################################################################
# Programm
################################################################################

&titel("sophomorix-mail is creating Mail-Aliases/Lists");


# ! Careful ! This string CANNOT be changed!
my $magic_line="### Add your own entries BEFORE this line ###\n";
my $magic=0;


# 1) read until magic line
open(ALIASESTMP, ">$file_tmp");
open(ALIASES, "<$file");
while (<ALIASES>) {
    my $line = $_;
    print ALIASESTMP "$line";
    if ($line eq $magic_line){
        $magic=1;
        last;
    }
}
close(ALIASES);



# 2) append magic line if necessary
if ($magic==0){
    # append magic line
    print ALIASESTMP $magic_line;
}


# 3) add own stuff
# additional comment
print ALIASESTMP "### Entries after here are managed by sophomorix-mail ###\n";

# aliases of adminclasses
foreach my $gruppe (@mail_klassen) {
   if (${Conf::mail_aliases} eq "vorname.nachname"
       or ${Conf::mail_aliases} eq "vorname_nachname") {
      print "Generating mailaliases for adminclass $gruppe\n";
      &generate_mail_alias_classes("$gruppe"); 
  } else {
      print "Warning: ${Conf::mail_aliases} is not a valid",
            " alias template for $gruppe.\n";
  }
}


# aliases of projects
foreach my $gruppe (@mail_projects) {
   if (${Conf::mail_aliases} eq "vorname.nachname"
       or ${Conf::mail_aliases} eq "vorname_nachname") {
      print "Generating mailaliases for project $gruppe\n";
      &generate_mail_alias_projects("$gruppe"); 
  } else {
      print "Warning: ${Conf::mail_aliases} is not a valid",
            " alias template for $gruppe.\n";
  }
}



# mailinglist of adminclass
foreach my $gruppe (@mail_liste_klassen) {
      print "Generating mailinglist for adminclass $gruppe\n";
      &generate_maillist_classes("$gruppe"); 
}


# mailinglist of projects
foreach my $gruppe (@mail_list_projects) {
      print "Generating mailinglist for project $gruppe\n";
      &generate_maillist_projects("$gruppe"); 
}

close(ALIASESTMP);



# replace /etc/aliases
system("mv $file_tmp $file");


# 4) run newaliases
if (-e "$new_aliases"){
    print "Running $new_aliases ... ";
    system("$new_aliases");
    print "done!\n";
} else {
    print "WARNING: Script $new_aliases not found\n";
}


&log_script_end(@arguments);




################################################################################
# Subroutines
################################################################################

sub get_mail_alias_classes {
    my @classes=();
    my $dbh=&db_connect();
    # select the columns that i need
    my $sth= $dbh->prepare( "SELECT gid,mailalias 
                            FROM classdata 
                            WHERE (mailalias='t'
                              AND (type='adminclass' OR type='teacher'
                               OR type='hiddenclass')) 
                           " );
    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
       # split the array, to give better names
       my ($gid,$mailalias)=@$row;
       push @classes, $gid;
    }
    &db_disconnect($dbh);
    return @classes;
}


sub get_mail_alias_projects {
    my @projects=();
    my $dbh=&db_connect();
    # select the columns that i need
    my $sth= $dbh->prepare( "SELECT gid,mailalias 
                            FROM projectdata 
                            WHERE mailalias='t'
                            " );
    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
       # split the array, to give better names
       my ($gid,$mailalias)=@$row;
       push @projects, $gid;
    }
    &db_disconnect($dbh);
    return @projects;
}


sub get_mail_list_classes {
    my @classes=();
    my $dbh=&db_connect();
    # select the columns that i need
    my $sth= $dbh->prepare( "SELECT gid,maillist 
                            FROM classdata 
                            WHERE (maillist='t'
                              AND (type='adminclass' OR type='teacher'
                               OR type='hiddenclass')) 
                           " );
    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
       # split the array, to give better names
       my ($gid,$maillist)=@$row;
       push @classes, $gid;
    }
    &db_disconnect($dbh);
    return @classes;
}


sub get_mail_list_projects {
    my @projects=();
    my $dbh=&db_connect();
    # select the columns that i need
    my $sth= $dbh->prepare( "SELECT gid,maillist 
                            FROM projectdata 
                            WHERE maillist='t'
                           " );
    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
       # split the array, to give better names
       my ($gid,$maillist)=@$row;
       push @projects, $gid;
    }
    &db_disconnect($dbh);
    return @projects;
}





sub generate_mail_alias_classes {
   # generate mailaliases for all users in adminclass
   my ($group)=@_;
   my @user = &fetchusers_from_adminclass($group);
   if (defined $user[0]){
       print ALIASESTMP "\n### Mailaliases for adminclass $group \n";
       foreach my $user (@user) {
           # Gecos-Feld ermitteln
           my ($home,$type,$gecos)=
             &Sophomorix::SophomorixPgLdap::fetchdata_from_account($user);
           if (${Conf::mail_aliases} eq "vorname.nachname"){
	       $gecos=&recode_to_ascii($gecos);
               $gecos=~tr/A-Z/a-z/; # make lowercase
               if (not exists $seen_aliases{$gecos}){
                   $seen_aliases{$gecos}="seen";
                   printf ALIASESTMP "%-35s %-20s \n",$gecos.":",$user;
	       } else {
                   printf ALIASESTMP "#%-35s %-20s \n",$gecos.":",$user;
               }
           } elsif (${Conf::mail_aliases} eq "vorname_nachname"){
	       $gecos=&recode_to_ascii_underscore($gecos);
               $gecos=~tr/A-Z/a-z/; # make lowercase
               if (not exists $seen_aliases{$gecos}){
                   $seen_aliases{$gecos}="seen";
                   printf ALIASESTMP "%-35s %-20s \n",$gecos.":",$user;
	       } else {
                   printf ALIASESTMP "#%-35s %-20s \n",$gecos.":",$user;
               }
           }
       }
   }
}




sub generate_mail_alias_projects {
   # generate mailaliases for all users in project
   my ($group)=@_;
   my @user=&fetchusers_from_project($group);
   print ALIASESTMP "\n### Mailaliases for project $group \n";
   foreach my $user (@user) {
      # Gecos-Feld ermitteln
      my ($home,$type,$gecos)=
             &Sophomorix::SophomorixPgLdap::fetchdata_from_account($user);
      if (${Conf::mail_aliases} eq "vorname.nachname"){
	  $gecos=&recode_to_ascii($gecos);
          $gecos=~tr/A-Z/a-z/; # make lowercase
          if (not exists $seen_aliases{$gecos}){
               $seen_aliases{$gecos}="seen";
               printf ALIASESTMP "%-35s %-20s \n",$gecos.":",$user;
	   } else {
               printf ALIASESTMP "#%-35s %-20s \n",$gecos.":",$user;
           }
      } elsif (${Conf::mail_aliases} eq "vorname_nachname"){
	  $gecos=&recode_to_ascii_underscore($gecos);
          $gecos=~tr/A-Z/a-z/; # make lowercase
          if (not exists $seen_aliases{$gecos}){
               $seen_aliases{$gecos}="seen";
               printf ALIASESTMP "%-35s %-20s \n",$gecos.":",$user;
	   } else {
               printf ALIASESTMP "#%-35s %-20s \n",$gecos.":",$user;
           }
      }
   }
}




sub generate_maillist_classes {
   # generate mailinglist alias for group
   my ($group)=@_;
   my $letzter_user="";
   my $ende="";

   my @user_local_mail_domain=&fetchusers_from_adminclass($group);
   my @user_external_from_file=&fetchusers_external($group);
   my @user = (@user_local_mail_domain,@user_external_from_file);

   print ALIASESTMP "\n### Mailinglist for adminclass $group \n";

   if (defined $user[0]){
      # Gruppen-Mailingliste erzeugen:
      if (defined ${Conf::teachers_alias_name} and 
          $group eq ${DevelConf::teacher}){
          print ALIASESTMP "${Conf::teachers_alias_name}:\n";
      } else {
          print ALIASESTMP "$group:\n";
      }
      $ende=$#user;
      $letzter_user=$user[ $ende ];
      if($Conf::log_level>=3){
          print "User $letzter_user hat den index $ende und ist der Letzte.\n";
      }
      foreach my $user (@user) {
         print ALIASESTMP "   $user";
         if ($user eq $letzter_user){
            # add additional aliases for group teachers
            if ($group eq ${DevelConf::teacher} and
                defined ${Conf::teachers_alias_additions}){
                if (${Conf::teachers_alias_additions} ne ""){               
                    print ALIASESTMP ",\n";
                    print ALIASESTMP "   ${Conf::teachers_alias_additions}\n";
	        }
            }
            if ($group eq ${Language::alumni} and
                defined ${Conf::alumni_alias_additions}){
                if (${Conf::alumni_alias_additions} ne ""){               
                    print ALIASESTMP ",\n";
                    print ALIASESTMP "   ${Conf::alumni_alias_additions}\n";
	        }
            }
            print ALIASESTMP "\n";
         } else {
            print ALIASESTMP ",\n"; 
         }
      } 
   }
 }



sub generate_maillist_projects {
   # generate mailinglist alias for group
   my ($group)=@_;
   my $letzter_user="";
   my $ende="";

   my @user_local_mail_domain=&fetchusers_from_project($group);
   my @user_external_from_file=&fetchusers_external($group);
   my @user = (@user_local_mail_domain,@user_external_from_file);

   my ($longname)=&fetchinfo_from_project($group);

   print ALIASESTMP "\n### Mailinglist for project ",
                    "$group (LongName: $longname)\n";

   # get mail also for non "p_"-beginning
   $group=~s/^p_//g;

   if (defined $user[0]){
       # create alias from longname to name
       if ($group ne $longname){
          printf ALIASESTMP "%-35s %-20s \n",$longname.":",$group;
       }
       print ALIASESTMP "$group:\n";

       # Gruppen-Mailingliste erzeugen:
       $ende=$#user;
       $letzter_user=$user[ $ende ];
       if($Conf::log_level>=3){
          print "User $letzter_user hat den index $ende und ist der Letzte.\n";
       }
       foreach my $user (@user) {
          print ALIASESTMP "   $user";
          if ($user eq $letzter_user){
              print ALIASESTMP "\n";
          } else {
              print ALIASESTMP ",\n"; 
          }
       } 
   }
}


sub fetchusers_external {
   my ($group)=@_;
   my @external_users;
   my $file=${DevelConf::mailconf_pfad}."/".${group};
   if($Conf::log_level>=3){
       print "     * Looking for $file\n";
   }
   if (-e $file){
       print "  $file EXISTS:\n";
       open(FILE, "${file}"); 
       while(<FILE>){
           s/ //g; # Leerzeichen ersetzen
           if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
               next;
           }
           chomp();
           if ($_ eq ""){# leere Zeilen nicht nutzen
               next;
           }
           # Aufnehmen in die Liste
           print "    >$_<\n";
           push @external_users, $_;
       }
       close(FILE);
   } else {
       if($Conf::log_level>=3){
           print "        -> $file nonexisting\n";
       }
   }

   @external_users = sort @external_users;
   return @external_users;
}

