#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use Text::Unidecode;
use Date::Calc qw\Delta_Days\;

our $SERVICE_NAME = "passwd";
our $PROTOCOL_VERSION = "3.0.0";
my $SCRIPT_VERSION = "3.0.3";

perunServicesInit::init;
my $DIRECTORY = perunServicesInit::getDirectory;
my $data = perunServicesInit::getHashedHierarchicalData;

#Constants
our $A_FACILITY_MIN_UID;                       *A_FACILITY_MIN_UID =                      \'urn:perun:facility:attribute-def:virt:minUID';
our $A_FACILITY_MAX_UID;                       *A_FACILITY_MAX_UID =                      \'urn:perun:facility:attribute-def:virt:maxUID';
our $A_USER_FACILITY_LOGIN;                    *A_USER_FACILITY_LOGIN =                   \'urn:perun:user_facility:attribute-def:virt:login';
our $A_USER_FACILITY_UID;                      *A_USER_FACILITY_UID =                     \'urn:perun:user_facility:attribute-def:virt:UID';
our $A_USER_FACILITY_GID;                      *A_USER_FACILITY_GID =                     \'urn:perun:user_facility:attribute-def:virt:defaultUnixGID';
our $A_USER_FACILITY_HOME_MOUNT_POINT;         *A_USER_FACILITY_HOME_MOUNT_POINT =        \'urn:perun:user_facility:attribute-def:def:homeMountPoint';
our $A_USER_FACILITY_SHELL;                    *A_USER_FACILITY_SHELL =                   \'urn:perun:user_facility:attribute-def:virt:shell';
our $A_USER_FIRST_NAME;                        *A_USER_FIRST_NAME =                       \'urn:perun:user:attribute-def:core:firstName';
our $A_USER_LAST_NAME;                         *A_USER_LAST_NAME =                        \'urn:perun:user:attribute-def:core:lastName';
our $A_MEMBER_STATUS;                          *A_MEMBER_STATUS =                         \'urn:perun:member:attribute-def:core:status';
our $A_MEMBER_IS_SUSPENDED;                    *A_MEMBER_IS_SUSPENDED =                   \'urn:perun:member:attribute-def:virt:isSuspended';
our $A_MEMBER_EXPIRATION;                      *A_MEMBER_EXPIRATION =                     \'urn:perun:member:attribute-def:def:membershipExpiration';
our $A_R_VO_SHORT_NAME;                        *A_R_VO_SHORT_NAME =                       \'urn:perun:resource:attribute-def:virt:voShortName';

our $STATUS_SUSPENDED;                         *STATUS_SUSPENDED =                        \'SUSPENDED';
our $STATUS_VALID;                             *STATUS_VALID =                            \'VALID';

my $passwd_file_name = "$DIRECTORY/$::SERVICE_NAME";
my $shadow_file_name = "$DIRECTORY/shadow";
my $min_uid_file_name = "$DIRECTORY/min_uid";
my $max_uid_file_name = "$DIRECTORY/max_uid";

###### min_uid and max_uid file #####
open MIN_UID,">$min_uid_file_name" or die "Cannot open $min_uid_file_name: $! \n";
open MAX_UID,">$max_uid_file_name" or die "Cannot open $max_uid_file_name: $! \n";
print MIN_UID $data->getFacilityAttributeValue(attrName => $A_FACILITY_MIN_UID), "\n";
print MAX_UID $data->getFacilityAttributeValue(attrName => $A_FACILITY_MAX_UID), "\n";
close MIN_UID;
close MAX_UID;
#####################################

####### passswd and shadow file ######################
open PASSWD,">$passwd_file_name" or die "Cannot open $passwd_file_name: $! \n";
open SHADOW,">$shadow_file_name" or die "Cannot open $shadow_file_name: $! \n";

# Set restrictive rights on SHADOW file
chmod 0640, $shadow_file_name;

#hash memberAttributesByLogin to eliminate duplicities
#logins contain a reference to a hash of the attributes used to print files, e.g.:
#$memberAttributesByLogin->{$login}->{$A_MEMBER_STATUS} = $status;
my $memberAttributesByLogin = {};
foreach my $resourceId ($data->getResourceIds()) {
	foreach my $memberId ($data->getMemberIdsForResource(resource => $resourceId)) {
		my $login = $data->getUserFacilityAttributeValue(attrName => $A_USER_FACILITY_LOGIN, member => $memberId);
		my $status = $data->getMemberAttributeValue(attrName => $A_MEMBER_STATUS, member => $memberId);
		if($data->getMemberAttributeValue(attrName => $A_MEMBER_IS_SUSPENDED, member => $memberId)) {
			$status = $STATUS_SUSPENDED;
		}

		#set member's A_MEMBER_EXPIRATION to number of days since epoch
		#if member is not valid set expiration to 1
		my $memberExpiration = $data->getMemberAttributeValue(attrName => $A_MEMBER_EXPIRATION, member => $memberId);
		if($status eq $STATUS_VALID) {
			if(!$memberExpiration) {
				$memberExpiration = "";
			} else {
				my ($year, $month, $day) = unpack "A4xA2xA2", $memberExpiration;
				$memberExpiration = Delta_Days(1970, 1, 1, $year, $month, $day);
			}
		} else {
			$memberExpiration = 1;
		}

		unless(exists $memberAttributesByLogin->{$login}) {
			$memberAttributesByLogin->{$login} = {};
			$memberAttributesByLogin->{$login}->{$A_MEMBER_STATUS} = $status;
			$memberAttributesByLogin->{$login}->{$A_MEMBER_EXPIRATION} = $memberExpiration;
			$memberAttributesByLogin->{$login}->{$A_USER_FACILITY_LOGIN} = $data->getUserFacilityAttributeValue(attrName => $A_USER_FACILITY_LOGIN, member => $memberId);
			$memberAttributesByLogin->{$login}->{$A_USER_FACILITY_UID} = $data->getUserFacilityAttributeValue(attrName => $A_USER_FACILITY_UID, member => $memberId);
			$memberAttributesByLogin->{$login}->{$A_USER_FACILITY_GID} = $data->getUserFacilityAttributeValue(attrName => $A_USER_FACILITY_GID, member => $memberId);
			$memberAttributesByLogin->{$login}->{$A_USER_FIRST_NAME} = $data->getUserAttributeValue(attrName => $A_USER_FIRST_NAME, member => $memberId);
			$memberAttributesByLogin->{$login}->{$A_USER_LAST_NAME} = $data->getUserAttributeValue(attrName => $A_USER_LAST_NAME, member => $memberId);
			$memberAttributesByLogin->{$login}->{$A_USER_FACILITY_HOME_MOUNT_POINT} = $data->getUserFacilityAttributeValue(attrName => $A_USER_FACILITY_HOME_MOUNT_POINT, member => $memberId);
			$memberAttributesByLogin->{$login}->{$A_USER_FACILITY_SHELL} = $data->getUserFacilityAttributeValue(attrName => $A_USER_FACILITY_SHELL, member => $memberId);
		} else {
			my $m = $memberAttributesByLogin->{$login}; # $m is reference, so by modifying it we also modified $memberAttributesByLogin{$login}

			if($m->{$A_MEMBER_STATUS} eq $STATUS_SUSPENDED) { next; } #member is suspended - no change allowed

			if($status eq $STATUS_SUSPENDED) {
				$m->{$A_MEMBER_STATUS} = $STATUS_SUSPENDED;
				$m->{$A_MEMBER_EXPIRATION} = 1;
			} elsif($status eq $STATUS_VALID) {
				$m->{$A_MEMBER_STATUS} = $STATUS_VALID;
				if($m->{$A_MEMBER_EXPIRATION} eq "" || $memberExpiration eq "") {
					$m->{$A_MEMBER_EXPIRATION} = "";
				} else {
					$m->{$A_MEMBER_EXPIRATION} = $memberExpiration unless $m->{$A_MEMBER_EXPIRATION} > $memberExpiration;
				}
			}
		}
	}
}

#print data to files
#case-insensitive sort of logins
foreach my $memberLogin (sort {"\U$a" cmp "\U$b"} keys %$memberAttributesByLogin) {
	my $memberAttributes = $memberAttributesByLogin->{$memberLogin};
	print PASSWD $memberAttributes->{$A_USER_FACILITY_LOGIN}.":x:";
	print PASSWD $memberAttributes->{$A_USER_FACILITY_UID}.":";
	print PASSWD $memberAttributes->{$A_USER_FACILITY_GID}.":";

	my $userName = "";
	if($memberAttributes->{$A_USER_FIRST_NAME}) {
		$userName = unidecode($memberAttributes->{$A_USER_FIRST_NAME});
		$userName.= " " . unidecode($memberAttributes->{$A_USER_LAST_NAME}) if $memberAttributes->{$A_USER_LAST_NAME};
	} else {
		$userName.= unidecode($memberAttributes->{$A_USER_LAST_NAME}) if $memberAttributes->{$A_USER_LAST_NAME};
	}

	print PASSWD $userName . ":";
	print PASSWD $memberAttributes->{$A_USER_FACILITY_HOME_MOUNT_POINT}."/".$memberAttributes->{$A_USER_FACILITY_LOGIN}.":";
	print PASSWD $memberAttributes->{$A_USER_FACILITY_SHELL};
	print PASSWD "\n";

	print SHADOW $memberAttributes->{$A_USER_FACILITY_LOGIN}.":x::::::", $memberAttributes->{$A_MEMBER_EXPIRATION},":";
	print SHADOW "\n";
}
close(PASSWD);
close(SHADOW);
#####################################################
perunServicesInit::finalize;
