Date: Thu, 26 Nov 1998 09:39:52 +0000
From: David M Walker <davidw@datamgmt.com>

Thanks to Philip and others I now have my ISP style config built and
therefore am posting the final configuration fragments to the list in
case anyone else wants to do a similar thing.

Date: Mon, 26 Jul 1999 00:02:14 +0100
From: David M Walker <davidw@datamgmt.com>

Some time ago I posted ISP nstyle configuration C011 and continue
to get mails of the form:

>   But can you please explain me how do the clients get their
>   email if it's placed in many directories and the password
>   are stored in "private" passwd files. The popper is not able
>   to understand it. What approach do you use?

The way I approach this is described after the configuration.


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

# This configuration fragment is for use with an ISP type solution
# Each client has their own directory that contains their own
# editable passwd, alias etc type files. Furthemore if they create
# a local user then that user can also have a a .forward file and/or
# a .autoreply file.
#
# Files and Directories
# Password file for a domain
#	/clients/${domain}/etc/passwd
# Alias file for a domain
#	/clients/${domain}/etc/aliases
# Directory where users mail for a domain is stored
#	/clients/${domain}/mail/
# Location of file with a list of domains
#	/clients/utils/data/domains

######################################################################
#                      TRANPORTS CONFIGURATION                       #
######################################################################

# This transport is used for local delivery to user mailboxes.

virtual_localdelivery:
  driver = appendfile
  file = /clients/${domain}/mail/${local_part}
  user = ${lookup{$local_part}lsearch{/etc/passwd}{$value}{exim}}
  group = mail
  mode = 0660

# This transport is used to handly autoreplys

auto_transport:
  driver = autoreply
  from = $local_part@$domain 
  to = $sender_address
  subject = "Reply re: $header_subject:"
  file =
"${extract{5}{:}{${expand:${lookup{$local_part}lsearch{/clients/${domain}/etc/passwd}{$value}}}}}/.autoreply"
  user = exim
      
# This transport is used for handling pipe addresses generated by alias
# or .forward files. 

address_pipe:
  driver = pipe
  return_output

# This transport is used for handling file addresses generated by alias
# or .forward files.

address_file:
  driver = appendfile

# This transport is used for handling file addresses generated by alias
# or .forward files if the path ends in "/".

address_directory:
  driver = appendfile
  no_from_hack
  prefix = ""
  suffix = ""

# This transport is used for handling autoreplies generated by the
filtering
# option of the forwardfile director.

address_reply:
  driver = autoreply

# This transport is used for delivering messages over SMTP connections.

remote_smtp:
  driver = smtp
  command_timeout = 1m,
  connect_timeout = 10s
  
end

######################################################################
#                      DIRECTORS CONFIGURATION                       #
######################################################################

# Handles .autoreply files

auto_director:
  driver = smartuser
  transport = auto_transport
  require_files =
root:${extract{5}{:}{${expand:${lookup{$local_part}lsearch{/clients/${domain}/etc/passwd}{$value}}}}}/.autoreply
  condition = ${if eq{$sender_address}{}{no}{yes}}
  unseen

# Handles any .forward files

userforward:
  driver = forwardfile
  check_local_user = false
  #file_directory =
"${extract{5}{:}{${expand:${lookup{$local_part}lsearch{/clients/${domain}/etc/passwd}{$value}}}}}"
  #file = .forward
  file =
"${extract{5}{:}{${expand:${lookup{$local_part}lsearch{/clients/${domain}/etc/passwd}{$value}}}}}/.forward"
  user = root
  no_verify
  check_ancestor
  filter

# This director matches local user mailboxes.

virtual_localuser:
   driver = aliasfile
   transport = virtual_localdelivery
   domains = lsearch;/clients/utils/data/domains
   file = /clients/${domain}/etc/passwd
   search_type = lsearch

# This director matches anything in the aliases

virtual_alias:
   driver = aliasfile
   domains = lsearch;/clients/utils/data/domains
   file = /clients/${domain}/etc/aliases
   search_type = lsearch*
   qualify_preserve_domain 

end
######################################################################


Our domains each have a unique last number from their IP address
in the form x.y.z.222 etc. but a simple serial number would do.
We prefix this domain number with an 'm' because it's a bad thing 
to create a username that starts with a digit. The 'm' stands for
nothing special - originally I think it meant mail.

As a result we get a username of the form m222.dwalker

We now use the shell script below to create the user, setup
symbolic links from /var/spool/mail/m222.dwalker to the user's
account directory etc. This is either called from a web page via
ssl or an ssh login.

Therefore mail sent to dwalker@datamgmt.com will be accessable
from a pop or imap account called m222.dwalker and uniqueness
across multiple domains is preserved.


#!/bin/ksh -

PATH=${PATH}:/usr/sbin; export PATH

# General Configuration
export PROGNAME=`basename $0`
export POPGID=198
export SHELL=/bin/false
export SYSPASS=/etc/passwd
export SYSSHAD=/etc/shadow
export PREFIX=m

# Global Parameters
export DOMAIN=""
export DOMGID=""
export DOMPASS=""
export DOMROOT=""
export DOMUID=""
export DOMUSER=""
export HOMEDIR=""
export MAILBOX=""
export MAILLNK=""
export POPUSER=""
export STRNGTST=""

fn_check_exit()
{
ESTATUS=$1
if [ "${ESTATUS}" != 0 ]
then
   echo "Command exited with non-zero value (${ESTATUS})"
   exit ${ESTATUS}
else
   echo "Command successful"
fi
}

fn_getdomain()
{
DOMUID=$(id -u ${USER})
DOMGRP=${USER}
. getdomain ${DOMGRP}

# Derived Variables
DOMROOT=/monza/clients/${DOMAIN}
POPUSER=${PREFIX}${DOMUID}.${DOMUSER}
DOMGID=${DOMUID}
DOMPASS=${DOMROOT}/etc/passwd
HOMEDIR=${DOMROOT}/users/${DOMUSER}
MAILBOX=${DOMROOT}/mail/${DOMUSER}
MAILLNK=/var/spool/mail/${POPUSER}
}

fn_mkpasswd()
{
STRNGTST=`grep "^${POPUSER}:" ${SYSPASS}`
if [ -z "${STRNGTST}" ]
then
   echo "Making ${SYSPASS} entry"
   useradd -u ${DOMUID} -g ${DOMGRP} -d ${HOMEDIR} -s ${SHELL} -m -n ${POPUSER}
   fn_check_exit $?
   #echo "${POPUSER}:x:${DOMUID}:${POPGID}::${HOMEDIR}:${SHELL}" >> ${SYSPASS}
else
   echo "User ${POPUSER} already in ${SYSPASS}"
fi
}

fn_mkdomuser()
{
STRNGTST=`grep "^${DOMUSER}:" ${DOMPASS}`
if [ -z "${STRNGTST}" ]
then
   echo "Making ${DOMPASS} entry"
   echo "${DOMUSER}:x:${DOMUID}:${DOMGID}::${HOMEDIR}:" >> ${DOMPASS}
else
   echo "User ${DOMUSER} already in ${DOMPASS}"
fi
}

fn_mkmaildir()
{
if [ ! -e ${HOMEDIR}/mail ]
then
    echo "Making ${HOMEDIR}/mail"
    mkdir ${HOMEDIR}/mail
    fn_check_exit $?
    echo "Setting permissions on ${HOMEDIR}"
    chown ${DOMUID}:${DOMGID} ${HOMEDIR}
    fn_check_exit $?
fi
}
    
fn_creatembox()
{
if [ ! -e ${MAILBOX} ]
then
   echo "Creating empty mailbox"
   touch ${MAILBOX}
   fn_check_exit $?
fi
echo "Setting ownership"
chown ${DOMUID}.mail ${MAILBOX}
fn_check_exit $?
echo "Setting permissions"
chmod 660 ${MAILBOX}
fn_check_exit $?
}

fn_dellink()
{
if [ -e ${MAILLNK} ]
then
   if [ -L ${MAILLNK} ]
   then
      echo "Removing old link"
      rm ${MAILLNK}
      fn_check_exit $?
   else
      echo "${MAILLNK} is not a link - moving to .old"
      mv ${MAILLNK} ${MAILLNK}.old
      fn_check_exit $?
   fi
fi
}

fn_mklink()
{
if [ -e ${MAILLNK} ]
then
   if [ -L ${MAILLNK} ]
   then
      echo "Removing old link"
      rm ${MAILLNK}
      fn_check_exit $?
   else
      echo "File ${MAILLNK} exists moving to .old"
      mv ${MAILLNK} ${MAILLNK}.old
      fn_check_exit $?
   fi
fi
echo "Creating a new link"
ln -s ${MAILBOX} ${MAILLNK}
fn_check_exit $?
}

fn_setpasswd()
{
echo "Set password for ${DOMUSER}"
passwd ${POPUSER}
fn_check_exit $?
}

fn_delpopuser()
{
fn_getdomain
fn_delpass
fn_dellink
echo ""
echo "Pop account deleted for ${POPUSER}"
echo "Files in the domain user and mail directories must be deleted manually"
echo "The entry in the domain passwd file is also redundant"
echo ""
}

fn_connect()
{
   echo ""
   echo "E-Mail Address: ${DOMUSER}@${DOMAIN}"
   echo "Pop username:   ${POPUSER}"
   echo "Pop password:   ********"
   echo "Inbound mail:   mail.${DOMAIN}"
   echo "Inbound port:   110"
   echo "Outbound mail:  mail.${DOMAIN}"
   echo "Outbound mail:  25"
   echo ""
}

fn_addpopuser()
{
fn_getdomain
fn_mkpasswd
fn_mkmaildir
fn_mkdomuser
fn_creatembox
fn_mklink
fn_setpasswd
fn_connect
}

fn_setuserpass()
{
fn_getdomain
fn_setpasswd
fn_connect
}

fn_usage()
{
   echo "Usage: ${PROGNAME} -a username [add a user]"
   echo "       ${PROGNAME} -c username [change a users password]"
   echo "       ${PROGNAME} -d username [delete a user]"
   echo "       ${PROGNAME} -l          [lists all user]"
   echo ""
   echo "There is also a -n option to set the prefix to null rather"
   echo "than m for backward compatiblity with earlier versions"
   echo "Note: -n must proceed any other parameters"
   exit 1
}

fn_delpass()
{
STRNGTST=`grep "^${POPUSER}:" ${SYSPASS}`
if [ -n "${STRNGTST}" ]
then
userdel ${POPUSER}
fn_check_exit $?
else
   echo "User ${POPUSER} already deleted from ${SYSPASS}"
fi
}

fn_listpops()
{
    fn_getdomain
    echo "Username\t/etc/passwd\t/etc/shadow\tLocal Password"
    for POPUSER in `grep "^${PREFIX}${DOMUID}\." ${SYSPASS} | cut -d: -f1 -s`
    do
       echo "${POPUSER}\tOK\t\t\c"
       OKS=`grep "^${POPUSER}:" ${SYSSHAD}`
       if [ -n "${OKS}" ]
       then
          echo "OK\t\t\c"
       else
          echo "Bad\t\t\c"
       fi
       DUS=`echo ${POPUSER} | cut -d"." -f2 -s`
       OKD=`grep "^${DUS}:" ${DOMPASS}`
       if [ -n "${OKD}" ]
       then
          echo "OK\t\c"
       else
          echo "Bad\t\c"
       fi
       echo ""
    done
}

DONE=""
while getopts a:c:d:ln PARAM
do
   case ${PARAM} in
       a) DOMUSER=${OPTARG}
	  fn_addpopuser
          DONE=TRUE
	  ;;
       c) DOMUSER=${OPTARG}
	  fn_setuserpass
          DONE=TRUE
	  ;;
       d) DOMUSER=${OPTARG}
	  fn_delpopuser
          DONE=TRUE
	  ;;
       l) fn_listpops
          DONE=TRUE
	  ;;
       n) PREFIX=""
	  POPUSER=${PREFIX}${DOMUID}.${DOMUSER}
	  ;;
       ?) fn_usage
   esac
done
shift $(($OPTIND -1))

if [ -z "${DONE}" ]
then
   fn_usage
fi
