#!/usr/bin/perl -wT

#----------------------------------------------------------------------
# heading     : Collaboration
# description : User accounts export
# navigation  : 3000 3110
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
# GNU General Public License for more details.
#----------------------------------------------------------------------
#######################################################################
# INSTALLATION
#######################################################################
# To install this script do:
# cp $ThisScriptName /etc/e-smith/web/functions/
# ln -s ../../../functions/$ThisScriptName /etc/e-smith/web/panels/manager/cgi-bin/$ThisScriptName
# chmod 4750 /etc/e-smith/web/functions/$ThisScriptName
# chown root.admin /etc/e-smith/web/functions/$ThisScriptName
#######################################################################
#----------------------------------------------------------------------
# Otherwise it you won't run correctly
#----------------------------------------------------------------------
# Last Update 04-10-02 at 9:16pm
#######################################################################

package esmith;

use strict;
use CGI ':all';
use CGI::Pretty;
use CGI::Carp qw/fatalsToBrowser/;

use esmith::cgi;
use esmith::config;
use esmith::util;
use esmith::db;
use esmith::event;

sub showInitial ($$);
sub Delimiter($$$);
sub GetDelimiter($$$);
sub Enclose($$);
sub GetEnclose($$);
sub performExport($);
sub SystemFields();
sub AllFields();
sub TimeStamp();
sub CheckConditions($$$);
sub GetGroups ($$);

# This gets the only the name of the script;
my @trash = split(/\//,$0);
my $only_script_name = pop(@trash);
$#trash=-1;

#----------------------------------------------------------------------


BEGIN
{
    # Clear PATH and related environment variables so that calls to
    # external programs do not cause results to be tainted. See
    # "perlsec" manual page for details.

    $ENV {'PATH'} = '';
    $ENV {'SHELL'} = '/bin/bash';
    delete $ENV {'ENV'};
}

esmith::util::setRealToEffective ();

my %conf;
tie %conf, 'esmith::config';

my %accounts;
tie %accounts, 'esmith::config', '/home/e-smith/accounts';

my $pwddb;

if (-f '/home/e-smith/userspwds')
{
$pwddb = 1;
}

my %userspwds;
tie %userspwds, 'esmith::config', '/home/e-smith/userspwds' if $pwddb;

my $q = new CGI;

$q->param(-name=>'pwddb', -value=>$pwddb);


#===========================================================

if (! grep (/^state$/, $q->param))
{
	showInitial ($q,'');
}
elsif ($q->param('state') eq  'export')
{
	performExport($q);
}
else
{
    esmith::cgi::genStateError ($q, \%conf);
}

exit (0);

#===========================================================

sub showInitial ($$)
{
my ($q,$msg) = @_;

	if ($msg eq '')
    	{
		esmith::cgi::genHeaderNonCacheable($q, \%conf, 'Export user accounts/information');
    	}
    	else
    	{
		esmith::cgi::genHeaderNonCacheable($q, \%conf, 'Operation status report');
		print $q->p,$msg, $q->hr;
	}
	
	#------------------------------------------------------------
	# Look up accounts and user names
	#------------------------------------------------------------
	my @userAccounts = ();

    	foreach (sort keys %accounts)
    	{
		push (@userAccounts, $_) if (db_get_type(\%accounts, $_) eq "user");
    	}   	

    	unless (scalar @userAccounts)
    	{
		print $q->p ($q->b ('There are no user accounts in the system.'));
		esmith::cgi::genFooter ($q);
		exit;
    	}
    	
    	my @Systemfields = &SystemFields();    	
	my @fields = &AllFields();
	              	
	my ($i,$TableRow,$Conditions);
	foreach $i (0..$#fields)
	{		
		$TableRow .= $q->Tr({-align=>'center'},
			$q->td(	
				$q->popup_menu (-override=>1,
					-name => $i,
					-values  => [@fields,'NONE','EMPTY'],
					-default => $fields[$i]
				)
			)
		);
	}
	
	foreach $i (@Systemfields)
	{	
		$Conditions .= $q->Tr(
			$q->th($i),
			$q->td(	
				$q->popup_menu (-override=>1,
					-name => "ConditionType_$i",
					-values  => ['Numeric_Equal', 'Text_Equal',							
						'Greater_Then','Less_Then','Contains',
						'Not_Contains','Starts_With','Ends_With',
						'Not_Starts_With','Not_Ends_With']
				)
			),
			$q->td(
				$q->textfield(-name=>"Condition_$i",
					-maxlength=>20,
					-override=>1,
					-size=>10
				)
			)
		);
	}
	
	print $q->hr,
	$q->p,	
	$q->start_form(-method => 'POST', -action => $q->url (-absolute => 1)),
	$q->p,
	'Delimiter for output : ',	
	&Delimiter($q,'Delimiter','MyDelimiter'),
	$q->p,
	'Enclose each field : (leave blank for no) ',	
	&Enclose($q, 'EncloseFields'),
	$q->p,
	'File format: ',	
	$q->popup_menu (-name => 'FileFormat',
		     -values  => ['DOS','Linux'],		     
		     -override => 1,
		     -default => 'Linux'),
	$q->p,
	'Put everyones email in "Forwarding Address" (FEmail)',
	$q->checkbox_group(-name=>'all_emails',
			-values=>'yes'),
	$q->p,
	$q->p,
	$q->strong('***Note:'),
	'In order to export the users password (PWD), the "userspwds" ',
	'database must exist. If it doesn\'t "PWD" will be ignore.',
	$q->p,
	'Put these in the format you would like the output. ',
	'If you do not need a field select "NONE", If you want the field ',
	'but not the value, select "EMPTY".',
	$q->p,
	$q->start_table({-border=>1}),
	$q->Tr(
		$q->td(							
			$q->table({-border=>0},	$TableRow)
		),
		$q->td(			
			$q->table({-border=>0},	
				$q->caption(
					$q->strong('Conditions')
				),
				$Conditions
			)			
		)
	),
	$q->end_table,
	$q->p,
	$q->submit('submit',' Export '), '&nbsp;'x5, $q->reset(),
	$q->hidden(-override=>1, -name=>'state',-value=>'export'),
	$q->endform;
	
esmith::cgi::genFooter ($q);
exit;
}

#==========================================================

sub performExport($)
{	
	my ($q) = @_ ;

	my ($FirstName,
	$LastName,
	$acctName,
	$Passwd,
	$Department,
	$Company,
	$Street,
	$City,
	$Phone,
	$Email,
	$EmailAddress,
	@Groups,
	$i,
	$LineFormat,	
	$userLine,
	$output);

	my ($DelimiterName, $Delimiter) = &GetDelimiter($q, 'Delimiter','MyDelimiter');	
	my $EncloseChar = &GetEnclose($q,'EncloseFields');	
	my $pwddb = $q->param('pwddb');	
	my $OSFormat = $q->param('FileFormat');
	my $LineEnding = (($OSFormat eq 'DOS') ? ("\cM\cJ") : ("\n")) ;
	
	my @Systemfields = &SystemFields();    	
	my @fields = &AllFields();
	
	my $HeaderLine = '#';
	my $no_dash;
	my $ConditionsToMeet = 0;
	my $ConditionsMet = 0;
	my @myGroups =();		
	my $count;
    	my $groups;
				     	
	foreach $i (0..$#fields)
	{
		if ($q->param("Condition_$fields[$i]"))
		{
			$ConditionsToMeet++;
		}
		
		if ($q->param($i) ne 'NONE')
		{	
		$no_dash = $q->param($i);
		$no_dash =~ s/-*//g;
			if(($q->param($i) eq '-Pwd') and (!$pwddb))
			{				
			}
			elsif($i == $#fields)
			{
				$LineFormat .= "$EncloseChar".$q->param($i)."$EncloseChar$LineEnding";
				$HeaderLine .= $no_dash."$LineEnding";
			}
			else
			{		
				$LineFormat .= "$EncloseChar".$q->param($i)."$EncloseChar$Delimiter";
				$HeaderLine .= $no_dash."$Delimiter";
			}			
		}
	}
	
	$LineFormat = "$LineFormat$LineEnding" if $LineFormat !~ m/$LineEnding$/;
	$HeaderLine = "$HeaderLine$LineEnding" if $HeaderLine !~ m/$LineEnding$/;
		
	my @AllUsers = ();
	my @AllGroups = ();
	
	foreach $acctName (sort keys %accounts)	
    	{    	
    		if (db_get_type(\%accounts, $acctName) eq "user")
		{
		push(@AllUsers,$acctName);
		}
		elsif (db_get_type(\%accounts, $acctName) eq "group")
		{
		push(@AllGroups,$acctName);
		}
    	}
    	
    	foreach $acctName (@AllUsers)	
    	{   		
    		$userLine = $LineFormat;
    		$ConditionsMet = 0;
    		my $txtpwd;
    		
    		$#myGroups = -1;		
		$count = '';
    		$groups='';
    		    		
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-User"),
							$q->param("Condition_-User"),
							$acctName) if $q->param("Condition_-User");
		my $FN = db_get_prop(\%accounts, $acctName, "FirstName");    		
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-FName"),
							$q->param("Condition_-FName"),
							$FN) if $q->param("Condition_-FName");
    		my $LN = db_get_prop(\%accounts, $acctName, "LastName");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-LName"),
							$q->param("Condition_-LName"),
							$LN) if $q->param("Condition_-LName");
   		my $Ph = db_get_prop(\%accounts, $acctName, "Phone");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-Phone"),
							$q->param("Condition_-Phone"),
							$Ph) if $q->param("Condition_-Phone");
    		my $Cp = db_get_prop(\%accounts, $acctName, "Company");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-Comp"),
							$q->param("Condition_-Comp"),
							$Cp)if $q->param("Condition_-Comp");
    		my $Dp = db_get_prop(\%accounts, $acctName, "Dept");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-Dept"),
							$q->param("Condition_-Dept"),
							$Dp) if $q->param("Condition_-Dept");
    		my $Ct = db_get_prop(\%accounts, $acctName, "City");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-City"),
							$q->param("Condition_-City"),
							$Ct) if $q->param("Condition_-City");
    		my $St = db_get_prop(\%accounts, $acctName, "Street");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-Addr"),
							$q->param("Condition_-Addr"),
							$St) if $q->param("Condition_-Addr");		
    		my $Ef = db_get_prop(\%accounts, $acctName, "EmailForward");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-Email"),
							$q->param("Condition_-Email"),
							$Ef) if $q->param("Condition_-Email");		
		my $Fa = db_get_prop(\%accounts, $acctName, "ForwardAddress");
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-FEmail"),
							$q->param("Condition_-FEmail"),
							$Fa) if $q->param("Condition_-FEmail");
		if (($Fa eq '') and ($q->param('all_emails') eq 'yes'))
		{
			$Fa = $acctName.'@'.$conf{'DomainName'};			
		}
		
    		if($pwddb)
		{
			$txtpwd = &do_decode(db_get(\%userspwds, &do_encode($acctName)));
			$ConditionsMet += &CheckConditions($q->param("ConditionType_-Pwd"),
							$q->param("Condition_-Pwd"),
							$txtpwd) if $q->param("Condition_-Pwd");
		}
		
		@myGroups = &GetGroups ($acctName, @AllGroups);		
		$count = scalar @myGroups;    		
    		if ($count > 0)
    		{
    			foreach (0..$#myGroups)
    			{
    				if($_ == $#myGroups)
    				{
    					$groups .= $myGroups[$_];
    				}
    				else
    				{
    					$groups .= $myGroups[$_].',';
    				}
    			}
    			
    			if ($EncloseChar eq '')
    			{
    				$groups = "\"$groups\"";
    			}
    		}
    		
    		$ConditionsMet += &CheckConditions($q->param("ConditionType_-Groups"),
							$q->param("Condition_-Groups"),
							$groups) if $q->param("Condition_-Groups");
		
    		
    		if ($ConditionsToMeet > 0 )
    		{
    		next if ( $ConditionsMet != $ConditionsToMeet);
    		}
    		
    		if ($EncloseChar eq '')
    		{
    			#change this names so that i can enclose them safer.
    			$userLine =~ s/-FName -LName*/"-FName -LName"/g;
    			$userLine =~ s/-LName -FName*/"-LName -FName"/g;
    			$userLine =~ s/-LName, -FName*/"-LName, -FName"/g;    				
    		}
    		
    		$userLine =~ s/-User*/$acctName/g;
    		$userLine =~ s/-FName*/$FN/g;
    		$userLine =~ s/-LName*/$LN/g;
    		$userLine =~ s/-Phone*/$Ph/g;
    		$userLine =~ s/-Comp*/$Cp/g;
    		$userLine =~ s/-Dept*/$Dp/g;
    		$userLine =~ s/-City*/$Ct/g;
    		$userLine =~ s/-Addr*/$St/g;    		
    		$userLine =~ s/-Email*/$Ef/g;
    		$userLine =~ s/-FEmail*/$Fa/g; # this needs to be before -Email, because -FEmail contains -Email.
    		$userLine =~ s/-Groups*/$groups/g;    		
    		
    		if($pwddb)
		{
    		$userLine =~ s/-Pwd*/$txtpwd/g;
    		}
    		
    		$userLine =~ s/EMPTY*//g;
    		#check conditions
    		
    		$userLine = "$userLine$LineEnding" if $userLine !~ m/$LineEnding$/;
    		
    		$output .= $userLine;
	}
	
	my $file_name = 'ExportData-'.&TimeStamp().'.txt';
	
	print $q->header(-type=>'application/octet-stream',
			-Content_Disposition=>"attachment;filename=$file_name"),
	"$HeaderLine$output";
		
	exit;
}



###########################################################
sub SystemFields()
{
	return ("-FName", "-LName", "-User", "-Pwd", "-Dept", "-Comp", "-Addr",
		"-City", "-Phone", "-Email", "-FEmail", "-Groups" );
}
###########################################################   	
sub AllFields()
{
	return ('-FName -LName', '-LName -FName','-LName, -FName',
		     	&SystemFields() );
}
###########################################################
sub Delimiter($$$)
{
	my ($q, $pullname,$textname) = @_ ;
	
	my $default = $q->param($pullname) ? $q->param($pullname) : 'tab';

	return $q->popup_menu (-name => $pullname,
		     -values  => ['tab', 'comma', 'space', 'semicolon', 'colon', 'other'],
		     -override => 1,
		     -default => $default
		     ),
		" If other: ",		
		$q->textfield(-name=>$textname,
				-maxlength=>10,
				-override=>1,
				-size=>10,
				-value=>$q->param($textname)
		),"\n";
	
}
###########################################################
sub GetDelimiter($$$)
{
	my ($q,$pullname,$textname) = @_ ;
	if ($q->param($pullname) eq 'other' and $q->param($textname))
	{		
		my $i = $q->param($textname);
		return ($q->param($pullname),"$i");
	}
	elsif ($q->param($pullname) eq 'tab')
	{
		return ($q->param($pullname),"\t");
	}
	elsif ($q->param($pullname) eq 'comma')
	{		
		return ($q->param($pullname),",");
	}
	elsif ($q->param($pullname) eq 'space')
	{		
		return ($q->param($pullname),' ');
	}
	elsif ($q->param($pullname) eq 'semicolon')
	{		
		return ($q->param($pullname),';');
	}
	elsif ($q->param($pullname) eq 'colon')
	{		
		return ($q->param($pullname),':');
	}
	else
	{
		return ('tab',"\t");
	}
}
###########################################################
sub Enclose($$)
{
	my ($q, $name) = @_;
	
	my %Labels = (''   => ' ',
		       'double' => 'Double "quote"',
		       'single'    => "Single 'quote'");
	
	return $q->popup_menu (-name => $name,
			-values  => [' ', 'double', 'single'],
			-labels => \%Labels
	);

}
###########################################################
sub GetEnclose($$)
{
	my ($q,$name) = @_ ;
	
	if ($q->param($name) eq 'double')
	{
		return '"';
	}
	elsif($q->param($name) eq 'single')
	{
		return "'";
	}
	else
	{
		return '';
	}
}
###########################################################
sub CheckConditions($$$)
{	
	my ($ConditionType,$Condition,$Data) = @_ ;
	
	if ($ConditionType eq "Numeric_Equal")
	{
		return 1 if($Data == $Condition);
	}
	elsif ($ConditionType eq "Text_Equal")
	{
		return 1 if($Data eq $Condition);
	}	
	elsif ($ConditionType eq "Greater_Then")
	{
		return 1 if($Data > $Condition);
	}
	elsif ($ConditionType eq "Less_Then")
	{
		return 1 if($Data < $Condition);
	}
	elsif ($ConditionType eq "Contains")
	{
		return 1 if($Data =~ m/$Condition/i);
	}
	elsif ($ConditionType eq "Not_Contains")
	{
		return 1 if($Data !~ m/$Condition/i);
	}
	elsif ($ConditionType eq "Starts_With")
        {
                return 1 if($Data =~ m/^$Condition/i);
        }
	elsif ($ConditionType eq "Ends_With")
        {
                return 1 if($Data =~ m/$Condition$/i);
        }
	elsif ($ConditionType eq "Not_Starts_With")
        {
                return 1 if($Data !~ m/^$Condition/i);
        }
        elsif ($ConditionType eq "Not_Ends_With")
        {
                return 1 if($Data !~ m/$Condition$/i);
        }

	# If it gets down here, the condition was not met.
	return 0;
}
###########################################################
sub do_encode($)
{
my @TXTS ;
my ($i, $ox);

@TXTS = split(//, $_[0]);

        foreach $i (@TXTS)
        {
                $ox .= unpack('H*', ord($i) ) . "O";
        }
$ox =~ s/O$//;
return $ox ;
}

###########################################################
sub do_decode($)
{
my @CHARS;
my ($i, $string);

@CHARS = split(/O/, $_[0]);

        foreach $i (@CHARS)
        {
                $string .= chr( pack('H*',$i) );
        }
return $string;
}

#############################################################
sub TimeStamp()
{
my ($second,$minute,$hour,$day,$month,$year,$wday,$yday,$isdst,$current_date);
   ($second,$minute,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime(time);

   $year += 1900;
 
   my @months = ("XX","Jan","Feb","Mar","Apr","May","Jun","Jul",
              "Aug","Sep","Oct","Nov","Dec");

   my @days = ("XX","1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th",
            "11th","12th","13th","14th","15th","16th","17th","18th","19th",
            "20th","21st","22nd","23rd","24th","25th","26th","27th","28th",
            "29th","30th","31st");

   $current_date = $months[($month + 1)]."_".$days[$day]."_".$year."_".$hour;
   if ($minute < 10) {$current_date = "$current_date"."_0$minute";}
   else {$current_date = "$current_date"."_$minute";}

   if ($second < 10) {$current_date = "$current_date"."_0$second";}
   else {$current_date = "$current_date"."_$second";}

return $current_date;
}
#############################################################
sub GetGroups ($$)
{
	my ($user,@groups) = @_;
	
	my @selected = ();

	foreach (@groups)
	{
		my @members = split (/,/, db_get_prop(\%accounts, $_, "Members"));

		if (grep (/^$user$/, @members))
		{
		    push @selected, $_;
		}
	}

    	@selected = sort @selected;
    	
    	return @selected;
}
#############################################################
# THE END :)