#!/bin/sh

# 
# Copyright 1999-2006 University of Chicago
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 


#
# grid-mapfile-delete-entry
#

prefix="${GLOBUS_LOCATION-/usr}"
exec_prefix="/usr"
sbindir="/usr/sbin"
bindir="/usr/bin"
includedir="${prefix}/include/globus"
datarootdir="${prefix}/share"
datadir="/usr/share"
libexecdir="${datadir}/globus"
sysconfdir="/etc"
sharedstatedir="/var/lib"
localstatedir="/var"

PROGRAM_NAME=`echo $0 | sed 's|.*/||g'`

PROGRAM_VERSION="10.19"

VERSION="10.19"

PACKAGE="globus_gss_assist"

DIRT_TIMESTAMP="1472841005"

DIRT_BRANCH_ID="85"

short_usage="$PROGRAM_NAME [-help] [-dn <DN>] [-ln <local name>] [-d] [-f file]"

long_usage() {

    cat >&2 <<EOF

${short_usage}

    $PROGRAM_NAME deletes one or more matching entries from the Grid mapfile.

    Options:
    -help, -usage           Displays help
    -version                Displays version
    -dn <DN>                Distinguished Name (DN) to delete
    -ln <local name>        Local Login Name (LN) to delete
    -dryrun, -d             Shows what would be done but will not delete the
                            entry
    -mapfile file, -f file  Path of gridmap file to be used

EOF
}

##############################################
Cleanup()
{
    if [ -f $DELETED_ENTRIES ] ; then
        rm $DELETED_ENTRIES
    fi

    if [ -f $CONSISTENCY_CHECK ] ; then
	rm $CONSISTENCY_CHECK
    fi

    if [ -f $GRID_MAP_FILE_COPY ] ; then
	rm $GRID_MAP_FILE_COPY
    fi

    if [ -f $NEW_GRID_MAP_FILE ] ; then
	rm $NEW_GRID_MAP_FILE
    fi

    chmod 644 $GRID_MAP_FILE
    if [ $? -ne 0 ] ; then
        echo "ERROR: Could not change mode of $GRID_MAP_FILE back to 644" >&2
        exit 1
    fi

}

removeLNs ()
{
    parseLine
    aux_line=$existing_ln

    for name in $ln ; do
	modif_line=$aux_line
	aux_line=`echo $aux_line | sed -e "s/,$name,/,/" -e "s/,$name$//" -e "s/^$name,//" -e "s/^$name$//"`
	if test ! "$modif_line" = "$aux_line" ; then
	    removed_map="$removed_map $name"
	    removed_map_count=`expr $removed_map_count + 1`
	fi
    done

    if test -n "$aux_line" ; then
	aux_line="\"$existing_dn\" $aux_line"
    fi
}

parseLine()
{
    # Check for double quote delimitor
    delim=`echo $line | cut -c1`
    if [ "X$delim" = "X\"" ]; then
        # DN is double quote delimited
        # Check for terminating double quote
        term_check=`echo $line | cut -c2- | grep \"`
        if [ -z "$term_check" ]; then
            echo "The following entry is missing a closing double quote"
            echo "$line"
            Cleanup
            exit 1
        fi
        existing_dn=`echo $line | cut -f2 -d\"`
        existing_ln=`echo $line | cut -f3 -d\"`
    else
        # No double quote delimitor on DN
        existing_dn=`echo $line | sed -e 's/\([^	 ]*\)[	 ]*.*/\1/'`
	existing_ln=`echo $line | sed -e 's/[^	 ]*[	 ]*\(.*\)/\1/'`
    fi

    existing_ln=`echo ${existing_ln} | sed -e 's/[	 ]*//g'`
}

# Main Logic

secconfdir="/etc/grid-security"
GRID_MAP_FILE=${GRIDMAP-${secconfdir}/grid-mapfile}
ECHO_DRYRUN=:
deleted_entries=0
removed_map_count=0

# Parse command line arguments
globus_args_short_usage()
{
    cat 1>&2 <<-EOF
	Syntax : ${short_usage}

	Use -help to display full usage.
	EOF
}

globus_args_option_error()
{
    cat 1>&2 <<-EOF
	ERROR: option $1 : $2
	EOF
    globus_args_short_usage
    exit 1
}

globus_args_unrecognized_option()
{
    globus_args_option_error $1 "unrecognized option"
    exit 1
}	

while [ -n "$1" ]; do
    case "$1" in
	-help | -h | --help | -usage | --usage)
	    long_usage
	    exit 0
	    ;;
        -version|--version)
            if [ "X${PROGRAM_NAME}" != "X" -a \
                  "X${PROGRAM_VERSION}" != "X" ]; then
                echo "${PROGRAM_NAME}: ${PROGRAM_VERSION}"
            elif [ "X${PACKAGE}" != "X" -a \
                   "X${VERSION}" != "X" ]; then
                echo "${PACKAGE}: ${VERSION}"
            else
                echo "No version information available."
            fi
            exit 0
            ;;
        -versions|--versions)
            __AT__=@
            if [ -n "${PACKAGE}" -a -n "${VERSION}" -a \
                 -n "${DIRT_TIMESTAMP}" -a -n "${DIRT_BRANCH_ID}" -a \
                 "X${DIRT_TIMESTAMP}" != "X${__AT__}DIRT_TIMESTAMP${__AT__}" -a \
                 "X${DIRT_BRANCH_ID}" != "X${__AT__}DIRT_BRANCH_ID${__AT__}" ];
            then
                echo "${PACKAGE}: ${VERSION} (${DIRT_TIMESTAMP}-${DIRT_BRANCH_ID})"
            else
                echo "No DiRT information available."
            fi
            exit 0;
            ;;
        -dn) 
	    shift
            if [ $# -ge 1 ] ; then
		dn=$1
                shift
	    else
                globus_args_option_error "-dn" "needs a DN argument"
	    fi
            ;;
        -ln )
            shift
            if [ $# -ge 1 ] ; then
		ln=$1
		shift
		if [ $# -ge 1 ] ; then
		    while [ "$1" != "-dn" -a "$1" != "-d" \
                        -a "$1" != "-dryrun" -a "$1" != "-f" \
                        -a "$1" != "-mapfile" ] ; do
                      ln=$ln" "$1
                      shift
                      if [ $# -eq 0 ] ; then
                          break
                      fi
		    done
		fi
	    else
                globus_args_option_error "-ln" "needs a list of user login names"
		exit 1
	    fi
	    ;;
        -d | -dryrun )
	    ECHO_DRYRUN=echo
	    shift
	    ;;
        -f | -mapfile )
	    opt=$1
	    shift
            if test $# -lt 1 ; then
                globus_args_option_error "$opt" "${opt} requires a argument"
            fi
	    GRID_MAP_FILE=$1
	    shift
	    ;;
        
        * )
	    globus_args_unrecognized_option "$1"
	    ;;
    esac

done

secure_tmpdir="`dirname \"${GRID_MAP_FILE}\"`"

if test ! \( -r "${secure_tmpdir}" -a -w "${secure_tmpdir}" \) ; then
    echo "ERROR: This script requires read/write permissions in ${secure_tmpdir}" >&2
    exit 1
fi 

GRID_MAP_FILE_COPY=${secure_tmpdir}/.mapfile.copy.$$
NEW_GRID_MAP_FILE=${secure_tmpdir}/.new_mapfile.$$
CONSISTENCY_CHECK=${secure_tmpdir}/.consistency_check.$$
DELETED_ENTRIES=${secure_tmpdir}/.deleted_entries.$$

trap Cleanup 1 2 3 6 9 13 15

# Verify mapfile existence

if [ ! -f $GRID_MAP_FILE ] ; then
    globus_args_option_error "$opt" "\"${GRID_MAP_FILE}\" does not exist"
    exit 1
fi

if [ ! -r $GRID_MAP_FILE ] ; then
    globus_args_option_error "$opt" "\"${GRID_MAP_FILE}\" is not readable."
    exit 1
fi

if [ ! -w $GRID_MAP_FILE ] ; then
    globus_args_option_error "$opt" "\"${GRID_MAP_FILE}\" is not writeable."
    exit 1
fi

echo "Modifying ${GRID_MAP_FILE} ..."

# Make a copy of production map file for comparison to original later

cp $GRID_MAP_FILE $GRID_MAP_FILE_COPY
if [ $? -ne 0 ] ; then
    echo "ERROR: Could not make a copy of $GRID_MAP_FILE" >&2
    Cleanup
    exit 1
fi

# Change mode of existing map file to read only (logical UNIX lock)

chmod 400 $GRID_MAP_FILE
if [ $? -ne 0 ] ; then
    echo "ERROR: Could not change mode of $GRID_MAP_FILE" >&2
    Cleanup
    exit 1
fi

if [ -n "$dn" -a -z "$ln" ] ; then
    requested_dn=$dn
    $ECHO_DRYRUN "Searching for entries containing the Distinguished Name"
    $ECHO_DRYRUN "$requested_dn"
elif [ -n "$ln" -a -z "$dn" ] ; then
    requested_ln=$ln
    $ECHO_DRYRUN "Searching for entries containing any of the Local Name(s)"
    $ECHO_DRYRUN "$requested_ln"
elif [ -n "$ln" -a -n "$dn" ] ; then
    requested_entry="\"$dn\" $ln"
    $ECHO_DRYRUN "Searching for entries containing the Distinguished Name and any of the Local Name(s)"
    $ECHO_DRYRUN "$requested_entry"
fi

# Create new map file

touch $NEW_GRID_MAP_FILE

while read line || test ! -z "${line}" ; do
    # Match ONLY DNs
		
    if test -n "$requested_dn" -a -z "$requested_ln" ; then
	
	parseLine 
	
	if test ! "$requested_dn" = "$existing_dn" ; then
	    echo $line >> $NEW_GRID_MAP_FILE
	else
	    echo "Deleting entry: $line"
	    deleted_entries=`expr $deleted_entries + 1`
	fi
	
    # Match ANY LNs
	
    elif test -z "$requested_dn" -a -n "$requested_ln" ; then
	found_match=""
	for name in $ln ; do
	    found_match=`echo $line | grep $name`
	    if [ -n "$found_match" ] ; then
		break
	    fi
	done
	
	if [ -z "$found_match" ] ; then
	    echo $line >> $NEW_GRID_MAP_FILE
	else
	    removeLNs
	    if test -n "$aux_line" ; then
		echo "Current entry: $aux_line"
		echo $aux_line >> $NEW_GRID_MAP_FILE
	    else
		echo "No local mappings remained, deleting entry: $line"
	    fi
	fi
	
    # Match DN and list
	
    elif [ -n "$requested_entry" ] ; then
	
	parseLine 
	
	if test ! "$dn" = "$existing_dn" ; then
	    echo $line >> $NEW_GRID_MAP_FILE
	else
	    removeLNs
	    if test -n "$aux_line" ; then
		if test -n "$removed_map" ; then
		    echo "Current entry: $aux_line"
		fi
		echo $aux_line >> $NEW_GRID_MAP_FILE
	    else
		echo "No local mappings remained, deleting entry: $line"
	    fi
	fi
    fi
    
    echo $deleted_entries >  $DELETED_ENTRIES
    
done < $GRID_MAP_FILE_COPY

# Exit if Dry Run option was requested"
if [ -f "$DELETED_ENTRIES" ]; then
    deleted_entries=`cat $DELETED_ENTRIES`
else
    deleted_entries=0
fi

if [ "$ECHO_DRYRUN" = "echo" ] ; then
    if [ "$deleted_entries" -ne  0 ] ; then
	if [ "$deleted_entries" -eq  1 ] ; then
	    echo "($deleted_entries) entry would have been deleted"
	else
	    echo "($deleted_entries) entries would have been deleted"
	fi
    fi
    echo "Since ( dryrun, -d ) option was used no actions were carried out"
    Cleanup
    exit 0
fi

# Replace existing map file with new mapfile

if [ "$deleted_entries" -ne  0 ] || [ "$removed_map_count" -ne  0 ] ; then
    # Verify that no changes to original map file
    # during the execution of this program

    diff $GRID_MAP_FILE_COPY $GRID_MAP_FILE > $CONSISTENCY_CHECK
    if  [ -s $CONSISTENCY_CHECK ] ; then
	echo "ERROR: $GRID_MAP_FILE has changed since this program started" >&2
	echo "No changes will be made." >&2
	Cleanup
	exit 1
    else
      	# make copy of old map file
	
	cp $GRID_MAP_FILE_COPY $GRID_MAP_FILE.old
	if [ $? -ne 0 ] ; then
	    echo "ERROR: Could not create a copy of $GRID_MAP_FILE" >&2
	fi
	
	# Restore old permissions on map file
	
	chmod 644 $GRID_MAP_FILE
	
	mv $NEW_GRID_MAP_FILE $GRID_MAP_FILE
	
	if [ $? -ne 0 ] ; then
	    echo "ERROR: Could not create a new $GRID_MAP_FILE" >&2
	    Cleanup
	    exit 1
	fi
	
	removed_count=0
	ignored_count=0
	for name in $ln ; do
	    is_present=`echo $removed_map | grep "$name"`
	    if  test -n "$is_present" ; then
		removed_count=`expr $removed_count + 1`
		printable_removed="$printable_removed $name"
	    else
		printable_ignored="$printable_ignored $name"
		ignored_count=`expr $ignored_count + 1`
	    fi
	done
	
	if  test -n "$printable_removed" ; then
	    printable_removed="`echo $printable_removed | sed -e "s/ /, /g"`"
	    printable_removed=": ($printable_removed)"
	fi
	if  test -n "$printable_ignored" ; then
	    printable_ignored="`echo $printable_ignored | sed -e "s/ /, /g"`"
	    printable_ignored=": ($printable_ignored)"
	fi
	
	if [ "$removed_count" -ne 0 ] ; then
	    if [ "$removed_count" -eq 1 ] ; then
		echo "($removed_count) mapping removed$printable_removed, ($ignored_count) not present and ignored$printable_ignored"
	    else
		echo "($removed_count) mappings removed$printable_removed, ($ignored_count) not present and ignored$printable_ignored"
	    fi
	fi
	
	if [ "$deleted_entries" -eq  1 ] ; then
	    echo "($deleted_entries) entry deleted"
	else
	    echo "($deleted_entries) entries deleted"
	fi
    fi
else
    echo "ERROR: No such entry/mapping exists"
    Cleanup
    exit 1
fi

Cleanup
exit 0
