#!/bin/sh

alterator_api_version=1
. alterator-sh-functions

# unset this to disable debug
DEBUG=1

# directory with desktop-files
DESKTOPDIR=/etc/alterator/groups

# get min and max gids from /etc/login.defs
get_minmax_gid(){
  awk '/^GID_MIN/{gid_min=$2};
       /^GID_MAX/{gid_max=$2};
       /^UID_MIN/{uid_min=$2};
       /^UID_MAX/{uid_max=$2};
       END{print gid_min, gid_max, uid_min, uid_max}'\
    < /etc/login.defs
}
LOGIN_DEFS_DATA=$(get_minmax_gid)
GID_MIN=$(echo $LOGIN_DEFS_DATA | cut -f1 -d' ')
GID_MAX=$(echo $LOGIN_DEFS_DATA | cut -f2 -d' ')
UID_MIN=$(echo $LOGIN_DEFS_DATA | cut -f3 -d' ')
UID_MAX=$(echo $LOGIN_DEFS_DATA | cut -f4 -d' ')

##############################################################################

gid_of(){
  [ -n "$1" ] || return
  echo $(getent group $1 | cut -f3 -d:)
}

uid_of(){
  [ -n "$1" ] || return
  echo $(getent passwd $1 | cut -f3 -d:)
}

# is group with gid $1 a system group
# (gid < GID_MIN  or  gid > GID_MAX)
test_gid_system(){
  [ -n "$1" ] || return
  echo $(( ($1<$GID_MIN) || ($1>$GID_MAX)))
}
test_group_system(){
  [ -n $1 ] || return
  test_gid_system $(gid_of $1)
}

# is group with gid $1 a primary group?
test_gid_primary(){
  [ -n "$1" ] || return
  getent passwd | cut -f4 -d: | grep $1 > /dev/null
  echo $?
}
test_group_primary(){
  [ -n $1 ] || return
  test_gid_primary $(gid_of $1)
}

# same tests for users
test_uid_system(){
  [ -n "$1" ] || return
  echo $(( ($1<$UID_MIN) || ($1>$UID_MAX)))
}
test_user_system(){
  [ -n $1 ] || return
  test_uid_system $(uid_of $1)
}


##############################################################################

skip_empty=""
skip_system=""
skip_primary="#t"

# list system groups (according to $skip_* settings)
on_list(){
  write_debug "list groups\n"
  local IFS=:
  [ "$skip_empty"   = "#t" ] && write_debug "skipping empty groups\n"
  [ "$skip_system"  = "#t" ] && write_debug "skipping system groups\n"
  [ "$skip_primary" = "#t" ] && write_debug "skipping primary groups\n"
  getent group | sort |
  while read name password gid users; do
    [ "${name#_}" = "$name" ] &&\
    [ "$skip_empty"   != "#t" -o -n "$users" ] &&\
    [ "$skip_system"  != "#t" -o "$(test_gid_system $gid)" = 0 ] &&\
    [ "$skip_primary" != "#t" -o "$(test_gid_primary $gid)" = 1 ] &&\
    write_enum_item "$name"
  done
  return 0
}

# read group $in_name
on_read(){
  write_debug "read data for group $in_name\n"
  local IFS=:
  getent group "$in_name" | (
    read name password gid users
    if [ -f "$DESKTOPDIR/$name.desktop" ]; then
      write_string_param label "$(alterator-dump-desktop -v lang="$in_language" -v out="Name" "$DESKTOPDIR/$name.desktop")"
      write_string_param comment "$(alterator-dump-desktop -v lang="$in_language" -v out="Comment" "$DESKTOPDIR/$name.desktop")"
    fi
    write_string_param name   "$name"
    write_string_param gid    "$gid"
    write_string_param users  "$(echo $users | sed "s/,/\n/g")"
    write_string_param label  "$label"
    write_bool_param   protected "$(test_group_system "$name" || test_group_primary "$name")"
  )
  return 0
}

# create group
on_new(){
  write_debug "adding group $in_name\n"

  if [ -z "$in_name" ]; then
    write_error "`_ "Can't add group with empty name"`" #'
    return 1
  fi

  local ans=$(groupadd "$in_name" 2>&1)
  if [ -n "$ans" ]; then
    write_error "`_ "Can't add group"`: ${ans##*:}" #'
    return 1
  fi
  return 0
}

# delete group
on_delete(){
  write_debug "deleting group $in_name\n"
  if [ -z "$in_name" ]; then
    write_error "`_ "Can't delete group with empty name"`" #'
    return 1
  fi
  local ans=$(groupdel "$in_name" 2>&1)
  if [ -n "$ans" ]; then
    write_error "`_ "Can't delete group"`: ${ans##*:}" #'
    return 1
  fi
  return 0
}

# modify users in group
on_write(){
  # get old user list
  local IFS=:

  # users on input can be separated by newline, spaces, commas
  local new_users=$(echo "$in_users" | tr "\n " ",," | sed 's/,,*/,/g')
  write_debug "change users in group $in_name: $users -> $new_users\n"

  if [ -z "$in_name" ]; then
    write_error "`_ "Can't modify group with empty name"`" #'
    return 1
  fi

  getent group "$in_name" | (
    read name password gid users;

    local IFS=','

    # remove old users
    for ou in $users; do
      local eq=
      for nu in $new_users; do
        [ "$nu" == "$ou" ] && eq=1
      done
      [ -z "$eq" ] && user_del_from_gr "$ou" "$in_name"
    done

    # add new users
    for nu in $new_users; do
      local eq=
      for ou in $users; do
        [ "$nu" == "$ou" ] && eq=1
      done
      [ -z "$eq" ] && user_add_to_gr "$nu" "$in_name"
    done
  )
}

user_add_to_gr(){
  write_debug "add user $1 to $2\n"
  local mygroups=$2
  local IFS=' '
  local ans=
  for g in $(id -nG $1); do mygroups="$mygroups,$g"; done
  ans=$(usermod -G $mygroups $1 2>&1) ||\
    write_error "`_ "Can't add user"` $1 `_ "to group"` $2: ${ans##*:}" #'
}

user_del_from_gr(){
  write_debug "remove user $1 from $2\n"
  local mygroups=
  local IFS=' '
  local ans=
  for g in $(id -nG $1); do
    [ "$g" != "$2" ] && mygroups="${mygroups:+$mygroups,}$g"; 
  done
  ans=$(usermod -G $mygroups $1 2>&1) ||\
    write_error "`_ "Can't remove user"` $1 `_ "from group"` $2: ${ans##*:}"  #'
}

on_set_skippings(){
  write_debug "set skip_empty=$in_skip_empty skip_system=$in_skip_system skip_primary=$in_skip_primary\n"
  skip_empty=$in_skip_empty;
  skip_system=$in_skip_system;
  skip_primary=$in_skip_primary;
}

##############################################################################
##### Message loop
##############################################################################

on_message() {
  case "$in_action" in
    list)            on_list   ;;
    read)            on_read   ;;
    write)           on_write  ;;
    new)             on_new    ;;
    delete)          on_delete ;;
    set_skippings)   on_set_skippings ;;
  esac
}

message_loop
