#!/bin/sh

set -e

CONFFILE="/etc/nslcd.conf"
OCONFFILE="/etc/nss-ldapd.conf"

# fall back to old configfile if new one isn't present but old one is
[ ! -f "$CONFFILE" ] && [ -f "$OCONFFILE" ] && CONFFILE="$OCONFFILE"

# source debconf library.
. /usr/share/debconf/confmodule
db_version 2.0
db_capb backup

#
# This is the fist part of the script. In this part an attempt
# is made to get or guess the current configuration. This information
# is used later on to prompt the user and to provide a sensible
# default.
#

# read a configuration value from the specified file
# (it takes care in not overwriting a previously written value)
read_config()
{
  debconf_param="$1"
  cfg_param="$2"
  # overwrite debconf value if different from config file
  db_get "$debconf_param"
  debconf_value="$RET"
  cfgfile_value=`sed -n 's/^'"$cfg_param"'[[:space:]]*\([^[:space:]].*[^[:space:]]\)[[:space:]]*$/\1/ip' "$cfgfile" | tail -n 1`
  [ -n "$cfgfile_value" ] && [ "$debconf_value" != "$cfgfile_value" ] && db_set "$debconf_param" "$cfgfile_value"
  # we're done
  return 0
}

# figure out the system's domain name
get_domain()
{
  domain=`hostname --domain` || true
  [ -z "$domain" ] && domain=`hostname --nis | grep '\.'` || true
  [ -z "$domain" ] && domain=`hostname --fqdn | sed -n 's/^[^.]*\.//p'` || true
  [ -z "$domain" ] && domain=`sed -n 's/^ *\(domain\|search\) *\([^ ]*\) *$/\2/p' /etc/resolv.conf | head -n 1` || true
  echo "$domain"
}

# find a LDAP server URI by trying DNS and normal hostname lookups
guess_ldap_uri()
{
  # see if ldap server exists on localhost and is listening on ldapi://
  if [ -e /var/run/slapd/ldapi ]
  then
    echo "ldapi:///"
    return
  fi
  # try to lookup _ldap._tcp SRV records
  if [ -n "$domain" ]
  then
    server=`host -N 2 -t SRV _ldap._tcp.$domain 2> /dev/null | grep -v NXDOMAIN | awk '{print $NF}' | head -1 | sed 's/\.$//'` || true
    if [ -n "$server" ]
    then
      echo "ldap://$server/"
      return
    fi
  fi
  # fall back to name lookups
  if [ -z "$ldap_uri" ]
  then
    # try unqualified hostname lookup
    server=`getent hosts ldap` || true
    [ -z "$server" ] && server=`getent hosts dirhost` || true
    # try qualified hostname
    if [ -n "$domain" ] && [ -z "$server" ]
    then
      server=`getent hosts ldap."$domain"` || true
      [ -z "$server" ] && server=`getent hosts dirhost."$domain"` || true
    fi
    # turn into URI with IP address
    if [ -n "$server" ]
    then
      # extract IP address from host entry and quote IPv6 address
      ip=`echo $server | sed 's/[[:space:]].*//;s/^\(.*:.*\)$/[\1]/'`
      echo "ldap://$ip/"
    fi
  fi
}

# guess the LDAP search base by performing LDAP searches on the
# found server
query_search_base()
{
  ldap_uri="$1"
  # first try the default naming context
  context=`ldapsearch -LLL -H "$ldap_uri" -x -b '' -s base defaultNamingContext 2>/dev/null | sed -n 's/^defaultNamingContext: //pi'` || true
  if [ -n "$context" ]
  then
    echo "$context"
    return
  fi
  # go over naming contexts, pick the first one with posixAccount or
  # posixGroup objects in it
  for context in `ldapsearch -LLL -H "$ldap_uri" -x -b ''  -s base namingContexts 2>/dev/null | sed -n 's/^namingContexts: //pi'`
  do
    # search the context
    found=`ldapsearch -LLL -H "$ldap_uri" -x -b "$context" -s sub -z 1 '(|(objectClass=posixAccount)(objectclass=posixGroup))' dn 2>/dev/null` || true
    if [ -n "$found" ]
    then
      echo $context
      return
    fi
  done
}

# check the system (non-LDAP configuration files) for some
# reasonable defaults
parsesys()
{
  # guess domain based on system information
  domain=`get_domain`
  # guess ldap server URI
  db_get nslcd/ldap-uris
  if [ -z "$RET" ]
  then
    ldap_uri=`guess_ldap_uri "$domain"`
    [ -n "$ldap_uri" ] && db_set nslcd/ldap-uris "$ldap_uri"
  else
    # only get first URI from any stored (preseeded) value
    ldap_uri=`echo "$RET" | sed -n 's/[[:space:]].*//'`
  fi
  # guess search base
  db_get nslcd/ldap-base
  if [ -z "$RET" ]
  then
    # try to find the search base from the found URI
    [ -n "$ldap_uri" ] && search_base=`query_search_base "$ldap_uri"`
    # try to use the domain name to build the default base
    if [ -z "$search_base" ] && [ -n "$domain" ]
    then
      search_base=`echo "$domain" | sed 's/^/dc=/;s/\./,dc=/g'`
    fi
    [ -n "$search_base" ] && db_set nslcd/ldap-base "$search_base"
  fi
  # we're done
  return 0
}

# parse a LDAP-like configuration file
parsecfg()
{
  cfgfile="$1"
  # check existance
  [ -f "$cfgfile" ] || return 0
  # find uri/host/port combo
  db_get nslcd/ldap-uris
  if [ -z "$RET" ]
  then
    uris=`sed -n 's/^uri[[:space:]]*//ip' "$cfgfile" | tr '\n' ' '`
    if [ -z "$uris" ]
    then
      hosts=`sed -n 's/^host[[:space:]]*//ip' "$cfgfile"`
      port=`sed -n 's/^port[[:space:]]*//ip' "$cfgfile" | tail -n 1`
      for host in $hosts
      do
        if [ -z "$port" ] || (echo "$host" | grep -q ':' )
        then
          uris="$uris ldap://$host/"
        else
          uris="$uris ldap://$host:$port/"
        fi
      done
    fi
    [ -n "$uris" ] && db_set nslcd/ldap-uris "$uris"
  fi
  # read simple options
  read_config nslcd/ldap-base base
  read_config nslcd/ldap-binddn binddn
  read_config nslcd/ldap-bindpw bindpw
  read_config nslcd/ldap-sasl-mech sasl_mech
  read_config nslcd/ldap-sasl-realm sasl_realm
  read_config nslcd/ldap-sasl-authcid sasl_authcid
  read_config nslcd/ldap-sasl-authzid sasl_authzid
  read_config nslcd/ldap-sasl-secprops sasl_secprops
  read_config nslcd/ldap-sasl-krb5-ccname krb5_ccname
  # check ssl option
  db_get nslcd/ldap-starttls
  if [ -z "$RET" ]
  then
    if grep -qi '^ssl[[:space:]]*start_*tls' "$cfgfile"
    then
      db_set nslcd/ldap-starttls "true"
    elif grep -qi '^ssl[[:space:]]' "$cfgfile"
    then
      db_set nslcd/ldap-starttls "false"
    fi
  fi
  # check reqcert option
  db_get nslcd/ldap-reqcert
  if [ -z "$RET" ]
  then
    reqcert=`sed -n 's/^tls_\(reqcert\|checkpeer\)[[:space:]]*\([^[:space:]]*\)[[:space:]]*$/\2/ip' "$cfgfile" | tail -n 1`
    # normalise value
    reqcert=`echo "$reqcert" | tr 'A-Z' 'a-z' | sed 's/^no$/never/;s/^yes$/demand/'`
    [ -n "$reqcert" ] && db_set nslcd/ldap-reqcert "$reqcert"
  fi
  # we're done
  return 0
}

# fill our defaults with the current configuration if available
# and fall back to guessing the config from some other system files
if [ -f "$CONFFILE" ]
then
  # parse current configuration
  parsecfg "$CONFFILE"
else
  # first match wins
  parsecfg /etc/libnss-ldap.conf
  parsecfg /etc/pam_ldap.conf
  parsecfg /etc/ldap/ldap.conf
  parsecfg /etc/ldap.conf
  parsesys
  # fallback default values
  db_get nslcd/ldap-uris
  [ -z "$RET" ] && db_set nslcd/ldap-uris "ldap://127.0.0.1/"
  db_get nslcd/ldap-base
  [ -z "$RET" ] && db_set nslcd/ldap-base "dc=example,dc=net"
fi

# fallback for starttls option
db_get nslcd/ldap-starttls
[ -z "$RET" ] && db_set nslcd/ldap-starttls "false"

# deduce auth-type from available information
db_get nslcd/ldap-auth-type
if [ -z "$RET" ]
then
  db_get nslcd/ldap-sasl-mech
  sasl_mech="$RET"
  db_get nslcd/ldap-binddn
  binddn="$RET"
  if [ -n "$sasl_mech" ]
  then
    db_set nslcd/ldap-auth-type "SASL"
  elif [ -n "$binddn" ]
  then
    db_set nslcd/ldap-auth-type "simple"
  else
    db_set nslcd/ldap-auth-type "none"
  fi
fi

#
# This is the second part of the script. In this part the configurable
# settings will be presented to the user for approval. The postinst
# will finaly perform the actual modifications.
#

state="server"
while [ "$state" != "done" ]
do
  case "$state" in
  server)
    # ask about server configuration
    db_input high nslcd/ldap-uris || true
    db_input high nslcd/ldap-base || true
    # ask the questions, go to the next question or exit
    state="authtype"
    db_go || exit 1
    # TODO: add error checking on options
    ;;
  authtype)
    # ask for authentication type
    db_input medium nslcd/ldap-auth-type || true
    # ask the question, go to the next question or back
    state="authentication"
    db_go || state="server"
    ;;
  authentication)
    # check which questions to ask, depending on the authentication type
    db_get nslcd/ldap-auth-type
    case "$RET" in
    none)
      # anonymous bind, nothing to ask (clear password)
      db_set nslcd/ldap-bindpw ""
      state="starttls"
      ;;
    simple)
      # ask for binddn and bindpw
      db_input medium nslcd/ldap-binddn || true
      db_input medium nslcd/ldap-bindpw || true
      state="starttls"
      ;;
    SASL)
      # ask about SASL mechanism (other SASL questions depend on this)
      db_input medium nslcd/ldap-sasl-mech || true
      state="sasloptions"
      ;;
    *)
      exit 1
      ;;
    esac
    db_go || state="authtype"
    ;;
  sasloptions)
    # get SASL mech
    db_get nslcd/ldap-sasl-mech
    sasl_mech="$RET"
    # ask SASL questions
    db_input medium nslcd/ldap-sasl-realm || true
    if [ "$sasl_mech" != "GSSAPI" ]
    then
      db_input medium nslcd/ldap-sasl-authcid || true
      db_input medium nslcd/ldap-bindpw || true
    fi
    db_input medium nslcd/ldap-sasl-authzid || true
    db_input medium nslcd/ldap-sasl-secprops || true
    if [ "$sasl_mech" = "GSSAPI" ]
    then
      # have a default for ldap-sasl-krb5-ccname
      db_get nslcd/ldap-sasl-krb5-ccname
      [ -z "$RET" ] && db_set nslcd/ldap-sasl-krb5-ccname "/var/run/nslcd/nslcd.tkt"
      db_input low nslcd/ldap-sasl-krb5-ccname || true
    fi
    # ask the question, go to the next question or back
    state="starttls"
    db_go || state="authentication"
    ;;
  starttls)
    # check if ldaps:// URL's are used
    db_get nslcd/ldap-uris
    uris="$RET"
    if (echo "$uris" | grep -q 'ldaps://')
    then
      # ldaps: URI defined, don't ask about StartTLS
      db_set nslcd/ldap-starttls "false"
    else
      # ask whether to use StartTLS
      db_input medium nslcd/ldap-starttls || true
    fi
    # ask the question, go to the next question or back
    # (we go back to authtype because the previous questions were optional)
    state="reqcert"
    db_go || state="authtype"
    ;;
  reqcert)
    # check if ldaps:// URL's are used
    db_get nslcd/ldap-uris
    uris="$RET"
    # check if StartTLS is used
    db_get nslcd/ldap-starttls
    starttls="$RET"
    if (echo "$uris" | grep -q 'ldaps://') || [ "$starttls" = "true" ]
    then
      # ask whether to do certificate validation
      db_input high nslcd/ldap-reqcert || true
    else
      db_set nslcd/ldap-reqcert ""
    fi
    # ask the question, go to the next question or back
    # (we go back to authtype because the previous questions were optional)
    state="done"
    db_go || state="authtype"
    ;;
  esac
done

exit 0