415 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env bash
 | |
| 
 | |
| export LC_ALL=en_US.utf8
 | |
| 
 | |
| script_name="$(basename $(realpath $0))"
 | |
| working_dir="$(dirname $(realpath $0))"
 | |
| conf_file="${working_dir}/conf/${script_name%%.*}.conf"
 | |
| 
 | |
| LOCK_DIR="/tmp/dovecot_last_login.LOCK"
 | |
| error_log="${LOCK_DIR}/error.log"
 | |
| 
 | |
| _CACHE_FILE="${LOCK_DIR}/tmp_last_login"
 | |
| _CACHE_FILE_UNKNOWN="${LOCK_DIR}/tmp_unknown"
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| # -------------
 | |
| # --- Some defaut values
 | |
| # -------------
 | |
| 
 | |
| DEFAULT_MAILBOX_BASE_DIR="/var/vmail"
 | |
| DEFAULT_DST_DIR="/var/MAIL-STATS/LAST-LOGIN"
 | |
| DEFAULT_DOMAIN="*"
 | |
| 
 | |
| DEFAULT_USER="*"
 | |
| 
 | |
| 
 | |
| 
 | |
| # -------------
 | |
| # --- Some functions
 | |
| # -------------
 | |
| 
 | |
| 
 | |
| usage() {
 | |
| 
 | |
| 
 | |
|    [[ -n  "$1" ]] && error "$1"
 | |
| 
 | |
| 
 | |
|    [[ $terminal ]] && echo -e "
 | |
| \033[1mUsage:\033[m
 | |
| 
 | |
|    $(basename $0) [-d <mail-domain>]
 | |
| 
 | |
| \033[1mDescription\033[m
 | |
| 
 | |
|   Script determins the 'last login' time (pop/imap) for each mailbox of a domain. 
 | |
|   If no domain was given at the command line, statistics is written for all mailboxes 
 | |
|   of all available domains.
 | |
| 
 | |
|   Note:
 | |
|   this script expects a file '.las-log' at homedir of each mailbox. the timestamp of this
 | |
|   file is used as 'lost-login' time of the concerning mailbox.
 | |
| 
 | |
|   Configure a dovecot service to touch '~/.last-login' at each pop/imap login. See also:
 | |
|      \033[1mhttps://wiki.dovecot.org/PostLoginScripting\033[m
 | |
| 
 | |
| 
 | |
| \033[1mOptions\033[m
 | |
| 
 | |
|    -d 
 | |
|       The domain for which the 'last login' statistic is written. The default is to
 | |
|       write statistics for all available mail domains.
 | |
| 
 | |
| \033[1mFiles\033[m
 | |
| 
 | |
|    $conf_file: Configuration file
 | |
| 
 | |
| \033[1mExample:\033[m
 | |
|    
 | |
|    Write statistics for domain mail36.net
 | |
| 
 | |
|       $(basename $0) .. <command example>
 | |
| 
 | |
| "
 | |
| 
 | |
|    clean_up 1
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| clean_up() {
 | |
| 
 | |
|    # Perform program exit housekeeping
 | |
|    rm -rf "$LOCK_DIR"
 | |
| 	blank_line
 | |
|    exit $1
 | |
| }
 | |
| 
 | |
| echononl(){
 | |
|    if $terminal ; then
 | |
|       echo X\\c > /tmp/shprompt$$
 | |
|       if [[ $(wc -c /tmp/shprompt$$ | awk '{print $1}') -eq 1 ]]; then
 | |
|          echo -e -n "   $*\\c" 1>&2
 | |
|       else
 | |
|          echo -e -n "   $*" 1>&2
 | |
|       fi
 | |
|       rm /tmp/shprompt$$
 | |
|    fi
 | |
| }
 | |
| 
 | |
| fatal(){
 | |
|    echo ""
 | |
|    if $terminal ; then
 | |
|       echo -e "   [ \033[31m\033[1mError\033[m ]: $*"
 | |
|       echo ""
 | |
|       echo -e "\033[15G\033[31m\033[1mScript was interupted\033[m!"
 | |
|    else
 | |
|       echo "   [ Fatal ]: $*"
 | |
|       echo ""
 | |
|       echo "              Script was terminated...."
 | |
|    fi
 | |
|    echo ""
 | |
|    clean_up 1
 | |
| }
 | |
| 
 | |
| error(){
 | |
|    blank_line
 | |
|    if $terminal ; then
 | |
|       echo -e "   [ \033[31m\033[1mError\033[m ]: $*"
 | |
|    else
 | |
|       echo "   [ Error ]: $*"
 | |
|    fi
 | |
|    blank_line
 | |
| }
 | |
| 
 | |
| warn (){
 | |
|    if $terminal ; then
 | |
|       blank_line
 | |
|       echo -e "   [ \033[33m\033[1mWarn\033[m ]  $*"
 | |
|       blank_line
 | |
|    fi
 | |
| }
 | |
| 
 | |
| info (){
 | |
|    if $terminal ; then
 | |
|       blank_line
 | |
|       echo -e "   [ \033[32m\033[1mInfo\033[m ]  $*"
 | |
|       blank_line
 | |
|    fi
 | |
| }
 | |
| 
 | |
| echo_done() {
 | |
|    if $terminal ; then
 | |
|       echo -e "\033[75G[ \033[32mdone\033[m ]"
 | |
|    fi
 | |
| }
 | |
| echo_failed(){
 | |
|    if $terminal ; then
 | |
|       echo -e "\033[75G[ \033[1;31mfailed\033[m ]"
 | |
|    fi
 | |
| }
 | |
| echo_skipped() {
 | |
|    if $terminal ; then
 | |
|       echo -e "\033[75G[ \033[37m\033[1mskipped\033[m ]"
 | |
|    fi
 | |
| }
 | |
| echo_wait(){
 | |
|    if $terminal ; then
 | |
|       echo -en "\033[75G[ \033[5m\033[1m...\033[m ]"
 | |
|    fi
 | |
| }
 | |
| 
 | |
| blank_line() {
 | |
|    if $terminal ; then
 | |
|       echo ""
 | |
|    fi
 | |
| }
 | |
| 
 | |
| # Check if a given array (parameter 2) contains a given string (parameter 1)
 | |
| # 
 | |
| containsElement () {
 | |
|    local e
 | |
|    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
 | |
|    return 1
 | |
| }
 | |
| 
 | |
| 
 | |
| # -------------
 | |
| # - Job is already running?
 | |
| # -------------
 | |
| 
 | |
| # - If job already runs, stop execution..
 | |
| # -
 | |
| if mkdir "$LOCK_DIR" 2> /dev/null ; then
 | |
| 
 | |
|    ## - Remove lockdir when the script finishes, or when it receives a signal
 | |
|    trap clean_up SIGHUP SIGINT SIGTERM
 | |
| 
 | |
| else
 | |
| 
 | |
|    datum="$(date +"%d.%m.%Y %H:%M")"
 | |
| 
 | |
| 	msg="A previos instance of \"${script_name}\" seems already be running."
 | |
| 
 | |
| 	blank_line
 | |
| 
 | |
|    if $terminal ; then
 | |
|       echo -e "   [ \033[31m\033[1mError\033[m ]: $msg"
 | |
|       echo ""
 | |
|       echo -e "\033[15G\033[31m\033[1mScript was interupted\033[m!"
 | |
|    else
 | |
|       echo "   [ Fatal ]: $msg"
 | |
|       echo ""
 | |
|       echo "              Script was terminated...."
 | |
|    fi
 | |
|    echo ""
 | |
| 
 | |
| 	exit 1
 | |
| 
 | |
| fi
 | |
| 
 | |
| 
 | |
| 
 | |
| # -------------
 | |
| # --- Check some prerequisites
 | |
| # -------------
 | |
| 
 | |
| # - Running in a terminal?
 | |
| # -
 | |
| if [[ -t 1 ]] ; then
 | |
| 	terminal=true
 | |
| else
 | |
|    terminal=false
 | |
| fi
 | |
| 
 | |
| 
 | |
| # ==========
 | |
| # - Begin Main Script
 | |
| # ==========
 | |
| 
 | |
| # ----------
 | |
| # - Headline
 | |
| # ----------
 | |
| 
 | |
| if $terminal ; then
 | |
|    echo ""
 | |
|    echo -e "\033[1m----------\033[m"
 | |
|    echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m"
 | |
|    echo -e "\033[1m----------\033[m"
 | |
| fi
 | |
| 
 | |
| 
 | |
| # ----------
 | |
| # - Read Configurations from file '$conf_file'
 | |
| # ----------
 | |
| 
 | |
| if [[ -f "$conf_file" ]]; then
 | |
|    source "$conf_file"
 | |
| else
 | |
|    warn "No configuration file '$conf_file' present.\n
 | |
|              Loading default values.."
 | |
| fi
 | |
| 
 | |
| [[ -n "${MAILBOX_BASE_DIR}" ]] || MAILBOX_BASE_DIR="${DEFAULT_MAILBOX_BASE_DIR}"
 | |
| [[ -n "${DST_DIR}" ]] || DST_DIR="${DEFAULT_DST_DIR}"
 | |
| 
 | |
| 
 | |
| # ----------
 | |
| # - Read commandline parameter
 | |
| # ----------
 | |
| 
 | |
| while getopts d:h opt ; do
 | |
|    case $opt in
 | |
|       d)    DOMAIN="$OPTARG"
 | |
|             ;;
 | |
|       h)    usage
 | |
|             ;;
 | |
|       \?)   usage ;;
 | |
|    esac
 | |
| done
 | |
| 
 | |
| [[ -n "${DOMAIN}" ]] || DOMAIN="*"
 | |
| USER="*"
 | |
| 
 | |
| 
 | |
| echo -n '' > $_CACHE_FILE
 | |
| chmod 600 $_CACHE_FILE
 | |
| 
 | |
| declare -a _domain_arr=()
 | |
| 
 | |
| 
 | |
| blank_line
 | |
| 
 | |
| echononl "Create Directory '${DST_DIR}'.."
 | |
| if [[ ! -d "$DST_DIR" ]] ; then
 | |
| 	mkdir -p "$DST_DIR" > $error_log 2>&1
 | |
|    if [[ $? -eq 0 ]]; then
 | |
|       echo_done
 | |
|    else
 | |
|       echo_failed
 | |
| 		error "$(cat "$error_log")"
 | |
|    fi
 | |
| else
 | |
| 	echo_skipped
 | |
| fi
 | |
| 
 | |
| echononl "Remove old statistic files .."
 | |
| if [[ "$DOMAIN" = "*" ]] ; then
 | |
|    rm -f ${DST_DIR}/* > $error_log 2>&1
 | |
|    if [[ $? -eq 0 ]]; then
 | |
|       echo_done
 | |
|    else
 | |
|       echo_failed
 | |
| 		error "$(cat "$error_log")"
 | |
|    fi
 | |
| else
 | |
|    rm -f "${DST_DIR}/${DOMAIN}/*"
 | |
|    if [[ $? -eq 0 ]]; then
 | |
|       echo_done
 | |
|    else
 | |
|       echo_failed
 | |
| 		error "$(cat "$error_log")"
 | |
|    fi
 | |
| fi
 | |
| 
 | |
| 
 | |
| echononl "Determin last login time for mailboxes.."
 | |
| echo_wait
 | |
| for _dir in $(ls -d ${MAILBOX_BASE_DIR}/${DOMAIN}/*); do
 | |
| 
 | |
|    DOMAIN="$(basename $(dirname "$_dir"))"
 | |
|    USER="$(basename "$_dir")"
 | |
| 
 | |
|    if [[ ! -f "$_dir/.last_login" ]]; then
 | |
| 
 | |
|       echo "No login yet - ${USER}@${DOMAIN}" >> $_CACHE_FILE_UNKNOWN
 | |
| 
 | |
|    else
 | |
| 
 | |
|       LAST_LOGIN="$(stat "${_dir}/.last_login" | grep 'Change: ' | cut -d' ' -f2)"
 | |
|       echo "$LAST_LOGIN ${USER}@${DOMAIN}" >> $_CACHE_FILE
 | |
| 
 | |
|    fi
 | |
| 
 | |
| 	if ! containsElement "$DOMAIN" "${_domain_arr[@]}" ; then
 | |
| 		_domain_arr+=("$DOMAIN")
 | |
| 	fi
 | |
| 
 | |
| done;
 | |
| echo_done
 | |
| 
 | |
| echononl "Write down over all last-login statistic file .."
 | |
| if [[ ${#_domain_arr[@]} -gt 1 ]]; then
 | |
| 	cat $_CACHE_FILE | sort -n > "${DST_DIR}/00-all-domains.stat" 2> "$error_log"
 | |
| 	if [[ $? -eq 0 ]]; then
 | |
| 		echo_done
 | |
| 	else
 | |
| 		echo_failed
 | |
| 		error "$(cat "$error_log")"
 | |
| 	fi
 | |
| else
 | |
| 	echo_skipped
 | |
| fi
 | |
| 
 | |
| echononl "Write down over all nologin statistic file .."
 | |
| if [[ ${#_domain_arr[@]} -gt 1 ]]; then
 | |
| 	cat $_CACHE_FILE_UNKNOWN | sort -n > "${DST_DIR}/01-nologin.stat" 2> "$error_log"
 | |
| 	if [[ $? -eq 0 ]]; then
 | |
| 		echo_done
 | |
| 	else
 | |
| 		echo_failed
 | |
| 		error "$(cat "$error_log")"
 | |
| 	fi
 | |
| else
 | |
| 	echo_skipped
 | |
| fi
 | |
| 
 | |
| echononl "Write down statistic files per domain .."
 | |
| echo_wait
 | |
| _failed=false
 | |
| for _domain in "${_domain_arr[@]}" ; do
 | |
|    if [[ ${#_domain_arr[@]} -eq 1 ]] ; then
 | |
|       if grep -q "@$_domain" "$_CACHE_FILE"; then
 | |
|          cat $_CACHE_FILE | sort -n > "${DST_DIR}/${_domain}.stat" 2>> "$error_log"
 | |
|          if [[ $? -ne 0 ]]; then
 | |
|             _failed=true
 | |
|          fi
 | |
|       fi
 | |
|    else
 | |
|       if grep -q "@$_domain" "${DST_DIR}/00-all-domains.stat" ; then
 | |
|          cat "${DST_DIR}/00-all-domains.stat" | grep "@$_domain" > "${DST_DIR}/${_domain}.stat" 2>> "$error_log"
 | |
|          if [[ $? -ne 0 ]]; then
 | |
|             _failed=true
 | |
|          fi
 | |
|       fi
 | |
|    fi
 | |
|    if [[ ${#_domain_arr[@]} -eq 1 ]] ; then
 | |
|       if grep -q "@$_domain" "$_CACHE_FILE_UNKNOWN"; then
 | |
|          cat $_CACHE_FILE_UNKNOWN | sort -n > "${DST_DIR}/${_domain}-nologin.stat" 2> "$error_log"
 | |
|          if [[ $? -ne 0 ]]; then
 | |
|             _failed=true
 | |
|          fi
 | |
|       fi
 | |
|    else
 | |
|       if grep -q "@$_domain" "${DST_DIR}/01-nologin.stat" ; then
 | |
|          cat "${DST_DIR}/01-nologin.stat" | grep "@$_domain" > "${DST_DIR}/${_domain}-nologin.stat" 2> "$error_log"
 | |
|          if [[ $? -ne 0 ]]; then
 | |
|             _failed=true
 | |
|          fi
 | |
|       fi
 | |
|    fi
 | |
| done
 | |
| if $_failed ; then
 | |
| 	echo_failed
 | |
|    error "$(cat "$error_log")"
 | |
| else
 | |
| 	echo_done
 | |
| fi
 | |
| 
 | |
| info "Statistic file(s) stored at directory '${DST_DIR}/'"
 | |
| 
 | |
| clean_up 0;
 |