#!/usr/bin/perl -w
# $Id$
# This script (sophomorix-repair) 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


# next
# option --log-funny-files /path/to/file : list all files that should not be there in the log file
# time for bind repair: 3s for a user that is in 30 groups
#                       1s for a user that is in 3 groups

# update man page:
# add 4 options delete/repair-bind/links
# remove option --repairlinks (never existed)

# check printout in loglevel2

# If binds are to be used: replace obsolte funktions
   # obsolete functions in SophomorixBase.pm:
   # &Sophomorix::SophomorixBase::create_school_link($login); --> update_pointer_school
   # &create_share_link($login,$group,$group,$group_type);    --> update_pointer


# Bibliotheken
use strict;
use Getopt::Long;
use Sophomorix::SophomorixConfig;
use Sophomorix::SophomorixBase;
use Sophomorix::SophomorixAPI;
use Sophomorix::SophomorixPgLdap;
Getopt::Long::Configure ("bundling");
use DBI;
use Sophomorix::SophomorixPgLdap qw(show_modulename
                                   );
my @arguments = @ARGV;

#my $user="";
#my $gruppe="";

my $repairhome=0;

# delete following option ???????????
my $repairbinds=0;

my $always_bind=0;
my $quick=0;

# binds
my $delete_bind_extension="";  # default extension
my $repair_bind_extension="";  # default extension
my $delete_bind_extension_do_nothing="---";
my $repair_bind_extension_do_nothing="---";
# options for binds:
my $delete_binds=$delete_bind_extension_do_nothing;
my $repair_binds=$repair_bind_extension_do_nothing;

# links
my $delete_link_extension="";  # default extension
my $repair_link_extension="";  # default extension
my $delete_link_extension_do_nothing="---";
my $repair_link_extension_do_nothing="---";
# options for links:
my $delete_links=$delete_link_extension_do_nothing;
my $repair_links=$repair_link_extension_do_nothing;

my $help=0;
my $info=0;
my $filename="";
my $dateirechte=0;
my $htaccess=0;
my $command_number=0;
my $skiplock=0;
$Conf::log_level=1;


# not needed ?
my $login="";
my $classes="";
my $projects="";
my $student=0;

#my $lehrer=0;
#my $schueler=0;




###############################################################################
# Beginn
###############################################################################
# Datei, die die zu reparierenden Verzeichnisse beinhaltet
my $repair_directories="$DevelConf::devel_pfad/repair.directories";


# Parsen der Optionen
my $testopt=GetOptions(
           "help|h" => \$help,
           "info|i" => \$info,
           "verbose|v+" => \$Conf::log_level,
           "permissions" => \$dateirechte,
           "htaccess" => \$htaccess,
           "command-number=n" => \$command_number,
           "repairhome" => \$repairhome,
           "repairbinds" => \$repairbinds,
           "always-bind" => \$always_bind,
           "quick" => \$quick,
           "delete-binds:s" => \$delete_binds,
           "repair-binds:s" => \$repair_binds,
           "delete-links:s" => \$delete_links,
           "repair-links:s" => \$repair_links,
           "skiplock" => \$skiplock,
           "file|f=s" => \$filename,
           "user|u=s" => \$login,
           "class|classes|c=s" => \$classes,
           "project|projects|p=s" => \$projects,
           "student|students|s" => \$student,
          );


# Prüfen, ob Optionen erkannt wurden
&check_options($testopt);
if ($quick==1 and 
    $repair_binds eq $repair_bind_extension_do_nothing and 
    $delete_binds eq $delete_bind_extension_do_nothing ){
    print "\n Option --quick can only be used with --repair-binds",
          " or --delete-binds\n\n";
    exit;
}



# use options as extensions
$delete_bind_extension=$delete_binds;
$repair_bind_extension=$repair_binds;
$delete_link_extension=$delete_links;
$repair_link_extension=$repair_links;


if ($dateirechte==0 
    and $htaccess==0
    and $info==0
    and $repairhome==0
    and $repairbinds==0
    and $delete_binds eq $delete_bind_extension_do_nothing
    and $repair_binds eq $repair_bind_extension_do_nothing
    and $delete_links eq $delete_link_extension_do_nothing
    and $repair_links eq $repair_link_extension_do_nothing
   ) {
   # Es muss eine Option angegeben werden, sonst hilfe
   $help=1;
}


# --help
if ($help==1) {
   # Scriptname ermitteln
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   # Befehlbeschreibung
   print "\n$scriptname repairs the permissions ",
         "of a sophomorix installation,\n",
         "reading the permissions from $repair_directories.\n\n";
   print('
Options
  -h  / --help
  -v  / --verbose
  -vv / --verbose --verbose
  -i
  -f filename / --file filename

What to repair (one option must be given):

  --permissions
      (repairs permission outside of $HOME)
      Limit the repair to a command-number (show the numbers with -i):
         --command-number number

  --htaccess
      (repairs .htacces files for users,classes and projects)

  --repairhome
      (repairs $HOME/* of students and teachers)
      Limit the repair to some users:
         -u user1,user2,... / --user user1,user2,...
         -c class1,class2,... / --class class1.class2,... (teachers can be used)
         -p project1,project2,... / --project project1,project2,...
         --students

  Repairing pointers (links, bind mounts, ...)

    Do also the bind mount:
      --always-bind

    Bind the bind mounts as quick as possible (--always-bind is automatically set).
    These options are used by sophomorix-bind.
      --quick --user username --repair-binds ""
      --quick --user username --delete-binds ""

    Remove the link/bind (Default extension is the empty string)
      --delete-links / --delete-links ".link-extension" 
      --delete-binds / --delete-binds ".bind-extension" 

    Repair (remove and recreate) the link/bind (Default extension is the empty string)
      --repair-links / --repair-links ".link-extension" 
      --repair-binds / --repair-binds ".bind-extension" 

    Combining Options is possible (but option can be given only once):
      --delete-links --create-binds          --> delete the links an create binds with the default extension
      --delete-links ".link" --repair-links  --> result is sort of a rename of links 

    Limit the deletion/repair to some users:
         -u user1,user2,... / --user user1,user2,...
         -c class1,class2,... / --class class1.class2,... (teachers can be used)
         -p project1,project2,... / --project project1,project2,...
         --students

Please see the sophomorix-repair(8) man pages for full documentation
');
   exit;
   #&log_script_exit("",1,1,0,@arguments);
}


if ($quick==1){
    # no logging
} else {
    &log_script_start(@arguments);
}


# --file
if ($filename ne "") {
   if (-e $filename) {
      print "\nRepariere Verzeichnisse aus $filename\n";
      $repair_directories="$filename";
   } else {
      print "\n   ABBRUCH - Nichtexistierende Datei: $filename\n\n";
      &log_script_exit("Nonexisting file $filename",1,1,0,@arguments);
    }
}


if ($quick==1){
    # no permissions needed
    &titel("Reading permission data ...");
    &get_alle_verzeichnis_rechte();
} else {
    # repair.directories einlesen
    &titel("Reading permission data ...");
    &get_alle_verzeichnis_rechte();
    # fetch permission for all homes
    &fetch_repairhome();
}

&check_verzeichnis_mkdir("${DevelConf::log_pfad_repair}");



###############################################################################
# Gruppenlisten/Userlisten erstellen
###############################################################################
my @klassen=();
my @klassen_sub=();
my @projects=();
my @raeume=();
my @schueler=();
my @lehrer=();
my @admins=();
my @workstations=();


if ($quick==1){
    # no userdata needed
} else {

# Klassen
&titel("Creating list of every AdminClass ...");
@klassen=&fetchadminclasses_from_school("showhidden"); 
&print_list("The list of every AdminClass:",@klassen);

# Subclasses
&titel("Creating list of every AdminClass with subclasses...");
@klassen_sub=&fetchsubclasses_from_school(); 
&print_list("The list of every AdminClass with subclasses:",@klassen_sub);

# Projects
&titel("Creating list of every Project ...");
@projects=&fetchprojects_from_school(); 
&print_list("The list of every Project:",@projects);

# Räume
&titel("Creating list of every room ...");
@raeume=&fetchrooms_from_school(); 
&print_list("The list of every room:",@raeume);



# Userlisten

# schueler
&titel("Creating list of every pupil ...");
#my @schueler=&get_pupils_school();
@schueler=&fetchstudents_from_school();
&print_list("The list of every pupil:",@schueler);

# lehrer
&titel("Creating list of every teacher ...");
#my @lehrer=&get_lehrer_in_schule();
@lehrer=&fetchstudents_from_adminclass(${DevelConf::teacher});
&print_list("The list of every teacher:",@lehrer);

# admins
&titel("Creating list of every administrator ...");
#my @lehrer=&get_lehrer_in_schule();
@admins=&fetchadministrators_from_school();
&print_list("The list of every administrator:",@admins);

# workstations
&titel("Creating list of every workstation ...");
#my @workstations=&get_workstations_in_schule();
#my @workstations=&get_workstations_school();
@workstations=&fetchworkstations_from_school();
&print_list("The list of every workstation:",@workstations);

}






# do something


# --permissions
if ($dateirechte==1 or $info==1) {
   &repair_directories();
}


# --htaccess
if ($htaccess==1){
   &repair_htaccess();
}


# --repairhome type
if ($repairhome==1){
    my @not_deleted=();
    # fetch fermission for all homes
    my $ref_repair=&fetch_repairhome();

    my @users=();
    # create userlist
    if ($login ne ""
        or $classes ne ""
        or $projects ne ""
        or $student==1
       ){
       &titel("Checking login names ...");
       @users=&create_userlist($login,$classes,1,$projects,1,$student,
                               0,0,0,1);
    } else {
       # teachers and students
       @users=(@lehrer,@schueler,@admins);
    }
    foreach my $user (@users){
        print "Repairing \$HOME of $user\n";
        &repair_repairhome($user);
        if ($user ne "administrator" and $user ne "pgmadmin"){
          my (@undeleted) = &delete_oldstuff_home($user);
          @not_deleted = (@not_deleted, @undeleted);
        } else {
	    print "* Not deleting old stuff of $user\n";
        }
    }

    # Print summary of undeleted files for all users
    my $not_deleted_count=$#not_deleted+1;
    print "\nSummary: $not_deleted_count dirs could not be deleted",
          " (dir not empty):\n";
    foreach my $item (@not_deleted){
        print "    $item\n";
    }
}


# Call manage_pointers when one of the following option has changed the $*do_nothing Variable:
#   --delete-links
#   --repair-links
#   --delete-binds
#   --repair-binds
if (
    $delete_binds ne $delete_bind_extension_do_nothing or
    $repair_binds ne $repair_bind_extension_do_nothing or
    $delete_links ne $delete_link_extension_do_nothing or
    $repair_links ne $repair_link_extension_do_nothing
   ){
    my @users=();
    # create userlist
    if ($login ne ""
        or $classes ne ""
        or $projects ne ""
        or $student==1
       ){
       if($Conf::log_level>=3){
           &titel("Checking login names ...");
       }
       @users=&create_userlist($login,$classes,1,$projects,1,$student,
                               0,0,0,1);
    } else {
       # teachers and students
       @users=(@lehrer,@schueler,@admins);
    }
    foreach my $user (@users){
        print "Managing pointers (bind mounts, links, ...) of $user\n";
        &manage_pointers($user);
    }
}


if ($quick==1){
    # no logging
} else {
    &log_script_end(@arguments);
}








###############################################################################
# Erzeugt für jede Zeile von repair.directories einen Befehl
###############################################################################


sub repair_htaccess {
  if ($DevelConf::create_www==1) {
     print "Repairing htaccess files of adminclasses:\n";
     foreach my $group (@klassen){
        print "   ${group}:\n";
        my ($access)=&fetchhtaccess_from_group($group);
        &set_htaccess_for_group($group,$access);
     }
     print "Repairing htaccess files of projects:\n";
     foreach my $group (@projects){
        print "   ${group}:\n";
        my ($access)=&fetchhtaccess_from_group($group);
        &set_htaccess_for_group($group,$access);
     }
     print "Repairing htaccess files of students:\n";
     foreach my $student (@schueler){
        print "   ${student}:\n";
        my ($access)=&fetchhtaccess_from_user($student);
        &set_htaccess_for_user($student,$access);
     }
     print "Repairing htaccess files of teachers:\n";
     foreach my $teacher (@lehrer){
        print "   ${teacher}:\n";
        my ($access)=&fetchhtaccess_from_user($teacher);
        &set_htaccess_for_user($teacher,$access);
     }
 } else {
     print "sophomorix is configured not to repair .htaccess files\n";
 }
}


sub set_htaccess_for_user {
    my ($user,$access) = @_;
    if ($access eq "user-public-upload"){
        &user_public_upload($user);
    } elsif ($access eq "user-private-upload"){
        &user_private_upload($user);
    } elsif ($access eq "user-public-noupload"){
        &user_public_noupload($user);
    } elsif ($access eq "user-private-noupload"){
        &user_private_noupload($user);
    } else {
        # standard
        my ($home,$type)=
          &Sophomorix::SophomorixPgLdap::fetchdata_from_account($user);
        if ($type eq "teacher"){
            &user_private_upload($user);
        } elsif ($type eq "student"){
            &user_private_noupload($user);
        } else {
            # no type 
            print "     * WARNING: .htacess NOT created!\n";
            print "     *           NO standard for type ${type}!\n";
        }
    }
}

sub set_htaccess_for_group {
    my ($group,$access) = @_;
    if ($access eq "group-public-upload"){
        &group_public_upload($group);
    } elsif ($access eq "group-private-upload"){
        &group_private_upload($group);
    } elsif ($access eq "group-public-noupload"){
        &group_public_noupload($group);
    } elsif ($access eq "group-private-noupload"){
        &group_private_noupload($group);
    } else {
        # standard
        &group_private_noupload($group);
    }
}


sub repair_directories {
   # Liste mit einem Element, dass die Schleifen EINMAL durchlaufen werden
   my @gruppen=("");
   my @user=("");
   my $num=0;
   if ($info==1){
      print "\nThe following commands can be selected with the option\n",
            "    --command-number number\n",
            "Otherwise all commands are executed (this takes time)\n\n";
   }
   open(DIRS, "<${repair_directories}")|| die "Fehler: $!";
   while (<DIRS>) {
       my $gruppen_anzahl=0;
       my $user_anzahl=0;
       chomp();   
       if ($_ eq ""){next;} # Wenn Zeile Leer, dann aussteigen
       if(/^\#/){next;} # Bei Kommentarzeichen aussteigen

       $num++;
       # replace the first occurence of the string $homedir... 
       # with the value of the variable $homedir...

       #s/\$homedir_admin/$DevelConf::homedir_admin/;
       s/\$var_lib_pfad/$DevelConf::var_lib_pfad/;
       s/\$log_files/$DevelConf::log_files/;
       s/\$apache_user/$DevelConf::apache_user/;
       s/\$apache_group/$DevelConf::apache_group/;
       s/\$homedir_all_admins/$DevelConf::homedir_all_admins/;
       s/\$homedir_pupil/${DevelConf::homedir_pupil}/;     
       s/\$homedir_teacher/$DevelConf::homedir_teacher/; 
       s/\$homedir_ws/$DevelConf::homedir_ws/; 
       s/\$attic/$DevelConf::attic/; 

       # order is important($homedir_samba must be replaced at last)    
       s/\$homedir_samba_cds/$DevelConf::homedir_samba_cds/;     
       s/\$homedir_samba_netlogon/$DevelConf::homedir_samba_netlogon/;     
       s/\$homedir_samba_progs/$DevelConf::homedir_samba_progs/;     
       s/\$homedir_samba/$DevelConf::homedir_samba/;     

       s/\$share_exams/$DevelConf::share_exams/;     
       s/\$share_teacher/$DevelConf::share_teacher/;     
       s/\$share_school/$DevelConf::share_school/;     


       s/\$share_share/$DevelConf::share_share/;     
       s/\$share_classes/$DevelConf::share_classes/;     
       s/\$share_subclasses/$DevelConf::share_subclasses/;     
       s/\$share_projects/$DevelConf::share_projects/;     

       s/\$tasks_tasks/$DevelConf::tasks_tasks/;
       s/\$tasks_teachers/$DevelConf::tasks_teachers/;     
       s/\$tasks_classes/$DevelConf::tasks_classes/;     
       s/\$tasks_subclasses/$DevelConf::tasks_subclasses/;     
       s/\$tasks_projects/$DevelConf::tasks_projects/;     
       s/\$tasks_rooms/$DevelConf::tasks_rooms/;     

       s/\$share_dir/$Language::share_dir/;     
       s/\$task_dir/$Language::task_dir/;     
       s/\$inspection_dir/$Language::inspection_dir/;     
       s/\$to_handoutcopy_dir/$Language::to_handoutcopy_dir/;     
       s/\$to_handoutcopy_string/$Language::to_handoutcopy_string/;     
       s/\$handout_dir/$Language::handout_dir/;     
       s/\$handout_string/$Language::handout_string/;     
       s/\$handoutcopy_dir/$Language::handoutcopy_dir/;     
       s/\$handoutcopy_string/$Language::handoutcopy_string/;     
       s/\$exam/$Language::exam/;     
       s/\$collect_dir/$Language::collect_dir/;     
       s/\$collected_dir/$Language::collected_dir/;     
       s/\$collected_string/$Language::collected_string/;     
       s/\$current_room/$Language::current_room/;     
       s/\$www_people/$DevelConf::www_people/;     
       s/\$www_classes/$DevelConf::www_classes/;     
       s/\$www_projects/$DevelConf::www_projects/;     
       s/\$www_teachers/$DevelConf::www_teachers/;     
       s/\$www_students/$DevelConf::www_students/;     
       s/\$user_attic/$Language::user_attic/;     
       
       # print only number of command when --info
       if ($info==1){
           print "Nr. $num: $_\n";
	   next;
       }

       # act only on specific lines
       if ($command_number!=0 and $command_number!=$num){
           next;    
       }

       # show what command is processed
       &titel("Command number ${num}:");
       print "$_\n";

       # Zeile zerlegen
       my ($path, $owner, $groupowner, $permission) = split(/::/);

       # Gruppen auswählen
       if (/\$klassen/) {
           # Liste ist Klassenliste
           @gruppen=@klassen;
           $gruppen_anzahl++;
           if($Conf::log_level>=3){
              print "\$klassen gefunden ",
                    "(Gruppenplatzhalter: $gruppen_anzahl)\n";
           }
       }elsif (/\$raeume/) {
           # Liste ist Raumliste
           @gruppen=@raeume;
           $gruppen_anzahl++;
           if($Conf::log_level>=3){
              print "\$raeume found (Group: $gruppen_anzahl)\n";
	   }
       }elsif (/\$projects/) {
           # Liste ist Projektliste
           @gruppen=@projects;
           $gruppen_anzahl++;
           if($Conf::log_level>=3){
              print "\$projects found (Group: $gruppen_anzahl)\n";
	   }
       }elsif (/\$subclasses/) {
           # Liste ist Subclassliste
           @gruppen=@klassen_sub;
           $gruppen_anzahl++;
           if($Conf::log_level>=3){
              print "\$subclasses found (Group: $gruppen_anzahl)\n";
	   }
       } else {
           if($Conf::log_level>=3){
              print "No \$klassen, \$raeume, $\projects or $\subclasses ",
                    "found (Group: $groupowner)\n";
           }
           @gruppen=($groupowner);
       }

       # Abbruch, falls verschiedene Gruppenplatzhalter
       if ($gruppen_anzahl > 1){
             &log_script_exit("ERROR: Gruppen-Platzhalter-Anzahl $gruppen_anzahl",
                              1,1,0,@arguments);
       }

       # User auswählen
       if ($path=~/\$schueler/) {
           if($Conf::log_level>=3){
              print "\$schueler found (Schülerliste)\n";
	   }
           # Liste ist Schülerliste
           @user=@schueler;
           $user_anzahl++;
           if($Conf::log_level>=3){
              print "\$schueler found (Userplatzhalter: $user_anzahl)\n";
	   }
       } elsif (/\$lehrer/) {
           # Liste ist Lehrerliste
           @user=@lehrer;
           $user_anzahl++;
           if($Conf::log_level>=3){
              print "\$lehrer found (Userplatzhalter: $user_anzahl)\n";
	   }
       }elsif (/\$workstation/) {
           # Liste ist Workstationliste
           @user=@workstations;
           $user_anzahl++;
           if($Conf::log_level>=3){
              print "\$workstation found (Userplatzhalter: $user_anzahl)\n";
	   }
       }elsif (/\$members/) {
           # Liste ist leer ($members alleine macht keinen Sinn)
           @user=();
           $user_anzahl++;
           if($Conf::log_level>=3){
              print "\$members found (Userplatzhalter: $user_anzahl)\n";
	   }
       } else {
           if($Conf::log_level>=3){
              print ("No \$schueler, \$lehrer \$workstations or \$members ");
              print ("found (Userplatzhalter: $owner)\n");
	   }
           @user=($owner);
       }

       # Abbruch, falls verschiedene Userplatzhalter
       if ($user_anzahl > 1){
             &log_script_exit("ERROR: User-Platzhalter-Anzahl:  $user_anzahl",
                              1,1,0,@arguments);
       }
         
       # Verzeichnis reparieren
       if($Conf::log_level>=3){
          print "   Aufruf von &repair_directory mit:\n";
          print "     Pfad:         $path\n";
          print "     Owner:        $owner\n";
          print "     Gruppe:       $groupowner\n";
          print "     Rechte:       $permission\n";
          print "     Gruppenliste: @gruppen\n";
          print "     Userliste:    @user\n";
       }
       &repair_directory($path, $owner, $groupowner, $permission, \@gruppen, \@user);
   }

   close(DIRS);
}



###############################################################################
# Reparieren eines bind mounts 
###############################################################################



sub delete_link {
    # delete the link!
    my ($link_target,$link_name) = @_;
    my $rm_command = "rm $link_name &> /dev/null";
    print "      $rm_command\n";
    system("$rm_command");
}


sub update_pointer_school {
    my ($login) = @_;
    my ($home)=&Sophomorix::SophomorixPgLdap::fetchdata_from_account($login);
    if ($home ne ""){
       # remember and remove immutable bit on parent dir
       my $parent_dir = $home."/${Language::share_dir}";
       my $immutable_bit=&fetch_immutable_bit($parent_dir);
       &set_immutable_bit($parent_dir,0);

       my $basename_pointer=$home.
        "/${Language::share_dir}/${Language::share_string}".
        "${Language::school}";
       my $new_dir_delete=$basename_pointer.$delete_bind_extension;
       my $new_dir_repair=$basename_pointer.$repair_bind_extension;
       my $link_name_delete=$basename_pointer.$delete_link_extension;
       my $link_name_repair=$basename_pointer.$repair_link_extension;

       my $old_dir=$DevelConf::share_school;

       if($Conf::log_level>=2){
           print "   Link name (school): $new_dir_delete\n";
           print "   Link name (school): $new_dir_repair\n";
           print "   Newdir (school): $link_name_delete\n";
           print "   Newdir (school): $link_name_repair\n";
           print "   Target    (school): $old_dir\n";
       }

       ##################################################
       # delete and recreate bindmounts/links to school
       ##################################################
       # --delete-links
       if ($delete_link_extension ne $delete_link_extension_do_nothing){
           &delete_link($old_dir,$link_name_delete);
       }

       # --delete-binds
       if ($delete_bind_extension ne $delete_bind_extension_do_nothing){
           &delete_bind($old_dir,$new_dir_delete);
       }

       # --repair-links
       if ($repair_link_extension ne $repair_link_extension_do_nothing){
           &delete_link($old_dir,$link_name_repair);
           &delete_bind($old_dir,$link_name_repair);
           &create_symlink($old_dir,$link_name_repair);
       }

       # --repair-binds
       if ($repair_bind_extension ne $repair_bind_extension_do_nothing){
           # --quick
           if ($quick==1){
               &delete_link($old_dir,$new_dir_repair);
               &delete_bind($old_dir,$new_dir_repair);
               #&create_bind($old_dir,$new_dir_repair,"BINDONLY","ALWAYSBIND");
               &create_bind($old_dir,$new_dir_repair,"","ALWAYSBIND");
           } else {
               &delete_link($old_dir,$new_dir_repair);
               &delete_bind($old_dir,$new_dir_repair);
               if ($always_bind==0){
                   &create_bind($old_dir,$new_dir_repair,"","");
               } else {
                   # do also the bind
                   &create_bind($old_dir,$new_dir_repair,"","ALWAYSBIND");
               }
           }
       }
#       symlink $old_dir, $link_name;
       &set_immutable_bit($parent_dir,$immutable_bit);
    }
}




sub update_pointer {
    my ($login,$share_name,$share_long_name,$type) = @_;
    my $old_dir="";   # was $link_target 
    my $old_dir_tasks=""; # was $link_target_tasks
    my ($homedir,$pri_group)=
        &Sophomorix::SophomorixPgLdap::fetchdata_from_account($login);

    # replace teachers with language term
    if ($share_name  eq ${DevelConf::teacher}){
        $share_long_name=${Language::teacher};     
    }

    # use shortname as longname if not given
    if (not defined $share_long_name){
        $share_long_name=$share_name;
    }

    # project is standard
    if (not defined $type or $type eq ""){
	$type="project";
    }

    # Only act if uid is valid
    if ($homedir ne ""){
       # save immutable bit on share_dir and unset it
       my $immutable_share_dir=$homedir."/${Language::share_dir}";
       my $immutable_share_bit=&fetch_immutable_bit($immutable_share_dir);
       &set_immutable_bit($immutable_share_dir,0);

       # save immutable bit on task_dir and unset it
       my $immutable_task_dir=$homedir."/${Language::task_dir}";
       my $immutable_task_bit=&fetch_immutable_bit($immutable_task_dir);
       &set_immutable_bit($immutable_task_dir,0);


       # basenames without extensions
       my $basename_pointer=$homedir.
          "/${Language::share_dir}/${Language::share_string}".
          "${share_long_name}";

       my $basename_pointer_tasks=$homedir.
          "/${Language::task_dir}/${Language::task_string}".
          "${share_long_name}";

       my $new_dir_delete=$basename_pointer.$delete_bind_extension;
       my $new_dir_repair=$basename_pointer.$repair_bind_extension;
       my $new_dir_delete_tasks=$basename_pointer_tasks.$delete_bind_extension;
       my $new_dir_repair_tasks=$basename_pointer_tasks.$repair_bind_extension;

       my $link_name_delete=$basename_pointer.$delete_link_extension;
       my $link_name_repair=$basename_pointer.$repair_link_extension;
       my $link_name_delete_tasks=$basename_pointer_tasks.$delete_link_extension;
       my $link_name_repair_tasks=$basename_pointer_tasks.$repair_link_extension;


       # $new_dir for 
#       my $new_dir=$homedir.
#          "/${Language::share_dir}/${Language::share_string}".
#          "${share_long_name}"."$bind_extension";   
#
#       my $new_dir_tasks=$homedir.
#          "/${Language::task_dir}/${Language::task_string}".
#          "${share_long_name}"."$bind_extension";



       #print "\nHERE: type is $type\n\n";
       # decide path names according to user type
       if ($type eq "project"){
           # project
           $old_dir="${DevelConf::share_projects}/${share_name}";
           $old_dir_tasks="${DevelConf::tasks_projects}/${share_name}";
       } elsif ($type eq "adminclass" or $type eq "hiddenclass"){
           # class
           $old_dir="${DevelConf::share_classes}/${share_name}";
           $old_dir_tasks="${DevelConf::tasks_classes}/${share_name}";
       } elsif ($type eq "teacher"){
           # teacher
           $old_dir="${DevelConf::share_teacher}";
           $old_dir_tasks="${DevelConf::tasks_teachers}";
       } elsif ($type eq "subclass"){
           # subclass
           $old_dir="${DevelConf::share_subclasses}/${share_name}";
           $old_dir_tasks="${DevelConf::tasks_subclasses}/${share_name}";
       } elsif ($type eq "room"){
           # room
           $old_dir_tasks="${DevelConf::tasks_rooms}/${share_name}";
       } else {
           print "Unknown type $type\n\n";
	   return 0;
       }

       ##################################################
       # Link to share (all but workstations)
       ##################################################
       if ($type ne "room"){
           if($Conf::log_level>=2){
               print "   Newdir  (share): $new_dir_delete\n";
               print "   Newdir  (share): $new_dir_repair\n";
               print "   Olddir  (share): $old_dir\n";
           }
           if (-d $old_dir){
               if($Conf::log_level>=2){
                   print "   Creating bind mount for user $login ",
                         "to $type ${old_dir}.\n";
               }

               ##################################################
               # delete and recreate bindmounts/links
               ##################################################
               # --delete-links
               if ($delete_link_extension ne $delete_link_extension_do_nothing){
                   &delete_link($old_dir,$link_name_delete);
               }

               # --delete-binds
               if ($delete_bind_extension ne $delete_bind_extension_do_nothing){
                   &delete_bind($old_dir,$new_dir_delete);
               }

               # --repair-links
               if ($repair_link_extension ne $repair_link_extension_do_nothing){
                   &delete_link($old_dir,$link_name_repair);
                   &delete_bind($old_dir,$link_name_repair);
                   &create_symlink($old_dir,$link_name_repair);
               }

               # --repair-binds
               if ($repair_bind_extension ne $repair_bind_extension_do_nothing){
                   # --quick
                   if ($quick==1){
                       &delete_link($old_dir,$new_dir_repair);
                       &delete_bind($old_dir,$new_dir_repair);
                       #&create_bind($old_dir,$new_dir_repair,"BINDONLY","ALWAYSBIND");
                       &create_bind($old_dir,$new_dir_repair,"","ALWAYSBIND");
                   } else {
                       &delete_link($old_dir,$new_dir_repair);
                       &delete_bind($old_dir,$new_dir_repair);
                       if ($always_bind==0){
                           &create_bind($old_dir,$new_dir_repair,"","");
                       } else {
                           # do also the bind
                           &create_bind($old_dir,$new_dir_repair,"","ALWAYSBIND");
                       }
                   }
               }
           } else {
               print "   NOT creating bind mount to ",
                     "nonexisting/nondirectory $old_dir\n";
           }
       }


       ##################################################
       # Link to tasks (all users)
       ##################################################
       if($Conf::log_level>=2){
           print "   Newdir  (tasks): $new_dir_delete_tasks\n";
           print "   Newdir  (tasks): $new_dir_repair_tasks\n";
           print "   Olddir  (tasks): $old_dir_tasks\n";
       }
       if ($type eq "room"){
          # create the share_dir on the fly
          &setup_verzeichnis("\$tasks_rooms/\$raeume",
                             "$old_dir_tasks");
       }

       if (-d $old_dir_tasks){
           if($Conf::log_level>=2){
               print "   Creating bind mont for user $login ",
                     "to $type ${old_dir_tasks}.\n";
           }

           ##################################################
           # delete and recreate bindmounts/links
           ##################################################
           # --delete-links
           if ($delete_link_extension ne $delete_link_extension_do_nothing){
               &delete_link($old_dir_tasks,$link_name_delete_tasks);
           }

           # --delete-binds
           if ($delete_bind_extension ne $delete_bind_extension_do_nothing){
               &delete_bind($old_dir_tasks,$new_dir_delete_tasks);
           }

           # --repair-links
           if ($repair_link_extension ne $repair_link_extension_do_nothing){
               &delete_link($old_dir_tasks,$link_name_repair_tasks);
               &delete_bind($old_dir_tasks,$link_name_repair_tasks);
               &create_symlink($old_dir_tasks,$link_name_repair_tasks);
           }

           # --repair-binds
           if ($repair_bind_extension ne $repair_bind_extension_do_nothing){
               # --quick
               if ($quick==1){
                   &delete_link($old_dir,$new_dir_repair_tasks);
                   &delete_bind($old_dir,$new_dir_repair_tasks);
                   &create_bind($old_dir_tasks,$new_dir_repair_tasks,"","ALWAYSBIND");
               } else {
                   &delete_link($old_dir,$new_dir_repair_tasks);
                   &delete_bind($old_dir,$new_dir_repair_tasks);
                   if ($always_bind==0){
                       &create_bind($old_dir_tasks,$new_dir_repair_tasks,"","");
                   } else {
                       # do also the bind
                       &create_bind($old_dir,$new_dir_repair_tasks,"","ALWAYSBIND");
                   }
               }
           }

           # delete an recreate bind mounts  ?????????????????
           #&SophomorixBase::delete_bind($old_dir_tasks,$new_dir_tasks);
           #&create_bind($old_dir_tasks,$new_dir_tasks,"","");
       } else {
           print "   NOT creating bind mount to ",
                 "nonexisting/nondirectory $old_dir_tasks\n";
       }
       # restore immutable bit
       &set_immutable_bit($immutable_share_dir,$immutable_share_bit);
       &set_immutable_bit($immutable_task_dir,$immutable_task_bit);
    } else {
        print "   NOT removing directories: ",
              "Home of user $login not known.\n";
    }
}




sub manage_pointers {
    my ($login)= @_;
    if($Conf::log_level>=2){
        print "  * Starting manage_pointers:\n";
        printf "      Delete bind extension: %7s ".
               " ($delete_bind_extension_do_nothing does nothing)\n",
               $delete_bind_extension;
        printf "      Repair bind extension: %7s ".
               " ($repair_bind_extension_do_nothing does nothing)\n",
               $repair_bind_extension;
        printf "      Delete link extension: %7s ".
               " ($delete_link_extension_do_nothing does nothing)\n",
               $delete_link_extension;
        printf "      Repair link extension: %7s ".
               " ($repair_link_extension_do_nothing does nothing)\n",
               $repair_link_extension;
    }
    my ($home,$type) = &fetchdata_from_account($login);
    my $share_dir=$home."/".$Language::share_dir;
    my $exists_string="Exists";
    my %mandatory_binds=();


    # repairing all pointers
    if ($login eq "administrator" or $login eq "pgmadmin" or $type eq "teacher"){
        # removing _schueler if it exists
        my $target_1=$home."/$Language::inspection_dir";
        my $source_1="/home/students";
        &delete_link($source_1,$target_1);
        &delete_bind($source_1,$target_1);
    }

    # administrators    
    if ($login eq "administrator" or $login eq "pgmadmin"){
        print "Creating admin pointers\n";
        my $target_2=$home."/$Language::share_dir";
        my $source_2="/home/share";
        &delete_link($source_2,$target_2);
        &delete_bind($source_2,$target_2);
        if ($repair_bind_extension ne $repair_bind_extension_do_nothing){
            &create_bind($source_2,$target_2,"","ALWAYSBIND");
        }
        if ($login eq "administrator"){
            my $target_3=$home."/registry-patches";
            my $source_3="/usr/share/linuxmuster/registry-patches";
            &delete_link($source_3,$target_3);
            &delete_bind($source_3,$target_3);
            if ($repair_bind_extension ne $repair_bind_extension_do_nothing){
                &create_bind($source_3,$target_3,"","ALWAYSBIND");
            }
        }
        return;
    }

    my @list=&Sophomorix::SophomorixPgLdap::pg_get_group_list($login);
    my $count=1;
    foreach my $group (@list){
        my ($group_type)=&pg_get_group_type($group);
        # update links,binds, ... (user, workstations)
        print " $count)  $group ($group_type):\n";
        &update_pointer($login,$group,$group,$group_type);
        $mandatory_binds{$group}="mandatory";
        $count++;
    }

    my ($group_type)=&pg_get_group_type($list[0]);
    if ($group_type eq "room"){
        # no bind mounts for workstation accounts
        print " No pointer to School-share because $list[0] is a room\n";
    } else {
        # school dir
        print " $count)  School-share:\n";
        &update_pointer_school($login);
    }

    if ($quick==0){
        print "-------------------------------------------------------------------\n";
        print "Checking binds of $login in $share_dir\n";
        print "-------------------------------------------------------------------\n";
        if (not -e $share_dir){
            &log_script_exit("Nonexisting dir $share_dir",1,1,0,@arguments);
        }
        opendir DIR, $share_dir or die "Cannot open $share_dir: $!";
        my @files=();
        foreach my $file (readdir DIR) {
            push @files, $file;
        }
        @files = sort @files;

        $count=1;
        foreach my $file (@files) {
            if ($file eq "." or $file eq ".."){
	        next;
            }

            my $group = $file;
            # remove string, so that the group is shown
            $group =~ s/$Language::share_string//;

            #
            # check here if it is a dir and mounted correctly ??????????????
            #
            if (exists $mandatory_binds{$group}){
                print "   $count) $file ($group)\n";
            } else {
                # ???????????? avoid ? when tausch-Lehrer and tausch-Schule
                print "?  $count) $file ($group)\n";
            }

            $count++;
        }
        closedir DIR;
    }
    return;
}

###############################################################################
# Reparieren eines Verzeichnisses 
###############################################################################

sub repair_directory {
   # Übergabewerte:
   # pfad(string) mit variablen $gruppe und $user
   # owner(name)
   # gruppenowner(name)
   # Dateirechte(octal, z.B. 0775)
   # Referenz auf gruppen (evtl. auch leere liste)
   # Referenz auf user (evtl. auch leere liste)

   # Übergabewerte zuweisen
   my ($path, $owner, $groupowner, $permission, $gruppen, $user) = @_;

   my $korrigieren=1;
   my $permission_to_set="";
   # Allgemeine Ersetzungen
   # $webserver
   $path=~s/\/\$webserver/$DevelConf::apache_root/;
   # $apache_user
   $owner=~s/\$apache_user/$DevelConf::apache_user/;
   # $samba
   my @userliste=();

   if($Conf::log_level>=3){
      print "Pfad mit Ersetzungen: $path\n";
   }

   foreach my $gruppe (@$gruppen){
         if($Conf::log_level>=3){
            print "***** Gruppe ist $gruppe *****\n";
         }
         # Pfad sichern
         my $save_path=$path;
         # Owner sichern
         my $save_owner=$owner; 

         # Group-owner sichern
         my $save_groupowner=$groupowner; 

         # Userliste evtl. einschränken
	 if ($path=~/\$klassen/ && $path=~/\$schueler/) {
            @userliste=&fetchstudents_from_adminclass($gruppe);
            #print "Userliste: Nur Klasse\n";
	 } elsif ($path=~/\$projects/ && $path=~/\$members/) {
            @userliste=&fetchusers_from_project($gruppe);
            #print "Userliste: Nur Workstations\n";
	 } elsif ($path=~/\$raeume/ && $path=~/\$workstation/) {
            @userliste=&fetchworkstations_from_room($gruppe);
            #print "Userliste: Nur Workstations\n";
	 } else {
            my $zahl=@$user;
            if (not $zahl==0) {
               @userliste=@$user;
               #print "Userliste: Alle ($zahl)\n";
	    } else {
               @userliste=("");
               print "Userliste: User ohne Namen ($zahl User)\n";
	    }
	 }

         # Ersetzungen vornehmen von Gruppen
         # Bsp.: String $klassen mit dem Wert von $gruppe ersetzen
         $path=~s/\$klassen/$gruppe/g; 
         $path=~s/\$raeume/$gruppe/g; 
         $path=~s/\$projects/$gruppe/g; 
         $path=~s/\$subclasses/$gruppe/g; 

         $groupowner=~s/\$klassen/$gruppe/g; 
         $groupowner=~s/\$raeume/$gruppe/g;
         $groupowner=~s/\$projects/$gruppe/g;
         $groupowner=~s/\$subclasses/$gruppe/g;

      foreach $user (@userliste){
         if($Conf::log_level>=3){
            print "User ist $user\n";
         }
         # Pfad sichern
         my $save_path=$path;         
         # Owner sichern
         my $save_owner=$owner;
         
         # Grou-owner sichern
         my $save_groupowner=$groupowner;
         
         my $ownerid=0;
         my $gid=0;

         # Ersetzungen vornehmen von Usern
         $path=~s/\$schueler/$user/g; 
         $path=~s/\$lehrer/$user/g; 
         $path=~s/\$workstation/$user/g; 
         $path=~s/\$members/$user/g; 

         $owner=~s/\$schueler/$user/g; 
         $owner=~s/\$lehrer/$user/g;
         $owner=~s/\$workstation/$user/g; 
         $owner=~s/\$members/$user/g; 

         # use database
         ($a,$a,$a,$a,$ownerid) = 
            &Sophomorix::SophomorixPgLdap::fetchdata_from_account($owner);
         if ($ownerid==-1) {
             # use ldap
	     if (not defined($ownerid=getpwnam $owner)){
                 print "WARNING: Benutzer $owner gibt es nicht!\n";
                 next;
	     }
         }

         if (not   defined ($gid = getgrnam $groupowner)) {
            print "WARNING: Gruppe $groupowner gibt es nicht!\n";
            next;
         }

         # Anlegen, falls nicht vorhanden
         # folgendes nicht mehr verwenden, da rechte unötigerweise verändert werden
         #system("install -d $path");
 
         if (not -e $path) {
            # anlegen
            mkdir $path;
            #mkdir $path, oct($permission);
         }
         # # Mehrere mit / getrente Permissions angegeben -> Liste
         if ($permission =~m/\//) {
             my @permission_list = split(/\//,$permission);
             # Dateirechte des Verzeichnises ermitteln
             my ($a,$b,$mode) = stat(${path});
             #print "Mode ist $mode\n";
             # Umwandeln in übliche Schreibweise
             $mode &=07777;
             $mode=sprintf "%04o",$mode;

             if($Conf::log_level>=2){
                print "Permissions are $mode  (Permitted: $permission)\n";
	     }
             # Falls Verzeichnisrechte gesetzt werden müssen, 
             # auf den ersten Wert setzten 
             $permission_to_set=$permission_list[0];
             # Sind die Verzeichnisrechte OK
	     foreach my $perm (@permission_list){
               if ($mode==$perm) {
               #print "VerzeichnisrechteOK\n";
               # Verzeichnisrechte wurden in der Liste der zulässigen gefunden
               # -> nicht mehr korrigieren
               $korrigieren=0;
               }
             }
         } else {
           $permission_to_set=$permission;
         }

         # Bei Bedarf Verzeichnisrechte korrigieren
         if ($korrigieren==1) {
            chmod oct($permission_to_set), $path;
            chown $ownerid, $gid, $path;
            print "  * $path $owner($ownerid) ",
                  "$groupowner($gid) $permission_to_set\n";
         } else {
            # Verzeichnisrechte nicht korrigieren
            chown $ownerid, $gid, $path;
            print "  * $path $owner($ownerid) ",
                  "$groupowner($gid) Rechte: OK\n";
            # Korrigier-Variable zurücksetzten
            $korrigieren=1;
	 }

         # Pfad wiederherstellen
         $path=$save_path;
         $owner=$save_owner;
         $groupowner=$save_groupowner;
      }

    # Pfad wiederherstellen
    $path=$save_path;
    $owner=$save_owner;
    $groupowner=$save_groupowner;
    }

}



