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

# modules
use strict;
use Getopt::Long;
Getopt::Long::Configure ("bundling");
use Sophomorix::SophomorixConfig;
use Net::LDAP;
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Useqq = 1;
$Data::Dumper::Terse = 1; 
use JSON;
use Sophomorix::SophomorixBase qw(
                                 analyze_smbcquotas_out
                                 );
use Sophomorix::SophomorixSambaAD qw(
                                 AD_bind_admin
                                 AD_unbind_admin
                                    );

############################################################
# parsing options
############################################################
my %options=();
$Conf::log_level=1; # remove later
my $testopt=GetOptions(\%options, 
                        "help|h",
                        "json|j+",
                        "json-path=s",
                        "verbose|v+",
                        "sam|sAMAccountName=s",
                        "sn=s",
                        "gn|givenName=s",
                        "anyname=s",
                        "custom=s",
                        "student",
                        "parent",
                        "staff",
                        "teacher",
                        "examuser",
                        "globaladministrator",
                        "schooladministrator",
                        "globalbinduser",
                        "schoolbinduser",
                        "addc",
                        "classroom-studentcomputer|csc",
                        "classroom-teachercomputer|ctc",
                        "faculty-teachercomputer|ftc",
                        "mobile",
                        "printer",
                        "server",
                        "staffcomputer|sc",
                        "adminclass",
                        "extraclass",
                        "teacherclass",
                        "class",
                        "project",
                        "sophomorix-group",
                        "room",
                        "printergroup",
                        "school-list",
                        "schoolbase=s",
                        "user-minimal",
                        "user-basic",
                        "user-full",
                        "quota-usage",
                        "group-minimal",
                        "group-basic",
                        "group-full",
                        "group-members",
                        "comp-minimal",
                        "comp-basic",
                        "comp-full",
                        "status=s",
                        "smbstatus",
                        "query-ip=s",
                        "query-comp=s",
                        "query-user=s",
                        "query-room=s"
    );



if ($testopt==0){
    print "Option Error\n";
    exit 88;
}



# option calculations
if (defined $options{'class'}){
    # find the group types
    $options{'adminclass'}=1;
    $options{'extraclass'}=1;
    $options{'teacherclass'}=1;
}



# --help
if (defined $options{'help'}) {
   # Scriptname ermitteln
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   # Befehlsbeschreibung
   print('
sophomorix-query is a performance optimised was to query AD for users, computers and groups.

Searches are case insensitive:
  --sam GasT      will find Gast,GAST, .....

If you use the following wildcardcharacters, put them in quotation marks:
  --sam "g*"       finds all sAMAccountName that beginn with g

Options
  -h  / --help
  -v  / --verbose
  show the search filter, searchbase 

  -i  / --info

  -j         pretty json output
  -jj        one line json output
  -jjj       perl dump
  -jjjj      pretty json output to /tmp/output.json
  -jjjjj     one line json output to /tmp/output.json

  --json-path /path/tpo/json/output/file

Search for schools:
--school-list

Search attribute(s) to find a user/group/computer:
(multiple options are connected with & to narrow down the search)
--sam|sAMAccountName <string> 
--sn <string>
--gn|givenName  <string>
OR
--anyname <string>      (All important name fields: sAMAccountName,sn,givenName, ...)
OR
--custom <string>       (The custom fields)

Limit the query for sophomorix users to a certain sophomorixRole:
(multiple options are connected with | to accumulate the results)
--student                       (sophomorixRole=student)
--parent                        (sophomorixRole=parent)
--staff                         (sophomorixRole=staff)
--teacher                       (sophomorixRole=teacher)
--examuser                      (sophomorixRole=examuser)
--globaladministrator           (sophomorixRole=globaladministrator)
--schooladministrator           (sophomorixRole=schooladministrator)
--globalbinduser                (sophomorixRole=globalbinduser)
--schoolbinduser                (sophomorixRole=schoolbinduser)

Limit the query for sophomorix computers to a certain sophomorixRole:
(multiple options are connected with | to accumulate the results)
--addc                          (sophomorixRole=addc)
--classroom-studentcomputer|csc (sophomorixRole=classroom-studentcomputer)
--classroom-teachercomputer|ctc (sophomorixRole=classroom-teachercomputer)
--faculty-teachercomputer|ftc   (sophomorixRole=faculty-teachercomputer)
--mobile                        (sophomorixRole=mobile)
--printer                       (sophomorixRole=printer)
--server                        (sophomorixRole=server)
--staffcomputer|sc              (sophomorixRole=staffcomputer)

Limit the query for sophomorix groups to a certain sophomorixType:
(multiple options are connected with | to accumulate the results)
--adminclass          (sophomorixType=adminclass)
--extraclass          (sophomorixType=extraclass)
--teacherclass        (sophomorixType=teacherclass)
--class               (| (sophomorixType=adminclass)(sophomorixType=extraclass)(sophomorixType=teacherclass) )
--project             (sophomorixType=project)
--sophomorix-group    (sophomorixType=sophomorix-group)
--room                (sophomorixType=room)
--printergroup        (sophomorixType=printer)

Query room data with smbstatus command:
--smbstatus                       (show all available room data, for debugging)
--smbstatus --query-ip <ip>       (show room that contains computer with <ip>)
--smbstatus --query-comp <comp>   (show room that contains computer <comp>)
--smbstatus --query-user <user>   (show room where <user> is logged in)
--smbstatus --query-room <room>   (show room <room>)

Limit the query to one or more sophomorixStatus:
--status T            (sophomorixStatus=T)
--status T,D          (| (sophomorixStatus=T)(sophomorixStatus=D) )

Performance improvements:

Limit the searchbase (this is faster):
--schoolbase <school> (limit the searchbase to one school or global)
  # maybe later:
  --sub-projectbase (only works together with schoolbase)

What user attributes to return:
--user-minimal              (i. e. for dropdown menues)
--user-basic                (default)
--user-full                 (all attributes)
--user-full --quota-usage   (all attributes and smb share quota usage/softlimit/hardlimit)

What group attributes to return:
--group-minimal      (i. e. for dropdown menues)
--group-basic        (default)
--group-full         (all attributes)
--group-members      (add members if object is a group)

What computer attributes to return:
--comp-minimal    (i. e. for dropdown menues)
--comp-basic      (default)
--comp-full       (all useful attributes)

Examples:
  Search for all objects where the sAMAccountName begins with the letter j:
  sophomorix-query --sam "j*"

  Search for all adminclasses and extraclassses of a school for dropdown menue:
  sophomorix-query --schoolbase <school> --extraclass --adminclass --group-minimal

  Search for all users (with data for listing) in a group with the name <7a>:
  sophomorix-query --schoolbase <school> --sam 7a --class --group-minimal --group-members --user-full

  Search for printers (with data for listing):
  sophomorix-query --schoolbase <school> --printer --comp-full

 Search for printergroups (with data and members for listing):
  sophomorix-query --schoolbase <school> --printergroup --group-full --group-members --sam <printername>

 Search for all users in the same room as logged in user <user>:
  sophomorix-query --smbstatus --query-user <user>

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


if (not defined $options{'json'}){
    # always use json output
    $options{'json'}=0;
}



my ($ldap,$root_dse) = &AD_bind_admin();
my $root_dns="";
if (defined $options{'quota-usage'}){
    $root_dns=&get_root_dns();
    use Math::Round;
}
############################################################

my $single_attrs;
my $multi_attrs;
my $query_attrs;

my $objectclass_filter="";

my $top_key;
my $top_count_key;

my $attr_filter="";
my $role_filter="";
my $type_filter="";
my $status_filter="";

# the search parameters
my $filter;
my $base;
my $scope="sub";
my $search_object="all"; # all is the default if nothing is specified

# json_output_path for -jjjj and -jjjjj
my $json_output_path="/tmp";

if (defined $options{'json-path'}){
    $json_output_path=$options{'json-path'};
}

# the search results, displayed as JSON object
my %search=();
$search{'COUNTERS'}{'ALL'}=0;
$search{'COUNTERS'}{'GROUP'}=0;
$search{'COUNTERS'}{'ROOM'}=0;
$search{'COUNTERS'}{'USER'}=0;
$search{'COUNTERS'}{'COMPUTER'}=0;
$search{'COUNTERS'}{'SCHOOL'}=0;

my $max;
my $mesg;


############################################################
# search for schools
############################################################
if (defined $options{'school-list'}){
    $base="OU=SCHOOLS,".$root_dse;
    $scope="one";
    $filter="(name=*)";
    if (defined $options{'verbose'} and $options{'verbose'}==1){
        print "\n";
        print "Search parameters:\n";
        print "  BASE:       $base\n";
        print "  SCOPE:      $scope\n";
        print "  FILTER:     $filter\n";
	print "\n";
    }
    $mesg = $ldap->search(
                   base   => $base,
                   scope => $scope,
                   filter => $filter,
                  # attrs => $query_attrs, # query all attributes
	);
    $max = $mesg->count;
    for( my $index = 0 ; $index < $max ; $index++) {
        my $entry = $mesg->entry($index);
        my $dn = $entry->dn();
        my $name = $entry->get_value('name');
        if ($name eq "SCHOOLS"){
            next;
        }
	push @{ $search{'LISTS'}{'SCHOOL'} }, $name;
	$search{'SCHOOL'}{$name}{'name'}=$name;
	$search{'SCHOOL'}{$name}{'DN'}=$dn;
    }
    @{ $search{'LISTS'}{'SCHOOL'} } = sort @{ $search{'LISTS'}{'SCHOOL'} };
    $search{'COUNTERS'}{'SCHOOL'}=$#{ $search{'LISTS'}{'SCHOOL'} }+1;
    #print Dumper(\%search);

    # print JSON Object
    &json_printout($options{'json'},\%search);

    # disconnect
    &AD_unbind_admin($ldap);
    exit;
}


############################################################
# set the search base
############################################################
if (defined $options{'schoolbase'}){
    if ($options{'schoolbase'} eq "global"){
        $base="OU=GLOBAL,".$root_dse;
    } else {
        $base="OU=".$options{'schoolbase'}.",OU=SCHOOLS,".$root_dse;
    }
} else {
    $base=$root_dse;
}



# --smbstatus
if (defined $options{'smbstatus'}){
    my %smbstatus=();
    my %lookup=();
    my $string=`/usr/bin/smbstatus`;
    #my $string=`cat /root/log1`;
    my @lines=split("\n",$string);
    #print "$string";
    # first loop: get computers
    foreach my $entry (@lines){
        my ($pid)=split(/ /,$entry);
        if (not defined $pid){
            # print "$entry\n";
        }
        if (not defined $pid){
            #print "IGNORED(pid not defined): <$entry>\n";
        } elsif ($pid=~/[0-9]/){
            if ($entry=~m/\\domain computers /){

               # extract computer name from line
               $entry=~m/\\(.*)\$\s+/;
               my $computer=$1;

               # extract ip from line
               $entry=~m/\\domain computers\s+([0-9.].*)\s+\(/;
               my $ip=$1;

               # extract ipv4 from line
               $entry=~m/ipv4:([0-9.].*)\)/;
               my $ipv4_port=$1;
               my ($ipv4,$port)=split(/:/,$ipv4_port);

               # Save Data
               $smbstatus{'COMPUTER'}{$computer}{'SMBSTATUS_LINE'}=$entry;
               $smbstatus{'COMPUTER'}{$computer}{'PID'}=$pid;
               $smbstatus{'COMPUTER'}{$computer}{'IP'}=$ip;
               $smbstatus{'COMPUTER'}{$computer}{'IPV4'}=$ipv4;
               $smbstatus{'COMPUTER'}{$computer}{'IPV4_PORT'}=$port;
         
               $smbstatus{'LOOKUP'}{'computer_by_ip'}{$ip}=$computer;
            } elsif ($entry=~m/ users /){
               # do nothing with users
            }
        } else {
            #print "IGNORED: >$pid< $entry\n";
        }
    }

    my ($ref_AD_rooms) = &AD_get_rooms({ldap=>$ldap,
                                        root_dse=>$root_dse,
                                        root_dns=>$root_dns,
                                      });

    # second loop: get users
    foreach my $entry (@lines){
        my ($pid)=split(/ /,$entry);
        if (not defined $pid){
            #print "$entry\n";
        }
        if (not defined $pid){
            #print "IGNORED(pid not defined): <$entry>\n";
        } elsif ($pid=~/[0-9]/){
            if ($entry=~m/\\domain computers /){
                # do nothing with computers
            } elsif ($entry=~m/ users /){
               
               # extract user name from line
               $entry=~m/\\(.*)\s+users\s+/;
               my $user=$1;
               $user=~s/\s+$//g;# remove trailing whitespace

               # extract ip from line
               $entry=~m/\s+users\s+([0-9.].*)\s+\(/;
               my $ip=$1;

               # extract ipv4 from line
               $entry=~m/ipv4:([0-9.].*)\)/;
               my $ipv4_port=$1;
               my ($ipv4,$port)=split(/:/,$ipv4_port);

               # Save Data
               $smbstatus{'USER'}{$user}{'SMBSTATUS_LINE'}=$entry;
               $smbstatus{'USER'}{$user}{'IP'}=$ip;
               $smbstatus{'USER'}{$user}{'IPV4'}=$ipv4;
               $smbstatus{'USER'}{$user}{'PORT'}=$port;

               # add computer name from LOOKUP
               if (defined $smbstatus{'LOOKUP'}{'computer_by_ip'}{$ip}){
                   $smbstatus{'USER'}{$user}{'SMBSTATUS_COMPUTER_LINE'}="TRUE";
                   $smbstatus{'USER'}{$user}{'COMPUTER'}=$smbstatus{'LOOKUP'}{'computer_by_ip'}{$ip};
                   $smbstatus{'COMPUTER'}{$smbstatus{'LOOKUP'}{'computer_by_ip'}{$ip}}{'USER'}=$user;
               } else {
                   $smbstatus{'USER'}{$user}{'SMBSTATUS_COMPUTER_LINE'}="NONE";
		   my $computer=$ref_AD_rooms->{'LOOKUP'}{$ip};
		   $smbstatus{'USER'}{$user}{'COMPUTER'}=$computer;
                   $smbstatus{'WARNINGS'}{$ip}="Could not find computer entry for ip $ip used by $user";
               }
            }
        } else {
            #print "IGNORED: >$pid< $entry\n";
        }
    }


    #&json_printout($options{'json'},$ref_AD_rooms);

    foreach my $room (keys  %{ $ref_AD_rooms->{'room'} }){
        foreach my $ip (@{ $ref_AD_rooms->{'room'}{$room}{'sophomorixRoomIPs'} }){
            $lookup{'LOOKUP'}{'room_by_IP'}{$ip}=$room;
        }
        foreach my $computer (@{ $ref_AD_rooms->{'room'}{$room}{'sophomorixRoomComputers'} }){
            $lookup{'LOOKUP'}{'room_by_COMPUTER'}{$computer}=$room;
        }
    }
    foreach my $user (keys  %{ $smbstatus{'USER'} }){
        my $room=$lookup{'LOOKUP'}{'room_by_IP'}{$smbstatus{'USER'}{$user}{'IP'}};

        if ($room){
            $lookup{'LOOKUP'}{'room_by_USER'}{$user}=$room;
            push @{ $lookup{'LISTS'}{'user_by_room'}{$room} },$user;

            # the result
            $lookup{'LOOKUP'}{'ROOM'}{$room}{$user}{'IP'}=$smbstatus{'USER'}{$user}{'IP'};
            $lookup{'LOOKUP'}{'ROOM'}{$room}{$user}{'IPV4'}=$smbstatus{'USER'}{$user}{'IPV4'};
            $lookup{'LOOKUP'}{'ROOM'}{$room}{$user}{'PORT'}=$smbstatus{'USER'}{$user}{'PORT'};
            $lookup{'LOOKUP'}{'ROOM'}{$room}{$user}{'SMBSTATUS_LINE'}=$smbstatus{'USER'}{$user}{'SMBSTATUS_LINE'};
            $lookup{'LOOKUP'}{'ROOM'}{$room}{$user}{'COMPUTER'}=$smbstatus{'USER'}{$user}{'COMPUTER'};
            $lookup{'LOOKUP'}{'ROOM'}{$room}{$user}{'ROOM'}=$room;
        }
    }

    # print the result as JSON
    my %result=();
    my $ref_lookup=\%lookup;
    if (defined $options{'query-ip'}){
        my $room=$lookup{'LOOKUP'}{'room_by_IP'}{$options{'query-ip'}};
        if (defined $room){
            &json_printout($options{'json'},$ref_lookup->{'LOOKUP'}{'ROOM'}{$room});
        } else {
            print "ip  \"$options{'query-ip'}\" not found in AD/smbstatus\n";
        }
    } elsif (defined $options{'query-comp'}){
        my $room=$lookup{'LOOKUP'}{'room_by_COMPUTER'}{$options{'query-comp'}};
        if (defined $room){
            &json_printout($options{'json'},$ref_lookup->{'LOOKUP'}{'ROOM'}{$room});
        } else {
            print "computer \"$options{'query-comp'}\" not found in AD/smbstatus\n";
        }
    } elsif (defined $options{'query-user'}){
        my $room=$lookup{'LOOKUP'}{'room_by_USER'}{$options{'query-user'}};
        if (defined $room){
            &json_printout($options{'json'},$ref_lookup->{'LOOKUP'}{'ROOM'}{$room});
        } else {
            print "user \"$options{'query-user'}\" not found in AD/smbstatus\n";
        }
    } elsif (defined $options{'query-room'}){

        &json_printout($options{'json'},$ref_lookup->{'LOOKUP'}{'ROOM'}{$options{'query-room'}});

    } else {
        &json_printout($options{'json'},\%smbstatus);
        &json_printout($options{'json'},$ref_AD_rooms);
        &json_printout($options{'json'},\%lookup);
    }
    exit;
}




############################################################
# set the filter: ( & (attr_filter) (role_filter) (type_filter) (status_filter) ) 
############################################################
$filter="(& ";

########################################
# set the attribute search filter
########################################
if (defined $options{'sam'} or 
    defined $options{'sn'} or 
    defined $options{'gn'}){
    ###
    $attr_filter=$attr_filter."(& ";
    if (defined $options{'sam'}){
        $attr_filter=$attr_filter."(sAMAccountName=".$options{'sam'}.")";
    }
    if (defined $options{'sn'}){
        $attr_filter=$attr_filter."(sn=".$options{'sn'}.")";
    }
    if (defined $options{'gn'}){
        $attr_filter=$attr_filter."(givenName=".$options{'gn'}.")";
    }
    $attr_filter=$attr_filter." )";
}


if (defined $options{'anyname'}){
    $attr_filter=$attr_filter."(| ";
    $attr_filter=$attr_filter."(sAMAccountName=".$options{'anyname'}.")";
    $attr_filter=$attr_filter."(sn=".$options{'anyname'}.")";
    $attr_filter=$attr_filter."(givenName=".$options{'anyname'}.")";
    $attr_filter=$attr_filter."(description=".$options{'anyname'}.")";
    $attr_filter=$attr_filter."(sophomorixComment=".$options{'anyname'}.")";
    $attr_filter=$attr_filter."(sophomorixFirstnameASCII=".$options{'anyname'}.")";
    $attr_filter=$attr_filter."(sophomorixSurnameASCII=".$options{'anyname'}.")";
    $attr_filter=$attr_filter." )";
} 


if (defined $options{'custom'}){
    $attr_filter=$attr_filter."(| ";
    $attr_filter=$attr_filter."(sophomorixCustom1=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustom2=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustom3=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustom4=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustom5=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustomMulti1=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustomMulti2=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustomMulti3=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustomMulti4=".$options{'custom'}.")";
    $attr_filter=$attr_filter."(sophomorixCustomMulti5=".$options{'custom'}.")";
    $attr_filter=$attr_filter." )";
} 


########################################
# set the role filter (combined with |)
########################################
if (defined $options{'student'} or 
    defined $options{'teacher'} or 
    defined $options{'parent'} or
    defined $options{'staff'} or
    defined $options{'examuser'} or
    defined $options{'globaladministrator'} or 
    defined $options{'schooladministrator'} or 
    defined $options{'globalbinduser'} or 
    defined $options{'schoolbinduser'} or 
    defined $options{'addc'} or
    defined $options{'classroom-studentcomputer'} or
    defined $options{'classroom-teachercomputer'} or
    defined $options{'faculty-teachercomputer'} or
    defined $options{'mobile'} or
    defined $options{'printer'} or
    defined $options{'server'} or
    defined $options{'staffcomputer'}
   ){
    ###
    $role_filter=$role_filter."(| ";
    if (defined $options{'student'}){
        $role_filter=$role_filter."(sophomorixRole=student)";
        $search_object="user";
    }
    if (defined $options{'parent'}){
        $role_filter=$role_filter."(sophomorixRole=parent)";
        $search_object="user";
    }
    if (defined $options{'staff'}){
        $role_filter=$role_filter."(sophomorixRole=staff)";
        $search_object="user";
    }
    if (defined $options{'teacher'}){
        $role_filter=$role_filter."(sophomorixRole=teacher)";
        $search_object="user";
    }
    if (defined $options{'examuser'}){
        $role_filter=$role_filter."(sophomorixRole=examuser)";
        $search_object="user";
    }
    if (defined $options{'globaladministrator'}){
        $role_filter=$role_filter."(sophomorixRole=globaladministrator)";
        $search_object="user";
    }
    if (defined $options{'schooladministrator'}){
        $role_filter=$role_filter."(sophomorixRole=schooladministrator)";
        $search_object="user";
    }
    if (defined $options{'globalbinduser'}){
        $role_filter=$role_filter."(sophomorixRole=globalbinduser)";
        $search_object="user";
    }
    if (defined $options{'schoolbinduser'}){
        $role_filter=$role_filter."(sophomorixRole=schoolbinduser)";
        $search_object="user";
    }
    if (defined $options{'addc'}){
        $role_filter=$role_filter."(sophomorixRole=addc)";
        $search_object="computer";
    }
    if (defined $options{'classroom-studentcomputer'}){
        $role_filter=$role_filter."(sophomorixRole=classroom-studentcomputer)";
        $search_object="computer";
    }
    if (defined $options{'classroom-teachercomputer'}){
        $role_filter=$role_filter."(sophomorixRole=classroom-teachercomputer)";
        $search_object="computer";
    }
    if (defined $options{'faculty-teachercomputer'}){
        $role_filter=$role_filter."(sophomorixRole=faculty-teachercomputer)";
        $search_object="computer";
    }
    if (defined $options{'mobile'}){
        $role_filter=$role_filter."(sophomorixRole=mobile)";
        $search_object="computer";
    }
    if (defined $options{'printer'}){
        $role_filter=$role_filter."(sophomorixRole=printer)";
        $search_object="computer";
    }
    if (defined $options{'server'}){
        $role_filter=$role_filter."(sophomorixRole=server)";
        $search_object="computer";
    }
    if (defined $options{'staffcomputer'}){
        $role_filter=$role_filter."(sophomorixRole=staffcomputer)";
        $search_object="computer";
    }
    $role_filter=$role_filter." )";
}


########################################
# set the type filter (combined with |)
########################################
if (defined $options{'adminclass'} or 
    defined $options{'extraclass'} or 
    defined $options{'teacherclass'} or 
    defined $options{'project'} or 
    defined $options{'sophomorix-group'} or 
    defined $options{'room'} or
    defined $options{'printergroup'}
   ){
     ###
    $type_filter=$type_filter."(| ";
    if (defined $options{'adminclass'}){
        $type_filter=$type_filter."(sophomorixType=adminclass)";
        $search_object="group";
    }
    if (defined $options{'extraclass'}){
        $type_filter=$type_filter."(sophomorixType=extraclass)";
        $search_object="group";
    }
    if (defined $options{'teacherclass'}){
        $type_filter=$type_filter."(sophomorixType=)";
        $search_object="group";
    }
    if (defined $options{'project'}){
        $type_filter=$type_filter."(sophomorixType=project)";
        $search_object="group";
    }
    if (defined $options{'sophomorix-group'}){
        $type_filter=$type_filter."(sophomorixType=sophomorix-group)";
        $search_object="group";
    }
    if (defined $options{'room'}){
        $type_filter=$type_filter."(sophomorixType=room)";
        $search_object="group";
    }
    if (defined $options{'printergroup'}){
        $type_filter=$type_filter."(sophomorixType=printer)";
        $search_object="group";
    }
    $type_filter=$type_filter." )";
}


########################################
# set the status filter (combined with |)
########################################
if (defined $options{'status'}){
    $status_filter=$status_filter."(| ";
    my @stati=split(/,/,$options{'status'});
    foreach my $stat (@stati){
        $status_filter=$status_filter."(sophomorixStatus=$stat)";
    }
    $status_filter=$status_filter." )";
} else {
    $status_filter="";
} 



############################################################
# user attributes to display
############################################################
### default user attrs
my $single_user_attrs=[
      'sAMAccountName',
      'sophomorixRole',
      'sophomorixAdminClass',
      'givenName',
      'sn',
    ];
my $multi_user_attrs=[
      'sophomorixQuota',
      'objectClass',
      'memberOf',
    ];
### override the user defaults
if (defined $options{'user-minimal'}){
    $single_user_attrs=[
            'sAMAccountName',
    ];
    $multi_user_attrs=[
    ];
} elsif (defined $options{'user-basic'}){
    # change nothing, use the default
} elsif (defined $options{'user-full'}){
    $single_user_attrs=[
        'sAMAccountName',
        'displayName',
        'sn',
        'givenName',
	'homeDirectory',
        'sophomorixFirstnameASCII',
        'sophomorixSurnameASCII',
        'sophomorixFirstnameInitial',
        'sophomorixSurnameInitial',
        'sophomorixBirthdate',
        'sophomorixUnid',
        'sophomorixAdminClass',
        'sophomorixExitAdminClass',
        'sophomorixSchoolname',
        'sophomorixAdminFile',
        'sophomorixComment',
        'sophomorixFirstPassword',
        'sophomorixExamMode',
        'sophomorixRole',
        'sophomorixUserToken',
        'sophomorixStatus',
        'sophomorixCreationDate',
        'sophomorixTolerationDate',
        'sophomorixDeactivationDate',
        'mail',
        'sophomorixMailQuota',
        'sophomorixMailQuotaCalculated',
        'sophomorixCloudQuotaCalculated',
        'userAccountControl',
        'sophomorixCustom1',
        'sophomorixCustom2',
        'sophomorixCustom3',
        'sophomorixCustom4',
        'sophomorixCustom5',
        'sophomorixIntrinsic1',
        'sophomorixIntrinsic2',
        'sophomorixIntrinsic3',
        'sophomorixIntrinsic4',
        'sophomorixIntrinsic5',
    ];
    $multi_user_attrs=[
      'sophomorixQuota',
      'memberOf',
      'sophomorixWebuiPermissionsCalculated',
      'sophomorixCustomMulti1',
      'sophomorixCustomMulti2',
      'sophomorixCustomMulti3',
      'sophomorixCustomMulti4',
      'sophomorixCustomMulti5',
      'sophomorixIntrinsicMulti1',
      'sophomorixIntrinsicMulti2',
      'sophomorixIntrinsicMulti3',
      'sophomorixIntrinsicMulti4',
      'sophomorixIntrinsicMulti5',
      'proxyAddresses',
    ];
}



############################################################
# computer attributes to display
############################################################
### default computer attrs
my $single_comp_attrs=[
      'sAMAccountName',
      'sophomorixRole',
    ];
my $multi_comp_attrs=[
    ];
### override the computer defaults
if (defined $options{'comp-minimal'}){
    $single_comp_attrs=[
            'sAMAccountName',
    ];
    $multi_comp_attrs=[
    ];
} elsif (defined $options{'comp-basic'}){
    # change nothing, use the default
} elsif (defined $options{'comp-full'}){
    $single_comp_attrs=[
        'sAMAccountName',
        'sophomorixRole',
        'sn',
        'name',
        'displayName',
        'dNSHostName',
        'sophomorixAdminClass',
        'sophomorixAdminFile',
        'sophomorixCreationDate',
        'sophomorixDnsNodename',
        'sophomorixSchoolname',
        'sophomorixStatus',
        'userAccountControl',
    ];
    $multi_comp_attrs=[
      'memberOf',
    ];
}


############################################################
# group attributes to display
############################################################
### default group attrs
my $single_group_attrs=[
      'sAMAccountName',
      'sophomorixType',
    ];
my $multi_group_attrs=[
      'sophomorixQuota',
    ];
### override the group defaults
if (defined $options{'group-minimal'}){
    $single_group_attrs=[
            'sAMAccountName',
    ];
    $multi_group_attrs=[
    ];
} elsif (defined $options{'group-basic'}){
    # change nothing, use the default
} elsif (defined $options{'group-full'}){
    $single_group_attrs=[
        'sAMAccountName',
        'cn',
        'name',
        'description',
        'gidNumber',
        'sophomorixCreationDate',
        'sophomorixHidden',
        'sophomorixJoinable',
        'sophomorixMaxMembers',
        'sophomorixStatus',
        'sophomorixType',
        'mail',
        'sophomorixMailQuota',
        'sophomorixAddMailQuota',
        'sophomorixMailAlias',
        'sophomorixMailList',
        'sophomorixMailList',
        'sophomorixSchoolname',
    ];
    $multi_group_attrs=[
      'sophomorixQuota',
      'sophomorixAddQuota',
      'sophomorixAdmins',
      'sophomorixMembers',
      'sophomorixAdminGroups',
      'sophomorixMemberGroups',
    ];
}

# add the member attribute, if option is given
if (defined $options{'group-members'}){
    push @{ $multi_group_attrs },"member";
}



############################################################
# find out which objectclass to search for
############################################################
($top_key,
 $top_count_key,
 $objectclass_filter,
 $single_attrs,
 $multi_attrs)=&setup_json_return($search_object);



############################################################
# combine the search filters
############################################################
$filter="(& ".
        "".$objectclass_filter."".
        "".$attr_filter."".
        "".$role_filter."".
        "".$type_filter."".
        "".$status_filter."".
        " )";



############################################################
# combine the attribute lists
############################################################
@{ $query_attrs }=(@{ $single_attrs },@{ $multi_attrs });



############################################################
# do the search
############################################################
# search for the object
if ($search_object eq "all"){
    $mesg = $ldap->search(
                   base   => $base,
                   scope => $scope,
                   filter => $filter,
                  # attrs => $query_attrs, # query all attributes
                  );
} else {
    $mesg = $ldap->search(
                   base   => $base,
                   scope => $scope,
                   filter => $filter,
                   attrs => $query_attrs,
                  );
}


# display debugging info
if (defined $options{'verbose'} and $options{'verbose'}==1){  
    print "Option hash:\n";
    print Dumper (\%options);
    print "\n";
    print "Search parameters:\n";
    print "  BASE:       $base\n";
    print "  SCOPE:      $scope\n";
    print "  FILTER:     $filter\n";
    print "  SEARCH FOR: $search_object\n";
    print "  ATTRIBUTES:";
    if ($search_object eq "all"){
        print " ALL attributes\n";
    } else {
        print "\n";
        foreach my $attr (@{ $single_attrs }){
            print "     * $attr (singlevalue)\n";
        }
        foreach my $attr (@{ $multi_attrs }){
            print "     * $attr (multivalue)\n";
        }
    }
    print "\n";
}


# go through results
$max = $mesg->count; 
$search{'COUNTERS'}{$top_count_key}=$max; 
for( my $index = 0 ; $index < $max ; $index++) {
    my $entry = $mesg->entry($index);
    my $dn = $entry->dn();
    my $sam=$entry->get_value('sAMAccountName');
    my $search_object_used;
    if ($search_object eq "all"){
        # find out what is searched for
        my @objectclass=$entry->get_value('objectClass');
        foreach my $oc (@objectclass){
            if ($oc eq "user"){
                $search_object_used="user";
                # continue, because it might also be a computer
            } elsif ($oc eq "computer"){
                $search_object_used="computer";
                # that is enough
                last;
            } elsif ($oc eq "group"){
                my $type=$entry->get_value('sophomorixType');
                if (not defined $type){
                    $search_object_used="non-sophomorix";
                } elsif ($type eq "room"){
                    $search_object_used="room";
                } else {
                    $search_object_used="group";
                }
            }
        }
        # setup some stuff
        ($top_key,
         $top_count_key,
         $objectclass_filter,
         $single_attrs,
         $multi_attrs)=&setup_json_return($search_object_used);
    } else {
        # use what is searched for
        $search_object_used=$search_object;
    }
    push @{ $search{'LISTS'}{$top_key} }, $sam;

    # add DN to result
    $search{$top_key}{$sam}{'DN'}=$dn;
    # read single value attributes into %search 
    foreach my $attribute ( @{ $single_attrs } ){
        $search{$top_key}{$sam}{$attribute}=$entry->get_value($attribute);    
    }
    # read multi value attributes into %search
    foreach my $attribute ( @{ $multi_attrs } ){
        if ($attribute eq "member" or 
            $attribute eq "objectClass"){
            # member is not needed at group, only to find members 
            # objectClass is onlx needed to decide which object has to be displayed
            next;
        }
        @{ $search{$top_key}{$sam}{$attribute} }=$entry->get_value($attribute);
    }

    # managementgroups
    if ($search_object eq "user"){
        my @member_of = $entry->get_value('memberOf');
        foreach my $member_of (@member_of){
	    if ($member_of=~m/OU=Management/){
	        my ($name,@other)=split(/,/,$member_of);
	        $name=~s/^CN=//;
                # push @{ $search{$top_key}{$sam}{'MANAGEMENTGROUPS'} }, $name;
                $search{$top_key}{$sam}{'MANAGEMENTGROUPS'}{$name}="TRUE";
	    }
        }
    }

    # if needed: query quota usage
    if (defined $options{'quota-usage'}){
        foreach my $sophomorix_quota (@{$search{$top_key}{$sam}{'sophomorixQuota'} }){
            my ($share,$value,$oldcalc,$quotastatus,$comment)=split(/:/,$sophomorix_quota);
            my $smbcquotas_command="/usr/bin/smbcquotas -U administrator%`cat /etc/linuxmuster/.secret/administrator`".
                " -u $sam //".$root_dns."/".$share." 2> /dev/null";
            #print "   $smbcquotas_command\n";
            # run the command
            my $smbcquotas_out=`$smbcquotas_command`;
            my $smbcquotas_return=${^CHILD_ERROR_NATIVE}; # return of value of last command

            my ($full_user,
                $quota_user,
                $colon,
                $used,
                $soft_limit,
                $hard_limit,
                $used_mib,
                $soft_limit_mib,
                $hard_limit_mib,
               )=&Sophomorix::SophomorixBase::analyze_smbcquotas_out($smbcquotas_out,$sam);

            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'FULL_USER'}=$full_user;
            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'USED'}=$used;
            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'SOFT_LIMIT'}=$soft_limit;
            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'HARD_LIMIT'}=$hard_limit;
            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'USED_MiB'}=$used_mib;
            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'SOFT_LIMIT_MiB'}=$soft_limit_mib;
            $search{$top_key}{$sam}{'QUOTA_USAGE_BY_SHARE'}{$share}{'HARD_LIMIT_MiB'}=$hard_limit_mib;
        }    
    }

    # if needed: find group members (users/computers)
    my $top_key_members="";
    my $top_count_key_members="";
    my $top_list_key_members="";
    my $filter_append;
    if (defined $options{'group-members'}){
        my @members = $entry->get_value('member');
        # decide if computers or users are searched
        if ($search_object_used eq "group"){
            $top_key_members="MEMBERS";
            $top_count_key_members="MEMBERCOUNT";
            $top_list_key_members="MEMBERLIST";
            @{ $query_attrs }=(@{ $single_user_attrs },@{ $multi_user_attrs });
            $single_attrs=$single_user_attrs;
            $multi_attrs=$multi_user_attrs;
            $filter_append="";
        } elsif ($search_object_used eq "room"){
            $top_key_members="COMPUTERS";
            $top_count_key_members="COMPUTERCOUNT";
            $top_list_key_members="COMPUTERLIST";
            @{ $query_attrs }=(@{ $single_comp_attrs },@{ $multi_comp_attrs });
            $single_attrs=$single_comp_attrs;
            $multi_attrs=$multi_comp_attrs;
            $filter_append="\$"; # sAMAccountName for computers are ended by $
        } else {
            # searching for something strange
            print "\nERROR: Searching for strange stuff\n\n";
            exit 88;
        }
        $search{'COUNTERS'}{$top_count_key_members}{$sam}=$#members+1;;
        foreach my $member (@members){
            my ($usersam,@rest)=split(/,/,$member);
            $usersam=~s/^CN=//;
            $search{$top_key_members}{$sam}{$usersam}{'sAMAccountName'}=$usersam;
            push @{ $search{'LISTS'}{$top_list_key_members}{$sam} }, $usersam;
            # query the members
            my $base=$member;
            my $scope="base";
            
            my $filter="(sAMAccountName=".$usersam.$filter_append.")"; # cannot be empty

            if (defined $options{'verbose'} and $options{'verbose'}==1){
                print "\n";
                print "BASE:       $base\n";
                print "SCOPE:      $scope\n";
                print "FILTER:     $filter\n";
            }

            my $mesg = $ldap->search( # perform a search
                              base   => $base,
                              scope => $scope,
                              filter => $filter,
                              attrs => $query_attrs,
                             );
            my $max = $mesg->count;

            # fill the result hash 
            # only none (ipdevice in room) OR one result possible (index=0):
            if ($max==0){
                next;
            } else {
                # there is one result
                my $entry = $mesg->entry(0);
                # add DN to result
                $search{$top_key_members}{$sam}{$usersam}{'DN'}=$entry->dn;
                foreach my $attribute ( @{ $single_attrs } ){
                    $search{$top_key_members}{$sam}{$usersam}{$attribute}=$entry->get_value($attribute);    
                }
                # work on multi value attributes
                foreach my $attribute ( @{ $multi_attrs } ){
                    @{ $search{$top_key_members}{$sam}{$usersam}{$attribute} }=$entry->get_value($attribute);
                }
            }

            # managementgroups
            if ($search_object eq "group"){
                foreach my $member_of (@{ $search{'MEMBERS'}{$sam}{$usersam}{'memberOf'} }){
	            if ($member_of=~m/OU=Management/){
	                my ($name,@other)=split(/,/,$member_of);
	                $name=~s/^CN=//;
                        $search{'MEMBERS'}{$sam}{$usersam}{'MANAGEMENTGROUPS'}{$name}="TRUE";
	            }
                }
            }
        }
    }
    if ($top_list_key_members ne "" and defined $search{'LISTS'}{$top_list_key_members}{$sam}){
        @{ $search{'LISTS'}{$top_list_key_members}{$sam} } = sort @{ $search{'LISTS'}{$top_list_key_members}{$sam} };
    }
}


#print Dumper(\%search{'LISTS'});

# sort and count some lists
if ($#{ $search{'LISTS'}{$top_key} }>-1){
    @{ $search{'LISTS'}{$top_key} } = sort @{ $search{'LISTS'}{$top_key} };
}

if ($#{ $search{'LISTS'}{'GROUP'} }>-1){
    @{ $search{'LISTS'}{'GROUP'} } = sort @{ $search{'LISTS'}{'GROUP'} };
    $search{'COUNTERS'}{'GROUP'}=$#{ $search{'LISTS'}{'GROUP'} }+1;
}

if ($#{ $search{'LISTS'}{'ROOM'} }>-1){
    @{ $search{'LISTS'}{'ROOM'} } = sort @{ $search{'LISTS'}{'ROOM'} };
    $search{'COUNTERS'}{'ROOM'}=$#{ $search{'LISTS'}{'ROOM'} }+1;
}

if ($#{ $search{'LISTS'}{'USER'} }>-1){
    @{ $search{'LISTS'}{'USER'} } = sort @{ $search{'LISTS'}{'USER'} };
    $search{'COUNTERS'}{'USER'}=$#{ $search{'LISTS'}{'USER'} }+1;
}

if ($#{ $search{'LISTS'}{'COMPUTER'} }>-1){
    @{ $search{'LISTS'}{'COMPUTER'} } = sort @{ $search{'LISTS'}{'COMPUTER'} };
    $search{'COUNTERS'}{'COMPUTER'}=$#{ $search{'LISTS'}{'COMPUTER'} }+1;
}

#print Dumper(\%search{'COUNTERS'});
# show the json object
#print Dumper(\%search);

# print JSON Object
&json_printout($options{'json'},\%search);

# disconnect
&AD_unbind_admin($ldap);


############################################################
# sub
############################################################
sub setup_json_return {
    my ($search_object)=@_;
    if ($search_object eq "user"){
        $top_key="USER";
        $top_count_key="USER";
        $objectclass_filter="(objectClass=user)";
        $single_attrs=$single_user_attrs;
        $multi_attrs=$multi_user_attrs;
    } elsif ($search_object eq "group"){
        $top_key="GROUP";
        $top_count_key="GROUP";
        $objectclass_filter="(objectClass=group)";
        $single_attrs=$single_group_attrs;
        $multi_attrs=$multi_group_attrs;
    } elsif ($search_object eq "room"){
        $top_key="ROOM";
        $top_count_key="ROOM";
        $objectclass_filter="(objectClass=group)";
        $single_attrs=$single_group_attrs;
        $multi_attrs=$multi_group_attrs;
    } elsif ($search_object eq "computer"){
        $top_key="COMPUTER";
        $top_count_key="COMPUTER";
        $objectclass_filter="(objectClass=computer)";
        $single_attrs=$single_comp_attrs;
        $multi_attrs=$multi_comp_attrs;
    } elsif ($search_object eq "all"){
        # no objectClass filter
        # this is the default
        $top_key="ALL";
        $top_count_key="ALL";
        $objectclass_filter="";
        $single_attrs=[
          'sAMAccountName',
          'sophomorixRole',
          'sophomorixType',
        ];
        $multi_attrs=[
          'objectClass',
        ];
    }
    return ($top_key,$top_count_key,$objectclass_filter,$single_attrs,$multi_attrs);
}



sub json_printout {
    my ($json,$ref_search)=@_;
    if ($json==0){
        my $line="--------------------------------------------------\n";
        # SCHOOL
        if ($#{ $ref_search->{'LISTS'}{'SCHOOL'} }>-1){
            my $counter=0;
            print $line;
            print "$ref_search->{'COUNTERS'}{'SCHOOL'} SCHOOLS:\n";
            foreach my $school (@{ $ref_search->{'LISTS'}{'SCHOOL'} }){
                $counter++;
                printf "  %4s:  %-60s\n",$counter,$school;
            }
            print "$ref_search->{'COUNTERS'}{'SCHOOL'} SCHOOLS\n";
        } else {
            print $line;
            print "   * No SCHOOLS found\n";
        }
        # GROUP
        if ($#{ $ref_search->{'LISTS'}{'GROUP'} }>-1){
            my $counter=0;
            print $line;
            print "$ref_search->{'COUNTERS'}{'GROUP'} GROUPS:\n";
            foreach my $group (@{ $ref_search->{'LISTS'}{'GROUP'} }){
                $counter++;
                printf "  %4s:  %-60s\n",$counter,$group;
            }
            print "$ref_search->{'COUNTERS'}{'GROUP'} GROUPS\n";
        } else {
            print $line;
            print "   * No GROUPS found\n";
        }
        # ROOM
        if ($#{ $ref_search->{'LISTS'}{'ROOM'} }>-1){
            my $counter=0;
            print $line;
            print "$ref_search->{'COUNTERS'}{'ROOM'} ROOMS:\n";
            foreach my $room (@{ $ref_search->{'LISTS'}{'ROOM'} }){
                $counter++;
                printf "  %4s:  %-60s\n",$counter,$room;
            }
            print "$ref_search->{'COUNTERS'}{'ROOM'} ROOMS\n";
        } else {
            print $line;
            print "   * No ROOMS found\n";
        }
        # USER
        if ($#{ $ref_search->{'LISTS'}{'USER'} }>-1){
            my $counter=0;
            print $line;
            print "$ref_search->{'COUNTERS'}{'USER'} USERS:\n";
            foreach my $user (@{ $ref_search->{'LISTS'}{'USER'} }){
                $counter++;
                printf "  %4s:  %-60s\n",$counter,$user;
            }
            print "$ref_search->{'COUNTERS'}{'USER'} USERS\n";
        } else {
            print $line;
            print "   * No USERS found\n";
        }
        # COMPUTER
        if ($#{ $ref_search->{'LISTS'}{'COMPUTER'} }>-1){
            my $counter=0;
            print $line;
            print "$ref_search->{'COUNTERS'}{'COMPUTER'} COMPUTERS:\n";
            foreach my $computer (@{ $ref_search->{'LISTS'}{'COMPUTER'} }){
                $counter++;
                printf "  %4s:  %-60s\n",$counter,$computer;
            }
            print "$ref_search->{'COUNTERS'}{'COMPUTER'} COMPUTERS\n";
        } else {
            print $line;
            print "   * No COMPUTERS found\n";
        }
    } elsif ($json==1){
        # pretty output
        my $json_obj = JSON->new->allow_nonref;
        my $utf8_pretty_printed = $json_obj->pretty->encode( $ref_search );
        print STDERR "# JSON-begin\n";
        print STDERR "$utf8_pretty_printed";
        print STDERR "# JSON-end\n";
    } elsif ($json==2){
        my $json_obj = JSON->new->allow_nonref;
        my $utf8_json_line   = $json_obj->encode( $ref_search  );
        print STDERR "# JSON-begin\n";
        print STDERR "$utf8_json_line";
        print STDERR "# JSON-end\n";
    } elsif ($json==3){
        print STDERR Dumper ($ref_search);
    } elsif ($json==4){
        # pretty output in file
        my $json_obj = JSON->new->allow_nonref;
        my $utf8_pretty_printed = $json_obj->pretty->encode( $ref_search );
        my $output=$json_output_path."/output.json";
	print "output to file $output\n";
        open(my $fh, ">", $output);
        print $fh $utf8_pretty_printed;
        close($fh);
    } elsif ($json==5){
        # compact output in file
        my $json_obj = JSON->new->allow_nonref;
        my $utf8_json_line   = $json_obj->encode( $ref_search  );
        my $output=$json_output_path."/output.json";
	print "output to file $output\n";
        open(my $fh, ">", $output);
        print $fh $utf8_json_line;
        close($fh);
    }

}



sub get_root_dns {
    my $realm_line =`cat /etc/samba/smb.conf | grep realm`;
    chomp($realm_line);
    my ($tmp,$root_dns)=split(/=/,$realm_line);
    $root_dns=~s/^\s+//g;# remove leading whitespace
    $root_dns=~s/\s+$//g;# remove trailing whitespace
    $root_dns=~tr/A-Z/a-z/; # make lowercase
    return $root_dns;
}



sub AD_get_rooms {
    my ($arg_ref) = @_;
    my $ldap = $arg_ref->{ldap};
    my $root_dse = $arg_ref->{root_dse};
    my $root_dns = $arg_ref->{root_dns};
    my %AD=();
    &Sophomorix::SophomorixBase::print_title("Query AD for rooms (start)");
    ############################################################
    # sophomorix rooms/devicegroupes from ldap
    { # BLOCK group start
        my $filter="(& (objectClass=group) (| ".
                   "(sophomorixType=room) ".
                   "(sophomorixType=devicegroup) ";
        $filter=$filter.") )";

	#print "Filter for device groups: $filter\n";
        $mesg = $ldap->search( # perform a search
                       base   => $root_dse,
                       scope => 'sub',
                       filter => $filter,
                       attrs => ['sAMAccountName',
                                 'sophomorixStatus',
                                 'sophomorixSchoolname',
                                 'sophomorixType',
                                 'description',
                                 'member',
                                 'sophomorixRoomIPs',
                                 'sophomorixRoomMACs',
                                 'sophomorixRoomComputers',
                                 'sophomorixRoomDefaults',
                                ]);
        my $max_group = $mesg->count; 
        &Sophomorix::SophomorixBase::print_title("$max_group sophomorix rooms/devicegroupes found in AD");
        $AD{'RESULT'}{'group'}{'TOTAL'}{'COUNT'}=$max_group;
        $AD{'RESULT'}{'group'}{'room'}{'COUNT'}=0;
        for( my $index = 0 ; $index < $max_group ; $index++) {
            my $entry = $mesg->entry($index);
            my $dn = $entry->dn();
            my $sam=$entry->get_value('sAMAccountName');
            my $type=$entry->get_value('sophomorixType');
            my $stat=$entry->get_value('sophomorixStatus');
            my $schoolname=$entry->get_value('sophomorixSchoolname');
            my $description=$entry->get_value('description');

            $AD{$type}{$sam}{'room'}=$sam;
            $AD{$type}{$sam}{'sophomorixStatus'}=$stat;
            $AD{$type}{$sam}{'sophomorixType'}=$type;
            $AD{$type}{$sam}{'description'}=$description;
            # increase role counter 
            $AD{'RESULT'}{'group'}{$type}{'COUNT'}++;

            $AD{$type}{$sam}{'sophomorixSchoolname'}=$schoolname;
            $AD{$type}{$sam}{'DN'}=$dn;

            @{ $AD{$type}{$sam}{'sophomorixRoomIPs'} }=$entry->get_value('sophomorixRoomIPs');
            @{ $AD{$type}{$sam}{'sophomorixRoomMACs'} }=$entry->get_value('sophomorixRoomMACs');
            @{ $AD{$type}{$sam}{'sophomorixRoomComputers'} }=$entry->get_value('sophomorixRoomComputers');
            @{ $AD{$type}{$sam}{'sophomorixRoomDefaults'} }=$entry->get_value('sophomorixRoomDefaults');

            # host groups
#            if (exists $ref_sophomorix_config->{'LOOKUP'}{'ROLES_DEVICE'}{$type}){
#                $AD{'host_group'}{$sam}=$type;
#            }

            # devicegroup memberships
            if ($type eq "devicegroup"){
                @{ $AD{$type}{$sam}{'member'} }=$entry->get_value('member');
                foreach my $dn (@{ $AD{$type}{$sam}{'member'} }){
                    my @parts=split(",",$dn);
                    my ($unused,$memsam)=split("=",$parts[0]);
		    $memsam=~tr/A-Z/a-z/;
                    $AD{$type}{$sam}{'member_sAMAccountName'}{$memsam}="seen";
                }
            }

            # lists
            if ($type eq "room"){
                push @{ $AD{'LISTS'}{'ROOM_BY_sophomorixSchoolname'}{$schoolname}{'rooms'} }, $sam;
            } elsif ($type eq "devicegroup"){
                push @{ $AD{'LIST_DEVICEGROUPS'} }, $sam;
            }
            #push @{ $AD{'LISTS'}{'BY_SCHOOL'}{$schoolname}{'groups_BY_sophomorixType'}{$type} }, $sam; 
            #$AD{'LOOKUP'}{'sophomorixType_BY_sophomorixAdminClass'}{$sam}=$type;
        }
    } # BLOCK group end
    { # BLOCK computer start
        ### create filter
        # finds all computer with any string as sophomorixRole
        my $filter="(& (objectClass=computer) (sophomorixRole=*) )";
        # print "Filter:  $filter\n";
        my $mesg = $ldap->search( # perform a search
                          base   => $root_dse,
                          scope => 'sub',
                          filter => $filter,
                          attrs => ['sAMAccountName',
                                    'sophomorixDnsNodename',
                                    'sophomorixComputerIP',
                                   ]);
        my $max_computer = $mesg->count; 
        &Sophomorix::SophomorixBase::print_title("$max_computer Computers found in AD");
        for( my $index = 0 ; $index < $max_computer ; $index++) {
            my $entry = $mesg->entry($index);
            $AD{'LOOKUP'}{$entry->get_value('sophomorixComputerIP')}=$entry->get_value('sophomorixDnsNodename');
        }
    }  # BLOCK computer end

    return(\%AD);
}

