#!/usr/bin/perl -w
# $Id$
# This script (sophomorix-janitor) 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::SophomorixAPI;
use Sophomorix::SophomorixPgLdap;
use Time::Local;
use Time::localtime;
use Term::ANSIColor qw(:constants); # farbiger Text RED, BLUE, ...
use Date::Calc qw(check_date);

use DBI;
use Net::LDAP;

use File::Find;
use File::Copy;
use File::Basename;
use File::Path qw(make_path);
use Lchown;

my @arguments = @ARGV;

# Scriptname ermitteln
my @list = split(/\//,$0);
my $scriptname = pop @list;

# nach jedem Printbefehl wieder auf Standardfarbe zurücksetzen
$Term::ANSIColor::AUTORESET = 1;
#use Sophomorix::SophomorixPgLdap qw(get_sys_users
#                                   );

# ===========================================================================
# Optionen verarbeiten
# ==========================================================================
$Conf::log_level=1;
my $help=0;
my $info=0;
my $login="";

# the following are dirs given by options 
# in these dirs files/dirs are searched:
my $find_files="";
my $copy_to_review="";
my $move_to_review="";
my $copy_to_review_home="";
my $move_to_review_home="";
my $copy_to_review_share_teacher="";
my $move_to_review_share_teacher="";
my $copy_to_review_share_project="";
my $move_to_review_share_project="";
my $copy_to_review_share_class="";
my $move_to_review_share_class="";
my $review_days=366;
my $review_end_date="---";

# unowned files
my $find_unowned_files="";
# uidnumber -> counter
my %unowned_files=();
my @unowned_files=();

# showing/deleting review_data
my $show_review_data=0;
my $delete_review_data=0;
my @review_dirs_overtime=(); # review until day overreached
my @review_dirs=();   # still in review time interval
my %review_dirs_days_to_go=();

# separator in logfiles
my $sep="|::|";
# The following are lists where the search result is saved:
my @find_files_list=();
my @find_links_list=();
my @find_dirs_list=();
my @find_strange_list=();
my $count_found_links=0;
my $count_found_files=0;
my $count_found_dirs=0;
my $count_found_strange=0;

# old owner and its uidnumber
my $owner="";
my $uid_number=0;

# new owner
my $new_owner="";
my $new_uid_number = -1;

my $copy_to="";
my $move_to="";

my $aclhomeshare_remove_uidnum=0;
my $aclhomeshare_remove_all_user_acls=0;
my @acl_homeshare_numeric=();
my @acl_homeshare_user=();
my @acl_homeshare_group=();


my $listfiles=0;
my $list_db_junk=0;
my $kill_db_junk=0;
my $check_project_members=0;

my $list_foreign_files=0;
my $check_horde=0;
my $dump_horde=0;
my $dump_pg_ldap=0;


my @userlist=();
my %userlist=();

my $classes="";
my $projects="";
my $student=0;
my $teacher=0;
my $administrator=0;
my $rooms="";
my $ws=0;
my $check=1;


# %future_members will be compared to the table groups_users
# key:    gidnumber
# value:  memberuidnumber
my %future_members=();



# ???????
my $list_files_path="/var/log/sophomorix/list_files";
system("mkdir -p $list_files_path");

# Parsen der Optionen
my $testopt=GetOptions(
           "verbose|v+" => \$Conf::log_level,
           "user|users|u=s" => \$login,
           "class|classes|c=s" => \$classes,
           "project|projects|p=s" => \$projects,
           "student|students|s" => \$student,
           "teacher|teachers|t" => \$teacher,
           "administrator|administrators" => \$administrator,
           "room|rooms|r=s" => \$rooms,
           "workstations|workstation|w" => \$ws,
           "list-db-junk" => \$list_db_junk,
           "kill-db-junk" => \$kill_db_junk,
           "aclhomeshare-remove-uidnum" => \$aclhomeshare_remove_uidnum,
           "aclhomeshare-remove-all-users" => \$aclhomeshare_remove_all_user_acls,
           "find-files|ff|findfiles=s" => \$find_files,
           "copy-to-review|cprv=s" => \$copy_to_review,
           "move-to-review|mvrv=s" => \$move_to_review,
           "move-to-review-home=s" => \$move_to_review_home,
           "copy-to-review-home=s" => \$copy_to_review_home,
           "move-to-review-share-teacher=s" => \$move_to_review_share_teacher,
           "copy-to-review-share-teacher=s" => \$copy_to_review_share_teacher,
           "move-to-review-share-project=s" => \$move_to_review_share_project,
           "copy-to-review-share-project=s" => \$copy_to_review_share_project,
           "move-to-review-share-classes=s" => \$move_to_review_share_class,
           "copy-to-review-share-classes=s" => \$copy_to_review_share_class,
           "review-days=i" => \$review_days,
           "find-unowned-files=s" => \$find_unowned_files,
           "show-review-data" => \$show_review_data,
           "delete-review-data" => \$delete_review_data,
           "owner=s" => \$owner,
           "new-owner|newowner=s" => \$new_owner,
           "copy-to=s" => \$copy_to,
           "move-to=s" => \$move_to,
           "listfiles" => \$listfiles,
           "list-foreign-files" => \$list_foreign_files,
           "check-horde" => \$check_horde,
           "dump-horde" => \$dump_horde,
           "dump-pg-ldap" => \$dump_pg_ldap,
           "check-project-members" => \$check_project_members,
           "info|i" => \$info,
           "help|h" => \$help,
          );

# Prüfen, ob Optionen erkannt wurden
&check_options($testopt);
# / as causes trouble because it ends with / and is useless anyway
if ($copy_to eq "/" or 
    $move_to eq "/"or 
    $copy_to_review eq "/"or 
    $move_to_review eq "/"or 
    $find_files eq "/"
    ){
    print "\nDirectory / not allowed\n\n";
    exit;
}
# remove trailing / from options (if present)
$copy_to=~s/\/$//;
$move_to=~s/\/$//;
$copy_to_review=~s/\/$//;
$move_to_review=~s/\/$//;
$find_files=~s/\/$//;

# --help
if ($help==1) {
   # Befehlbeschreibung
   print "\n$scriptname does some housekeeping:\n";

   print('
Options
  -h  / --help
  -v  / --verbose
  -vv / --verbose --verbose

Housekeeping the postgres database ldap:

  --list-db-junk
  --kill-db-junk

Unowned files handling:

  --find-unowned-files /path (list uidnumbers --> number of  unowned files)

Cleaning unused ACLs on /home/share:

  -- aclhomeshare-remove-uidnum  (removing acl entrys where users are deleted)
  -- aclhomeshare-remove-all-users  (removing all user acls)

Listing files/dirs/links/strange_stuff of user uid/uidnumber:

  --find-files /source --owner login/uidnumber     (summary)
  --find-files /source --owner login/uidnumber -v  (list every file)

Reowning files/dirs/links/strange_stuff of login/uidnumber to newlogin/newuidnumber:

  --find-files /source --owner login/uidnumber --new-owner newlogin/newuidnumber

Reowning files to user root, basic commands:

  --find-files /source --owner login/uidnumber --copy-to /target
  --find-files /source --owner login/uidnumber --move-to /target

Reviewing files (Files are copied/moved to alumni directory):

  --copy-to-review /source --owner login/uidnumber --review-days num
  --move-to-review /source --owner login/uidnumber --review-days num
  Option --review-days is optional and defaults to 366 days

Useful shortcuts (--review-days is optional):

  Reviewing files in the homediectory of a user:
    --move-to-review-home login --review-days num 
    --copy-to-review-home login --review-days num 
  Reviewing files in the teachers share:
    --move-to-review-share-teacher login/uidnumber --review-days num 
    --copy-to-review-share-teacher login/uidnumber --review-days num 
    With all orphaned files:
    --find-unowned-files /home/share/teachers --move-to-review-share-teacher all-orphaned-users 
    --find-unowned-files /home/share/teachers --copy-to-review-share-teacher all-orphaned-users
  Reviewing files in all project shares:
    --move-to-review-share-project login/uidnumber --review-days num 
    --copy-to-review-share-project login/uidnumber --review-days num 
    With all orphaned files:
    --find-unowned-files /home/share/projects --move-to-review-share-project all-orphaned-users 
    --find-unowned-files /home/share/projects --copy-to-review-share-project all-orphaned-users
  Reviewing files in all class shares:
    --move-to-review-share-class login/uidnumber --review-days num 
    --copy-to-review-share-class login/uidnumber --review-days num 
    With all orphaned files:
    --find-unowned-files /home/share/classes --move-to-review-share-class all-orphaned-users 
    --find-unowned-files /home/share/classes --copy-to-review-share-class all-orphaned-users

Show/Delete revied data:

    --show-review-data
    --delete-review-data

Working on users files:

  --listfiles --user name

  --list-foreign-files (List data in home of user, that are not owned by user)

  --check-horde (checks horde3 database (Table horde_prefs) for obsolete data)
  --dump-horde (dumps horde3 database)

  --dump-pg-ldap (dumps the postgresql database ldap)

Selecting users (Default is: no user)
  -s / --students
  --class teachers  (teachers)
  -w / --workstations
  -u user1,user2,...   /  --users user1,user2,...
  -c class1,class2,... /  --class class1,class2,... 
  -p project1,project2,... /  --project project1,project2,... 
  -r room1,room2,...   /  --room room1,room2,...


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



# ===========================================================================
# script starts here
# ===========================================================================


# acl stuff
#  --aclhomeshare-remove-uidnum
if ($aclhomeshare_remove_uidnum==1){
    print "Removing acls with uidnumber from ${DevelConf::share_share}\n";
    &get_homeshare_acls();
    foreach my $uidnumber (@acl_homeshare_numeric){
        my $command="setfacl -x u:$uidnumber ${DevelConf::share_share}";
        print "$command\n";
        system($command);
    }
    exit;
}

#  --aclhomeshare-remove-all-users
if ($aclhomeshare_remove_all_user_acls==1){
    print "Removing all user acl from ${DevelConf::share_share}\n";
    &get_homeshare_acls();
    foreach my $user (@acl_homeshare_user){
        my $command="setfacl -x u:$user ${DevelConf::share_share}";
        print "$command\n";
        system($command);
    }
    exit;
}


# --find-unowned-files
if ($find_unowned_files ne "" ){
    my %uid_uidname=();

    # reading archived userids
    open (ARCHIVELOG,
         "<${DevelConf::log_files}/user-modify-archive.log") 
         || die "Fehler: ${DevelConf::log_files}/user-modify-archive.log    $!";
    while(<ARCHIVELOG>){
      chomp();
      my ($type,$date,$login,$uid,$surname,$firstname)=split(/::/);
      if ($type eq "user archived"){
          $uid_uidname{$uid}=$login." (".$surname.", ".$firstname.")";
      }
    }
    close(ARCHIVELOG);

    my $return=`find $find_unowned_files -nouser -printf "%u %g %p\n"`;
    #print "$return";
    my @lines=split("\n",$return);
    foreach my $line (@lines){
        my ($uid,$gid,$rest)=split(" ", $line);
        if (not exists $unowned_files{$uid}){
            $unowned_files{$uid}=1;
            push @unowned_files, $uid;
        } else {
            my $oldcount=$unowned_files{$uid};
            my $newcount=$oldcount+1;
            $unowned_files{$uid}=$newcount;
        }
    }
    @unowned_files = sort @unowned_files;
    foreach my $uid (@unowned_files){
        my $user="?";
        if (exists $uid_uidname{$uid}){
            $user=$uid_uidname{$uid};
        } 
        printf "%7s%7s  files of  %-50s\n", $uid,$unowned_files{$uid},$user;
    }
    if (
        $move_to_review_share_teacher eq "all-orphaned-users" or
        $copy_to_review_share_teacher eq "all-orphaned-users" or
        $move_to_review_share_project eq "all-orphaned-users" or
        $copy_to_review_share_project eq "all-orphaned-users" or
        $move_to_review_share_class eq "all-orphaned-users" or
        $copy_to_review_share_class eq "all-orphaned-users"
       ){
        # continue
    } else {
        exit;
    }
}


# userlist according to options
@userlist=&create_userlist($login,$classes,0,$projects,0,$student,
                               $rooms,$ws,$administrator,$check);
# create hash for faster access
foreach my $login (@userlist){
    $userlist{$login}="seen";
}


# --info
if ($info==1){
    foreach my $login (@userlist){
        print "$login\n";
    }
    exit;
}


# calculating review end date
if ($copy_to_review ne "" or 
    $move_to_review ne "" or
    $copy_to_review_home ne "" or
    $move_to_review_home ne "" or
    $copy_to_review_share_teacher ne "" or
    $move_to_review_share_teacher ne "" or
    $copy_to_review_share_project ne "" or
    $move_to_review_share_project ne "" or
    $copy_to_review_share_class ne "" or
    $move_to_review_share_class ne ""
   ){
    #print "Calculating review end date\n";
    my $epoch_now=time; # unix seconds
    #print "Now: $epoch_now\n";
    my $epoch_end_date=$epoch_now+86400*$review_days;
    #print "End: $epoch_end_date\n";

    my $end_date=localtime($epoch_end_date);
    my $end_day=$end_date->mday;
    if (length($end_day) == 1) {$end_day = "0$end_day";}
    my $end_month=$end_date->mon+1;
    if (length($end_month)  == 1) {$end_month = "0$end_month";}
    my $end_year=$end_date->year+1900;

    $review_end_date=$end_year."-".$end_month."-".$end_day;
    print "Review end date is: $review_end_date\n";
}

# --find-files
# --copy-to-review 
# --move-to-review
if ($find_files ne "" or $copy_to_review ne "" or $move_to_review ne ""){
    if ($find_files ne ""){
        # use that as a search dir
    } elsif ($copy_to_review ne ""){
        $find_files = $copy_to_review;
    } elsif ($move_to_review ne ""){
        $find_files = $move_to_review;
    }
    # exit if directory is nonexisting
    if (not -d  $find_files){
        print "\n$find_files is not an existing  directory\n\n";
        exit;
    }
}

# ... search in $find_files
if ($find_files ne "" and $owner ne ""){
    # Creating a list of files ####################################

    # if uidname, fetch uidnumber/if uidnumber, user doesn't need to exist
    # transform owner in uidnumber
    if ($owner=~/^[0-9]+$/){
        # its a number already
        $uid_number = $owner;
    } else {
        $uid_number = getpwnam($owner);
        if (not defined $uid_number){
            print "\nUser $owner does not exist!\n\n";
            exit;
        } 
    }

    if ($new_owner ne ""){
        if ($new_owner=~/^[0-9]+$/){
            $new_uid_number = $new_owner;
        } else {
            $new_uid_number = getpwnam($new_owner);
        }
    }
    
    # save printout to show only if something ist found
    my $printout="";    

    $printout=$printout.
        "############################################################\n".
        "Finding files in:\n".
        "   $find_files \n".
        "owner: $owner (UidNumber: $uid_number)\n";
    if ($new_owner ne ""){
        $printout=$printout."new owner: $new_owner (UidNumber: $new_uid_number)\n";
    }
    $printout=$printout.
        "############################################################\n".

    # find the files an save them in perl lists
    find(\&files_owned_by, $find_files);

    # sort the lists
    # dir list: longest first (so that empty dirs can be deleted)
    @find_dirs_list = sort { length($b) <=> length($a) } @find_dirs_list;

    # files  
    $count_found_files=$#find_files_list+1;
    $printout=$printout.sprintf "%6s files found\n", $count_found_files;
    foreach my $file (@find_files_list){
        &chown_print($file,$new_owner);
    }

    # links
    $count_found_links=$#find_links_list+1;
    $printout=$printout.sprintf "%6s links found\n", $count_found_links;
    foreach my $link (@find_links_list){
        &chown_print($link,$new_owner);
    }

    # dirs
    $count_found_dirs=$#find_dirs_list+1;
    $printout=$printout.sprintf "%6s dirs found\n", $count_found_dirs;
    foreach my $dir (@find_dirs_list){
        &chown_print($dir,$new_owner);
    }

    #strange stuff
    $count_found_strange=$#find_strange_list+1;
    $printout=$printout.sprintf "%6s stuff found\n",$count_found_strange;
    foreach my $strange (@find_strange_list){
        &chown_print($strange,$new_owner);
    }

    # found
    my $count_found=$count_found_files+
                    $count_found_links+
                    $count_found_dirs+
                    $count_found_strange;

    if ($count_found>0){
        # print everthing collected in $printout
        print $printout;
    } else {
        print "Nothing found (Files, Links, Dirs, Strange)\n";
    }

    my $target_dir="";
    my $target_dir_log="";
    my $log_file="";
    my $log_dir="";
    my $log_link="";
    my $log_strange="";

    # exit if nothing was found or only a find was issued, else prepare logging
    if (($count_found_files ==0 and 
         $count_found_dirs==0 and 
         $count_found_links==0 and 
         $count_found_strange==0
        ) or (
         $copy_to eq "" and
         $move_to eq "" and
         $copy_to_review eq "" and
         $move_to_review eq ""
        )){
        exit;        
    } else {
        # There was something found, and copy or move is wanted: prepare logging
        if ($copy_to ne ""){
            # option --copy-to given
            $target_dir=$copy_to;
        } elsif ($move_to ne ""){
            # option --move-to given
            $target_dir=$move_to;
        } elsif ($copy_to_review ne ""){
            # option --copy-to-review given
            $target_dir=$copy_to_review."/".
                        ${Language::alumni}."/".
                        $owner.
                        ${Language::alumni_review_string}.
                        $review_end_date;
        } elsif ($move_to_review ne ""){
            # option --move-to-review given
            $target_dir=$move_to_review."/".
                        ${Language::alumni}."/".
                        $owner.
                        ${Language::alumni_review_string}.
                        $review_end_date;
        }
        
        # create log dirs and log files
        $target_dir_log = $target_dir."/.sophomorix-janitor.log";
        $log_file=$target_dir_log."/file.log";
        $log_dir=$target_dir_log."/dir.log";
        $log_link=$target_dir_log."/link.log";
        $log_strange=$target_dir_log."/strange.log";
        make_path($target_dir); # same as: mkdir -p
        make_path($target_dir_log); # same as: mkdir -p
        open(FILELOG, ">>$log_file");
        open(DIRLOG, ">>$log_dir");
        open(LINKLOG, ">>$log_link");
        open(STRANGELOG, ">>$log_strange");
    }


    ######################################################################
    # Do something with the lists
    ######################################################################
    print "######## Working on $count_found_files files ########\n";
    foreach my $source_abs_file (@find_files_list){
        my $target_abs_file=$source_abs_file;
        # decide where to copy/move the files
        # ??? avoid copy-to and move-to together ????????
        if ($copy_to ne ""){
            # option --copy-to given
            $target_abs_file=~s/$find_files/$target_dir/;
            &review_file("copy",$source_abs_file,$target_abs_file);
        } elsif ($move_to ne ""){
            # option --move-to given
            $target_abs_file=~s/$find_files/$target_dir/;
            &review_file("move",$source_abs_file,$target_abs_file);
        } elsif ($copy_to_review ne ""){
            # option --copy-to-review given
            $target_abs_file=~s/$copy_to_review/$target_dir/;
            &review_file("copy",$source_abs_file,$target_abs_file);
        } elsif ($move_to_review ne ""){
            # option --move-to-review given
            $target_abs_file=~s/$move_to_review/$target_dir/;
            &review_file("move",$source_abs_file,$target_abs_file);
        } else {
            # --find-files without --copy-to/move-to --> do nothing
            &review_file("show",$source_abs_file,$target_abs_file);
        }
    } # end walking through @find_files_list

    ######################################################################
    print "######## Working on $count_found_links links ########\n";
    foreach my $source_abs_link (@find_links_list){
        my $target_abs_link=$source_abs_link;
        # decide where to copy/move the links
        # ??? avoid copy-to and move-to together ????????
        if ($copy_to ne ""){
            # option --copy-to given
            $target_abs_link=~s/$find_files/$target_dir/;
            &review_link("copy",$source_abs_link,$target_abs_link);
        } elsif ($move_to ne ""){
            # option --move-to given
            $target_abs_link=~s/$find_files/$target_dir/;
            &review_link("move",$source_abs_link,$target_abs_link);
        } elsif ($copy_to_review ne ""){
            # option --copy-to-review given
            $target_abs_link=~s/$copy_to_review/$target_dir/;
            &review_link("copy",$source_abs_link,$target_abs_link);
        } elsif ($move_to_review ne ""){
            # option --move-to-review given
            $target_abs_link=~s/$move_to_review/$target_dir/;
            &review_link("move",$source_abs_link,$target_abs_link);
        } else {
            # --find-files without --copy-to/move-to --> do nothing
            &review_link("show",$source_abs_link,$target_abs_link);
        }
    } # end walking through @find_links_list

    ######################################################################
    print "######## Working on $count_found_dirs directories ########\n";
    foreach my $source_abs_dir (@find_dirs_list){
        my $target_abs_dir=$source_abs_dir;
        # decide where to copy/move the dirs
        # ??? avoid copy-to and move-to together ????????
        if ($copy_to ne ""){
            # option --copy-to given
            $target_abs_dir=~s/$find_files/$target_dir/;
            &review_dir("copy",$source_abs_dir,$target_abs_dir);
        } elsif ($move_to ne ""){
            # option --move-to given
            $target_abs_dir=~s/$find_files/$target_dir/;
            &review_dir("move",$source_abs_dir,$target_abs_dir);
        } elsif ($copy_to_review ne ""){
            # option --copy-to-review given
            $target_abs_dir=~s/$copy_to_review/$target_dir/;
            &review_dir("copy",$source_abs_dir,$target_abs_dir);
        } elsif ($move_to_review ne ""){
            # option --move-to-review given
            $target_abs_dir=~s/$move_to_review/$target_dir/;
            &review_dir("move",$source_abs_dir,$target_abs_dir);
        } else {
            # --find-files without --copy-to/move-to --> do nothing
            &review_dir("show",$source_abs_dir,$target_abs_dir);
        }
    } # end walking through @find_dirs_list

    ######################################################################
    print "######## Working on $count_found_strange strange stuff ########\n";
    foreach my $source_abs_strange (@find_strange_list){
        my $target_abs_strange=$source_abs_strange;
        # decide where to copy/move the links
        # ??? avoid copy-to and move-to together ????????
        if ($copy_to ne ""){
            # option --copy-to given
            $target_abs_strange=~s/$find_files/$target_dir/;
            &review_strange("copy",$source_abs_strange,$target_abs_strange);
        } elsif ($move_to ne ""){
            # option --move-to given
            $target_abs_strange=~s/$find_files/$target_dir/;
            &review_strange("move",$source_abs_strange,$target_abs_strange);
        } elsif ($copy_to_review ne ""){
            # option --copy-to-review given
            $target_abs_strange=~s/$copy_to_review/$target_dir/;
            &review_strange("copy",$source_abs_strange,$target_abs_strange);
        } elsif ($move_to_review ne ""){
            # option --move-to-review given
            $target_abs_strange=~s/$move_to_review/$target_dir/;
            &review_strange("move",$source_abs_strange,$target_abs_strange);
        } else {
            # --find-files without --copy-to/move-to --> do nothing
            &review_strange("show",$source_abs_strange,$target_abs_strange);
        }

    } # end walking through @find_strange_list

    # close logfiles
    close(FILELOG);
    close(DIRLOG);
    close(LINKLOG);
    close(STRANGELOG);

} elsif ($find_files ne "" and $owner eq ""){
    print "\n";
    print "  --find-files/--ff\n";
    print "  --copy-to-review/cprv\n";
    print "  --move-to-review/mvrv\n";
    print "    need additional option --owner\n\n";
    exit;
} 



# --move-to-review-home user
# --copy-to-review-home user
if ($move_to_review_home ne "" or 
    $copy_to_review_home ne ""){
    my $login="";
    if ($copy_to_review_home ne ""){
        $login=$copy_to_review_home;
    } elsif ($move_to_review_home ne ""){
        $login=$move_to_review_home;
    }
    my ($home,$type,$gecos,$group,$uidnumber) 
        = &fetchdata_from_account($login);
    my $target=$home."/".$login.${Language::alumni_review_string}.
               $review_end_date;
    if ($home eq ""){
        print "\n";
        print "ERROR: Could not determine \$HOME of $login\n";
        print "       I do not know from where to move/copy the data!\n\n";
        exit;
    }
    print "########## Moving/Copying data in \$HOME of $gecos ($login): ##########\n";
    print "    $home\n";
    print "  for review into directory:\n";
    print "    $target\n";
    my $command="";
    if ($copy_to_review_home ne ""){
        $command="sophomorix-janitor --find-files $home --owner $login".
                 " --copy-to $target";
    } elsif ($move_to_review_home ne ""){
        $command="sophomorix-janitor --find-files $home --owner $login".
                 " --move-to $target";
    }
    if ($Conf::log_level==2){
        $command=$command." -v";
    } elsif ($Conf::log_level==3){
        $command=$command." -vv";
    }
    print "  $command\n";
    system("$command");
    # make sure this account is usable
    my $repair_command="sophomorix-repair --repairhome -u $login";
    print "\n";
    print "BEGIN OUTPUT of: $repair_command\n";
    system("$repair_command");
    print "END OUTPUT of: $repair_command\n";
}



# --move-to-review-share-teacher user
# --copy-to-review-share-teacher user
if ($move_to_review_share_teacher ne "" or 
    $copy_to_review_share_teacher ne ""){
    my @logins=();

    if ($copy_to_review_share_teacher eq "all-orphaned-users"){
        @logins = @unowned_files;
    } elsif ($move_to_review_share_teacher ne "all-orphaned-users"){
        @logins = @unowned_files;
    } elsif ($copy_to_review_share_teacher ne ""){
        push @logins, $copy_to_review_share_teacher;
    } elsif ($move_to_review_share_teacher ne ""){
        push @logins, $move_to_review_share_teacher;
    }

    foreach my $login (@logins){
        my ($home,$type,$gecos,$group,$uidnumber) 
            = &fetchdata_from_account($login);
        if ($gecos eq ""){
            $gecos="unknown user";
        }

        my $source=${DevelConf::share_teacher};
        my $target=${DevelConf::share_teacher}."/".
                   ${Language::alumni}."/".
                   $login.
                   ${Language::alumni_review_string}.
                   $review_end_date;

        print "########## Moving/Copying data of $gecos ($login) in: ##########\n";
        print "    $source\n";
        print "  for review into directory:\n";
        print "    $target\n";
        my $command="";
        if ($copy_to_review_share_teacher ne ""){
            $command="sophomorix-janitor --find-files $source --owner $login".
                     " --copy-to $target";
        } elsif ($move_to_review_share_teacher ne ""){
            $command="sophomorix-janitor --find-files $source --owner $login".
                     " --move-to $target";
        }
        print "  $command\n";
        system("$command");
    }
}


# --move-to-review-share-project
# --copy-to-review-share-project
# --move-to-review-share-class
# --copy-to-review-share-class
if ($move_to_review_share_project ne "" or 
    $copy_to_review_share_project ne "" or
    $move_to_review_share_class ne "" or 
    $copy_to_review_share_class ne ""
   ){

    my @logins=();
    my $review_dir="";
    # /home/share/projects
    # home/share/classes
    print "UID: @unowned_files\n";

    if ($move_to_review_share_project ne ""){
        push @logins, $move_to_review_share_project;
        $review_dir=${DevelConf::share_projects};
    } elsif ($copy_to_review_share_project ne ""){
        push @logins, $copy_to_review_share_project;
        $review_dir=${DevelConf::share_projects};
    } elsif ($move_to_review_share_class ne ""){
        push @logins, $move_to_review_share_class;
        $review_dir=${DevelConf::share_classes};
    } elsif ($copy_to_review_share_class ne ""){
        push @logins, $copy_to_review_share_class;
        $review_dir=${DevelConf::share_classes};
    } else {
        print "\nERROR: Cannot determine directory to work on\n\n";
	exit;
    }

    foreach my $login (@logins){
        my ($home,$type,$gecos,$group,$uidnumber) 
            = &fetchdata_from_account($login);
        if ($gecos eq ""){
            $gecos="unknown user";
        }
        print "######## Working on $review_dir ########\n";
        # go through subdirectories
        opendir REVDIR, $review_dir;
        my $count=0;
        my @dirs_to_walk_through;
        foreach my $dir (readdir REVDIR){
            if ($dir eq "." or $dir eq ".."){
                next;
            }
            push @dirs_to_walk_through, $dir;    
        }
        @dirs_to_walk_through  = sort @dirs_to_walk_through;

        my @logins=();
        if ($login eq "all-orphaned-users"){
            # all orphaned users
	    @logins = @unowned_files;
        } else {
            # only the user given by option
            @logins=("$login");
        }

        foreach my $login (@logins){
        foreach my $dir (@dirs_to_walk_through){
            if ($dir eq "." or $dir eq ".."){
                next;
            }
	    $count++;

            print"\n";
	    print "######## $count) $dir ########\n";
            my $source=$review_dir."/".$dir;
            my $target=$review_dir."/".$dir."/".
                       ${Language::alumni}."/".
                       $login.
                       ${Language::alumni_review_string}.
                       $review_end_date;
            if ($Conf::log_level>=2){
                print "  ########## Moving/Copying data of $gecos ($login) in: ##########\n";
                print "      $source\n";
                print "    for review into directory:\n";
                print "      $target\n";
            }
            # execute sophomorix-janitor
            my $command="";
            if ($move_to_review_share_project ne ""){
                $command="sophomorix-janitor --find-files $source --owner $login".
                         " --move-to $target";
            } elsif ($copy_to_review_share_project ne ""){
                $command="sophomorix-janitor --find-files $source --owner $login".
                    " --copy-to $target";
            } elsif ($move_to_review_share_class ne ""){
                $command="sophomorix-janitor --find-files $source --owner $login".
                     " --move-to $target";
            } elsif ($copy_to_review_share_class ne ""){
                $command="sophomorix-janitor --find-files $source --owner $login".
                     " --copy-to $target";
            }
            print "  $command\n";
            system("$command");
        }
        }
    }
}



# --show-review-data
# --delete-review-data
if ($show_review_data==1 or 
    $delete_review_data==1
   ){
    print "Looking for Directories containing ${Language::alumni_review_string}... \n";
    my @searchdirs=("/home/students","/home/share","/home/teachers");
    # find the files an save them in perl lists
    find(\&all_review_dirs, @searchdirs);
    @review_dirs_overtime=sort @review_dirs_overtime;
    @review_dirs=sort @review_dirs;
    print "\n";
    print "+------+-------------------------------------------------------------------------\n";
    print "| Days | Overtime Directory (in asciibetic order)\n";
    print "+------+-------------------------------------------------------------------------\n";
    foreach my $dir (@review_dirs_overtime){
        printf "|%5s | %-70s\n",$review_dirs_days_to_go{$dir},$dir;
    }
    print "+------+-------------------------------------------------------------------------\n";
    print "\n";

    if ($show_review_data==1){
        print "+------+-------------------------------------------------------------------------\n";
        print "| Days | Review Directory (in asciibetic order)\n";
        print "+------+-------------------------------------------------------------------------\n";
        foreach my $dir (@review_dirs){
            printf "|%5s | %-70s\n",$review_dirs_days_to_go{$dir},$dir;
        }
        print "+------+-------------------------------------------------------------------------\n";
    }
    if ($delete_review_data==1){
        print "\n";
        print "Please delete the Directories from the table 'Overtime Directory' by hand!\n";
        print "\n";
    }
    exit;
}




# --list-db-junk / --kill-db-junk
if ($list_db_junk==1 or $kill_db_junk==1){
    print "Analyzing ldap database ...\n";
#    &check_connections();
    my $dbh=&db_connect();

    # TEST 1
    print "TEST 1: gids in project_groups must be existing\n";
    my %gid_db=();
    my @gid_db=();
    my $sth= $dbh->prepare( "SELECT projectid,membergid from project_groups" );
    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
        # split the array, to give better names
        my ($project_id,$gid)=@$row;
        if (not exists $gid_db{$gid}){
            $gid_db{$gid}="seen";
            push @gid_db, $gid;
        }
        #print "   * $gid (project id: $project_id)\n";
    }
    @gid_db = sort @gid_db;
    # test it
    foreach my $gidnumber_test (@gid_db){
        print "* Testing gid $gidnumber_test:\n";
        # fetching gidname
        my ($gid)= $dbh->selectrow_array( "SELECT gid 
                                           FROM groups 
                                           WHERE gidnumber='$gidnumber_test'
                                          ");
        if (defined $gid){
            print "    $gidnumber_test is group $gid\n";
        } else {
            print "    $gidnumber_test is a nonexisting group\n";
            # DELETE
            my $sql="DELETE FROM project_groups WHERE membergid=$gidnumber_test";
            print "  Suggested action for $gidnumber_test:\n";
            print "  $sql\n";
            if ($kill_db_junk==1){
                print "  Do you want to delete $gidnumber_test ",
                      "with the SQL command? y/n\n";
                my $do_it=<>;
                chomp($do_it);
                if($do_it eq "y"){
                   $dbh->do($sql);

                } else {
                    print "Skipping deletion of group $gidnumber_test\n";
                }
            }
        }
    }
    # TEST 2
    # follows here


    &db_disconnect($dbh);
    exit;
}





# --list-foreign-files
if ($list_foreign_files==1){
    foreach my $login (@userlist){
        my ($home) = &fetchdata_from_account($login);
        #print "Looking for foreign files of $login in $home ...\n";
        my $prune= "-path $home/${Language::to_handoutcopy_dir} -prune -o".
                  " -path $home/${Language::share_dir} -prune -o".
                  " -path $home/${Language::handoutcopy_dir} -prune -o".
                  " -path $home/${Language::collect_dir} -prune -o".
                  " -path $home/${Language::collected_dir} -prune -o".
                  " -path $home/${Language::task_dir} -prune -o". 
                  " -path $home/${Language::handout_dir} -prune -o".
      	      #    " -path $home/${Language::user_attic}/mailsync -prune -o".
      	          " -path $home/.Trash-* -prune -o";
        system ("find $home $prune ! -user $login -printf \"%u\t%g\t%p\n\"");
    }
    exit;
}


# --check-horde
if ($check_horde==1){
    my %horde_users=();
    my $database="horde";
    my $hostname="localhost";
    my $password=&get_horde_password();

    my $dsn = "DBI:mysql:database=$database;host=$hostname";
    my $dbh = DBI->connect($dsn, "horde",$password);
    print "Checking horde database for obsolete stuff\n";
    my $sth = $dbh->prepare( "SELECT pref_uid FROM horde_prefs");

    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
	my ($uid)=@$row;
        $horde_users{$uid}="seen";
	print "$uid\n";
    }

    while(my ($uid, $value) = each(%horde_users)) {
	my $return=system("id $uid > /dev/null");
        if ($return!=0){
        print "id returned $return\n";
        print "  User $uid ninexisting in system\n"; 
       }
    }

    $dbh->disconnect();
    print "... Done\n";
    exit;
}



# --dump-horde
if ($dump_horde==1){
    my $command="mysqldump --extended-insert=0 horde";
    #print "$command\n";
    system("$command");
}



# --dump-pg-ldap
if ($dump_pg_ldap==1){
    my $timestamp=&zeit_stempel;
    print "Timestamp: $timestamp\n";
    &backup_user_database($timestamp, "janitor.sql","JANITOR");
    exit;
}



# --check-project-members
if ($check_project_members==1){
    print "Checking ALL project memberships ...\n";

    # save this in a hash to avoid multiple database queries
    my %projectid_gidnumber=();


    # %current_members is the table groups_users
    # key:    gidnumber
    # value:  memberuidnumber
    my %current_members=();

    # %memberrelation contains the table projects_memberprojects
    # key:   projectID 
    # value: memberprojectIDs
    my %memberrelation=();

    my $dbh=&db_connect();

    # read current members
    my $sth0= $dbh->prepare( "SELECT groups_users.gidnumber,
                                     groups_users.memberuidnumber
                              FROM groups_users
                              JOIN projectdata 
                              ON projectdata.gidnumber = groups_users.gidnumber
                              ORDER BY gidnumber
                           ");
    $sth0->execute();
    my $array_ref0 = $sth0->fetchall_arrayref();
    foreach my $row (@$array_ref0){
        my ($gidnumber,$memberuidnumber)=@$row;
        $current_members{$gidnumber}{$memberuidnumber}="seen";
    }


    # read what should be created
    my $sth= $dbh->prepare( "SELECT id,gid,gidnumber,longname,maxmembers 
                             FROM projectdata 
                             ORDER BY gid");
    $sth->execute();
    my $array_ref = $sth->fetchall_arrayref();
    foreach my $row (@$array_ref){
        my ($id,$gid,$gidnumber,$longname,$maxmembers)=@$row;
        $projectid_gidnumber{$id}=$gidnumber;
        # nn
        if($Conf::log_level>=2){
            print "$id, $gid, $longname, $gidnumber, $maxmembers\n";       
        }

        # 1) adding admins
        my $sth1= $dbh->prepare( "SELECT uidnumber
                             FROM projects_admins 
                             WHERE projectid=$id");
        $sth1->execute();
        my $array_ref1 = $sth1->fetchall_arrayref();
        foreach my $row (@$array_ref1){
            my ($uidnumber)=@$row;
            # nn
            my $user=&user_name_from_id($uidnumber);
            if($Conf::log_level>=2){
                print "   * $uidnumber ($user) is admin in $gidnumber ($gid)\n";
            }
            #$future_members{$gidnumber}{$uidnumber}="admin";
            &update_future_members($gidnumber,
                                   $uidnumber,
                                   "admin");
        }


        # 2) adding members_by_option
        my $sth2= $dbh->prepare( "SELECT memberuidnumber
                             FROM projects_members 
                             WHERE projectid=$id");
        $sth2->execute();
        my $array_ref2 = $sth2->fetchall_arrayref();
        foreach my $row (@$array_ref2){
            my ($uidnumber)=@$row;
            # nn
            my $user=&user_name_from_id($uidnumber);
            if($Conf::log_level>=2){
                print "   * $uidnumber ($user) is MemberByOption in $gidnumber ($gid)\n";
            }
            #$future_members{$gidnumber}{$uidnumber}="MemberByOption";
            &update_future_members($gidnumber,
                                   $uidnumber,
                                   "MemberByOption");
        }


        # 3) adding members by class(group)
        my $sth3= $dbh->prepare( "SELECT membergid
                             FROM project_groups 
                             WHERE projectid=$id");
        $sth3->execute();
        my $array_ref3 = $sth3->fetchall_arrayref();
        foreach my $row (@$array_ref3){
            my ($membergid)=@$row;
            # nn
            my $group=&group_name_from_id($membergid);
            if($Conf::log_level>=2){
                print " * $membergid ($group) is MemberByClass in $gid\n";
            }
            # A) Adding Teachers (NT ANYMORE NEEDED)
            #my $sth3a= $dbh->prepare( "SELECT memberuidnumber
            #                     FROM groups_users 
            #                     WHERE gidnumber=$membergid");
            #$sth3a->execute();
            #my $array_ref3a = $sth3a->fetchall_arrayref();
            #foreach my $row (@$array_ref3a){
            #    my ($uidnum)=@$row;
            #    # nn
            #    my $user=&user_name_from_id($uidnum);
            #    if($Conf::log_level>=2){
            #        print "   * $uidnum ($user) is MemberByClass ",
            #              "in $membergid ($group)\n";
            #    }
            #    #$future_members{$gidnumber}{$uidnum}="MemberByClass";
            #    &update_future_members($gidnumber,
            #                           $uidnum,
            #                           "MemberByClass");
            #}
            # B) Adding Students (primary group)
            my $sth3b= $dbh->prepare( "SELECT uid,uidnumber
                                FROM userdata
                                WHERE gidnumber=$membergid");
            $sth3b->execute();
            my $array_ref3b = $sth3b->fetchall_arrayref();
            foreach my $row (@$array_ref3b){
                my ($uid,$uidnum)=@$row;
                if($Conf::log_level>=2){
                    print "   * $uidnum ($uid) is MemberByClass ",
                          "in $membergid ($group)\n";
                }
                #$future_members{$gidnumber}{$uidnum}="MemberByClass";
                &update_future_members($gidnumber,
                                       $uidnum,
                                       "MemberByClass");
            }
        }

        # 4) Step1 (projectmembers) Save Project -> Memberprojects 
        my $sth4= $dbh->prepare( "SELECT memberprojectid
                             FROM projects_memberprojects 
                             WHERE projectid=$id");
        $sth4->execute();
        my $array_ref4 = $sth4->fetchall_arrayref();
        foreach my $row (@$array_ref4){
            my ($memberprojectid)=@$row;
            # nn
            if($Conf::log_level>=2){
                print "  * $memberprojectid (ProjectID)\n";
            }
            $memberrelation{$id}{$memberprojectid}="seen";
        }
    }


    my $i=0;
    my @update_order=();
    my @delete=();
    my $count=0;

    until (keys %memberrelation == 0){
        # CAREFUL: do not modify the hash that you are 
        # using as a loop!
        $count++;
        if($Conf::log_level>=2){
            print "############### Run $count ###############\n";
        }
        # find, what can be deleted in this step
        while (my ($topkey,$topvalue) = each %memberrelation){
            if($Conf::log_level>=2){
                print "$topkey --> $topvalue\n";
            }
            while (my ($key,$value) = each %{ $memberrelation{$topkey} }){
                if($Conf::log_level>=2){
                    print "   $key --> $value\n";
                }
                if (not exists $memberrelation{$key}){
                    if($Conf::log_level>=2){
                        print "    * $key is not on toplevel:\n";
                        print "      Add members of project $key to $topkey\n";
                    }
                    # get gidnumber of project
                    my $gidnumber_fetch=$projectid_gidnumber{$key};
                    my $gidnumber_push=$projectid_gidnumber{$topkey};

                    # future members of project $key
                    while (my ($memberuid,$reason) = 
			   each %{ $future_members{$gidnumber_fetch} }){
                        if($Conf::log_level>=2){
                            print "        * $memberuid is in ",
                                  "$gidnumber_fetch ($reason)\n";
                        }
                        &update_future_members($gidnumber_push,
                                               $memberuid,
                                               "ByMemProject");
                    }
                    push @update_order, "${key}->${topkey}";
                    push @delete, "$topkey,$key";
	        }
            }
        }
        # END: find, what can be deleted in this step

    
        # delete findings of this step
        if($Conf::log_level>=2){
            print "### Deleting findings or run $count:\n";
            print "  1) Deleting secondlevel keys:\n";
        }
        foreach my $item (@delete){
            my ($topkey,$key)=split(/,/,$item);
            if($Conf::log_level>=2){
                print "     Delete: $topkey --> $key\n";
            }
            my $deleted = delete($memberrelation{$topkey}{$key});
        }
        if($Conf::log_level>=2){
            print "  2) Deleting empty toplevel keys:\n";
        }
        while (my ($topkey,$topvalue) = each %memberrelation){
            if (keys $memberrelation{$topkey} == 0){
                if($Conf::log_level>=2){
                    print "     $topkey is empty\n";
                }
                my $deleted = delete($memberrelation{$topkey});
            } else {
                if($Conf::log_level>=2){
                    print "   $topkey has elements\n";
                }
            }
        }
        # END: delete findings of this step


        # list whats left
        if($Conf::log_level>=2){
            print "\n### Thats left:\n";
        }
        while (my ($topkey,$topvalue) = each %memberrelation){
            if($Conf::log_level>=2){
                print "$topkey --> $topvalue\n";
            }
            while (my ($key,$value) = each %{ $memberrelation{$topkey} }){
                if($Conf::log_level>=2){
                    print "   $key --> $value\n";
                }
            }
        }
        # END: list whats left
    } # END: until


    &db_disconnect($dbh);
    if($Conf::log_level>=2){
        print "### Update order:\n";
    }
    foreach my $pro (@update_order){
        if($Conf::log_level>=2){
	    print "$pro ";
        }
    }
    if($Conf::log_level>=2){
        print "\n... Done after $count runs\n";
    }
    print "Memberships to be added:\n";
    # anzulegen: future hash durchgehen, welche fehlen
    # $gid --> $uid --> whymember($value)
    my $add=0;
    foreach my $gid (keys %future_members) {
         foreach my $uid (keys %{ $future_members{$gid} }) {
             my $value = $future_members{$gid}->{$uid};
             my $user=&user_name_from_id($uid);
             my $group=&group_name_from_id($gid);
             if (exists $current_members{$gid}{$uid}){
                 if($Conf::log_level>=2){
                 print "   OK: $gid($group) --> $uid($user) --> $value\n";
                 }
             } else {
                 print "  ADD: $gid($group) --> $uid($user) --> $value\n";
                 $add++;
             }
         }
    }
    print "$add users must be added as members\n\n";


    print "Memberships to be deleted:\n";
    # delete: current hash durchgehen, welche sind zuviel
    # $gid --> $uid --> whymember($value)
    my $delete=0;
    foreach my $gid (keys %current_members) {
         foreach my $uid (keys %{ $current_members{$gid} }) {
             my $value = $current_members{$gid}->{$uid};
             my $user=&user_name_from_id($uid);
             my $group=&group_name_from_id($gid);
             if (exists $future_members{$gid}{$uid}){
                 if($Conf::log_level>=2){
                 print "   OK: $gid($group) --> $uid($user) --> $value\n";
                 }
             } else {
                 print "  DEL: $gid($group) --> $uid($user) --> $value\n";
                 $delete++;
             }
         }
    }
    print "$delete users must be deleted as members\n\n";
}



# ===========================================================================
# list files and do something
# ===========================================================================
my @dirs=("/home/students/");

# --listfiles --user user)
if ($listfiles==1 and $login ne ""){
    my $out_file=$list_files_path."/".$login."-dir-list.txt";
    my $out_file_share=$list_files_path."/".$login."-dir-list-share.txt";
    my $out_file_summary=$list_files_path."/".
                         $login."-dir-list-summary.txt";
    my $out_file_tmp=$list_files_path."/".
                         $login."-dir-list-tmp.txt";
    open(SUMMARY, ">$out_file_summary");

    ########## searching $HOME of a user ##########
    # fetching $HOME
    my ($search_dir,$type,$gecos,$group,$uidnumber) 
          = &fetchdata_from_account($login);
    print SUMMARY "##### Ordner im persoenlichen Ordner: #####\n";
    my $search_command="find $search_dir -maxdepth 1 ".
                       "-type d -user $login > $out_file";
    print "Looking for files of user $login with uid $uidnumber:\n",
          "   $search_command\n";
    system($search_command);

    # Performance: 
    #   A) im home eines users(20 GB): ca. 30s
    #      Dateigroesse 6,7 MB, 72.000 Zeilen (1.800 Schreibmaschinenseiten)
    #   B) in /home user mit 20GB: ca: 
    #      Dateigroesse xx MB, x.000 Zeilen ( Schreibmaschinenseiten)
    # langsam
    #    find( \&files_of_uid,  @dirs);
    #    exit;

    # processing out_file
    my $count=-1;
    open(OUT, "<$out_file");
    while(<OUT>){
        my $dir=$_;
        chomp($dir);
        # ignore home
        if ($dir eq $search_dir){
            next;
        }
        # ignore dotfiles (--dotfiles, --nodotfiles) ?????
        if ($dir=~/$search_dir\/\./) {
            #print "Skipping dotfile $dir\n";
            next;
        }
        my $count_files=`find '$dir' -type f | wc -l`;
        my $count_dirs=`find '$dir' -type d | wc -l`;
        chomp($count_files);
        chomp($count_dirs);
        $count_dirs=$count_dirs-1; # ignore ..
        print SUMMARY "   $dir  --->  $count_dirs Ordner ",
                      "/ $count_files Dateien \n";
    }
    close(OUT);


    ########## searching /home/share ##########
    my $search_dir_share = "/home/share";
    print SUMMARY "##### Ordner in den Tausch-Ordnern: #####\n";
    #my $search_command_share="find $search_dir_share -maxdepth 2 ".
    #                   "-type d -user $login > $out_file_share";
    #print "Executing:\n   $search_command_share\n";
    #system($search_command_share);
    print "Looking in $search_dir_share/teachers for dirs:\n";
    system("find ${search_dir_share}/teachers -maxdepth 1 -type d -user $login > $out_file_share");

    print "Looking in $search_dir_share/classes for dirs:\n";
    system("find ${search_dir_share}/classes -maxdepth 2 -type d -user $login >> $out_file_share");

    print "Looking in $search_dir_share/subclasses for dirs:\n";
    system("find ${search_dir_share}/subclasses -maxdepth 2 -type d -user $login >> $out_file_share");

    print "Looking in $search_dir_share/projects for dirs:\n";
    system("find ${search_dir_share}/projects -maxdepth 2 -type d -user $login >> $out_file_share");

    print "Looking in $search_dir_share/exams for dirs:\n";
    system("find ${search_dir_share}/exams -maxdepth 2 -type d -user $login >> $out_file_share");

    print "Looking in $search_dir_share/school for dirs:\n";
    system("find ${search_dir_share}/school -maxdepth 1 -type d -user $login >> $out_file_share");

   # ????? -ignore_readdir_race einfuegen

    my $prune_string="";
    my %prune_hash=();

    my $count_share=-1;
    open(OUT, "<$out_file_share");
    while(<OUT>){
        my $dir=$_;
        chomp($dir);
        my $count_files=`find '$dir' -type f | wc -l`;
        my $count_dirs=`find '$dir' -type d | wc -l`;
        chomp($count_files);
        chomp($count_dirs);
        $count_dirs=$count_dirs-1; # ignore ..
        print SUMMARY "   $dir  --->  $count_dirs Ordner ",
                      "/ $count_files Dateien \n";
        # save dir and prune it in next find
        $prune_string=$prune_string."-path \"$dir\" -prune -o ";
	$prune_hash{$dir}="seen";
    }
    close(OUT);

# find /home/share/teachers/ -path /home/share/teachersopenoffice -prune -type f -user bz -o -print

    print SUMMARY "##### Sonstige Dateien in den Tausch-Ordnern: #####\n";

    #my $search_command_share_files="find $search_dir_share $prune_string ".
    #                   "-type f -user $login -o -print >> $out_file_summary";
    my $search_command_share_files="find $search_dir_share  $prune_string ".
                       "-type f -user $login >> $out_file_tmp";
    print "Looking in $search_dir_share for files:\n",
          "   $search_command_share_files\n";
    system($search_command_share_files);

    # remove pruned dirs from results 
    open(TMP, "<$out_file_tmp");
    while(<TMP>){
        my $line = $_;
        chomp($line);
        if (not exists $prune_hash{$line}){
            print SUMMARY "  $line\n";
        } else {
            # ignored
        }
    }
    close(TMP);

    # tauschdirs:
    # A) dirs suchen mit owner ...
    #    B) darin Anzahl Dateien und Ordner
    # C) alle Dateien suchen, Ordner von A) auslassen

    close(SUMMARY);
    print "--> Summary written to $out_file_summary\n";
    exit;
}



######################################################################
# subs
######################################################################

sub review_file {
    my ($type,$source,$target) = @_;
    # shorten path for display
    my $display=$source;
    $display=~s/$find_files//;
    # remove leading / (if present)
    $display=~s/^\///;
    my $parent_dir=dirname($target);
    my ($perm,$uid,$gid,$atime,$mtime,$ctime) = &get_statinfo($source);
    my $logline=$target.$sep.
                $source.$sep.
                $perm.$sep.
                $uid.$sep.
                $gid.$sep.
                $atime.$sep.
                $mtime.$sep.
                $ctime.$sep.
                "";

    if ($type eq "copy" or $type eq "move"){
        print "  FILE: $display $Conf::log_level\n";
        if($Conf::log_level>=2){
            print "    Source:   $source\n";
            print "    cp/mv to: $target\n";
            print "    Parent:   $parent_dir\n";
        }
        # copy the file
        make_path($parent_dir); # same as: mkdir -p
        copy($source,$target);
        chmod 0644, $target;
        $logline=$logline."file moved".$sep;
    } elsif ($type eq "show") {
        if($Conf::log_level>=2){
            print "  FILE: $display\n";
        }
    }

    if ($type eq "move"){
        if (-e $target){
           print "    Deleting file: $source\n";
           # delete the file
           unlink $source;
           $logline=$logline."file deleted".$sep;
        }
    }
    $logline=$logline."\n";
    print FILELOG $logline;
}



sub review_link {
    my ($type,$source,$target) = @_;
    # shorten path for display
    my $display=$source;
    $display=~s/$find_files//;
    # remove leading / (if present)
    $display=~s/^\///;

    print "  LINK: $display\n";
    if($Conf::log_level>=2){
            print "    Omitted:   $source\n";
    }
    my $readlink = readlink $source;

    my $logline=$target.$sep.$source.$sep.$readlink.$sep."\n";
    # in case of move, delete link, so that dirs can be deleted
    if ($type eq "move"){
        print "    Deleting link: $source\n";
        # delete the file
        unlink $source;
        $logline=$logline."link deleted".$sep;
    }
    print LINKLOG $logline;
}



sub review_dir {
    my ($type,$source,$target) = @_;
    # shorten path for display
    my $display=$source;
    $display=~s/$find_files//;
    # remove leading / (if present)
    $display=~s/^\///;
    my ($perm,$uid,$gid,$atime,$mtime,$ctime) = &get_statinfo($source);
    my $logline=$target.$sep.
                $source.$sep.
                $perm.$sep.
                $uid.$sep.
                $gid.$sep.
                $atime.$sep.
                $mtime.$sep.
                $ctime.$sep.
                "";

    if ($type eq "copy"){
        print "  DIR: $display\n";
        # make sure target is redable/browsable 
        chmod 0775, $target;
        $logline = $logline."\n";
        print DIRLOG $logline;

    } elsif ($type eq "move") {
        print "  DIR: $display (move: ...)\n";
        # make sure target is redable/browsable 
        chmod 0775, $target;
        # delete empty source dirs/reown them
        my $result = &delete_dir_if_empty($source);
        $logline=$logline.$result.$sep."\n";

        print DIRLOG $logline;

    } elsif ($type eq "show") {
        if($Conf::log_level>=2){
            print "  DIR: $display\n";
        }
    }
    if($Conf::log_level>=2){
        print "    Source:   $source\n";
        print "    Target:   $target\n";
    }
}



sub review_strange {
    my ($type,$source,$target) = @_;
    # shorten path for display
    my $display=$source;
    $display=~s/$find_files//;
    # remove leading / (if present)
    $display=~s/^\///;

    print "STRANGE: $display\n";
    my ($perm,$uid,$gid,$atime,$mtime,$ctime) = &get_statinfo($source);
    my $logline=$target.$sep.
                $source.$sep.
                $perm.$sep.
                $uid.$sep.
                $gid.$sep.
                $atime.$sep.
                $mtime.$sep.
                $ctime.$sep.
                "\n";
    print STRANGELOG $logline;
}



sub pro_name {
    my ($id)=@_;
    my $dbh=&db_connect();
    my ($name)= $dbh->selectrow_array( "SELECT gid 
                                         FROM projectdata 
                                         WHERE id=$id
                                        ");
    &db_disconnect($dbh);
    return $name;
}



sub user_name_from_id {
    my ($uid) = @_;
    my $username = getpwuid($uid);
    return $username;
}



sub update_future_members {
    my ($gidnumber,$uidnumber,$reason) = @_;
    if (exists $future_members{$gidnumber}{$uidnumber}){
        if($Conf::log_level>=2){
            print "          uid $uidnumber exists.",
                  " Updating reason:\n";
        }
        # fetching old_reason
        my $oldreason=$future_members{$gidnumber}{$uidnumber};
        my $newreason="";
        $newreason=$oldreason.",".$reason;
        if($Conf::log_level>=2){
            print "          $oldreason -> $newreason\n";
        }
        $future_members{$gidnumber}{$uidnumber}="$newreason";
    } else {
        if($Conf::log_level>=2){
            print "          uid $uidnumber missing.".
                  " Creating entry.\n";
        }
        $future_members{$gidnumber}{$uidnumber}="$reason";
   }
}



sub group_name_from_id {
    my ($gid) = @_;
    my ($groupname) = getgrgid($gid);
    return $groupname;
}



sub get_homeshare_acls {
    my $acls=`getfacl ${DevelConf::share_share}`;
    #print "$acls";
    my @acl_lines=(split "\n", $acls);
    foreach my $line (@acl_lines){
        if($line=~/^\#/){ # # am Anfang bedeutet Kommentarzeile
            next;
        }
        my ($type,$who,$acl)=split(":", $line);
        if ($Conf::log_level>=2){
    	    print "LINE: $line\n";
            print "  Type: $type\n";
            print "   Who: $who\n";
            print "   ACL: $acl\n";
        }
        if ($who eq ""){
            next;
        }
        if ($who=~/^\d+$/){ # match only digits
            push @acl_homeshare_numeric, $who;
        }
        if ($type eq "user"){
            push @acl_homeshare_user, $who;
        }
        if ($type eq "group"){
            push @acl_homeshare_group, $who;
        }
    }
    @acl_homeshare_numeric =sort @acl_homeshare_numeric;
    @acl_homeshare_user = sort @acl_homeshare_user;
    @acl_homeshare_group =sort @acl_homeshare_group;
}



sub get_statinfo {
    my ($source) = @_;
    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,
        $mtime,$ctime,$blksize,$blocks)= stat($source);

    # atime
    my $time=localtime($atime);
    my $day=$time->mday;
    my $month=$time->mon+1;
    my $year=$time->year+1900;
    $atime=$year."-".$month."-".$day;

    # mtime
    $time=localtime($mtime);
    $day=$time->mday;
    $month=$time->mon+1;
    $year=$time->year+1900;
    $mtime=$year."-".$month."-".$day;

    # ctime
    $time=localtime($ctime);
    $day=$time->mday;
    $month=$time->mon+1;
    $year=$time->year+1900;
    $ctime=$year."-".$month."-".$day;

    # convert octal type and permissions to string 
    my $perm=sprintf "%04o",$mode & 07777;

    return $perm,$uid,$gid,$atime,$mtime,$ctime;
}



sub delete_dir_if_empty {
    my ($dir) = @_;
    my %owners=();
    ${owners}{0}=0;
    my $topowner=0; # owner which has the most content in dir
    # check if its a dir
    opendir EMPTYDIR, $dir;
    my $count=0;
    print "  * Checking if $dir is empty\n";
    foreach my $content (readdir EMPTYDIR){
        if ($content eq "." or $content eq ".."){
            next;
        }
        my $abs_content=$dir."/".$content;
        my ($perm,$uid,$gid,$atime,$mtime,$ctime) 
             = &get_statinfo($abs_content);
	print "$uid : $abs_content \n";
        if (exists $owners{$uid}){
            my $old_count=$owners{$uid};
            my $new_count=$old_count+1;
            $owners{$uid}=$new_count;
        } else {
            $owners{$uid}=1;
        }
        $count++;
    }

    # print owners
    print "Owners:\n";
    while (my ($owner,$num) = each %owners){
        print $owner , " ", $num, "\n";
        if ($owners{$owner} > $owners{$topowner}){
            $topowner=$owner
        }
    }

    print "Top-owner=$topowner ($dir)\n";

    if ($count == 0){
        # remove empty dir and return
        print "    EMPTY: $dir\n";
        rmdir $dir;
        return "dir deleted/empty";
    } else {
        # dir not empty, but contains files from others
        # chown to topowner
        print "    $count: $dir\n";
        print "          : new owner is uidnumber $topowner\n";
        chown $topowner, 0, $dir;
        my $message= "dir chowned to $topowner";
        return $message;
    }
}



sub all_review_dirs {
    my $epoch_now=time;
    if (-d $_ and m/${Language::alumni_review_string}/){
        my ($user,$date) =split("${Language::alumni_review_string}");
        if ($login ne "" and not exists $userlist{$user}){
            return;
        }
        my ($year,$month,$day)=split("-",$date);
        my $delete_epoch=timelocal(0, 0, 0, $day , ($month-1), $year);
        my $time_to_go=$delete_epoch-$epoch_now;
        if ($time_to_go>1){
            # integer 0 goes from -0,999 to +0,999 (double a big as usual)
            # add 86400 seconds to avoid this problem
            $time_to_go=$time_to_go+86400;
        }
        my $time_to_go_days=int($time_to_go/86400);
        if ($time_to_go_days<0){
	    push @review_dirs_overtime, $File::Find::name;
        } else {
	    push @review_dirs, $File::Find::name;
        }
        $review_dirs_days_to_go{$File::Find::name}=$time_to_go_days;
    }    
}



sub files_owned_by {
    # all files in dirs
    my ($dev, $ino, $mode, $nlink, $uidnum, $gid) = lstat($File::Find::name);
    if (not defined $uidnum){
        # files that have disappeared get uidnumber -1
        # i.e. --> they come in no list
        $uidnum=-1;
    }
    if ($uidnum == $uid_number){
        #print "$uidnum $mode $File::Find::name\n";
        if (-l $_){
            push @find_links_list, $File::Find::name;
        } elsif (-f $_){
            push @find_files_list, $File::Find::name;
        } elsif (-d $_){
            if ($File::Find::name eq $find_files){
                # do not add the top dir itself
            } else {
                push @find_dirs_list, $File::Find::name;
	    }
        } else {
            push @find_strange_list, $File::Find::name;
        }
    }
}



sub files_of_user {
    my ($login) = @_;
    # find /home -user bz > bz-files-in-home.log
}



sub chown_print {
    my ($data,$new_owner) = @_;
    if ($new_owner eq ""){
        if($Conf::log_level>=2){
            print "       $data\n";
        }
    } else {
        # lchown does not dereference links 
        # (changes owner of link, instead of source)
        lchown $new_uid_number, -1, $data;
    }
}



sub get_horde_password {
    my $password="";
    my $conf="/etc/horde/horde3/conf.php";
    if (-e $conf){
        open (HORDECONF,$conf);
        while(<HORDECONF>){
            if (m/password/){
		my $line=$_;
                chomp($line);
                my ($trash,$string)=split(/=/,$line);
                $string=~m/ '([A-Za-z0-9_]+)';/;
                $password=$1;
            }
        }
        close HORDECONF;
    } else {
        print "\nERROR: Retrieving horde password failed:\n";
        print "    $conf nonexisting\n\n";
        exit;
    }
    return $password;
}
