#!/bin/sh # # email_account, Copyright © 2010 Thomas Preud'homme # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # email_account is a helper to create e-mail accounts in a configuration # using Postfix mail transport agent with vhosts and Cyrus IMAP server, # in a Debian environment. set -u # Display usage. usage () { local - progname progname=$1 echo "Usage :" echo echo "$progname emailaccount [emailalias]" echo "$progname -h" echo echo "First form adds an email account named emailaccount if it doesn't already exist" echo "and creates an alias named emailalias for this email account if specified." echo echo "Second form prints this help." } # Test an email is valid and exit if it isn't. test_email () { local - user=$(echo "$1" | cut -d '@' -f 1) if [ -z "$user" ] then echo "Error! Missing user in email account." >&2 exit 1 fi domain=$(echo "$1" | cut -d '@' -f 2) if echo "$domain" | grep -E -v "^([[:alnum:]]+\.)+[[:alnum:]]+$" > /dev/null then echo "Error! Invalid domain for email account." >&2 exit 1 fi } # Ask a question to the user. Return 1 if the answer is negative, # or 0 if it is positive. ask_user_default_no () { local - answer answer="unset" echo -n "$1 [y/N] " while [ -n "$answer" -a "$answer" != "y" -a "$answer" != "Y" -a "$answer" != "n" -a "$answer" != "N" ] do read answer if [ -z "$answer" -o "$answer" = "n" -o "$answer" = "N" ] then return 1 fi if [ "$answer" = "y" -o "$answer" = "Y" ] then break fi echo -n "$2 [y/N] " done return 0 } # Test number of argument is correct and their value are correct also. test_args () { local - user domain while getopts "h" opt do case $opt in "h") if [ $# -gt 1 ] then echo "Error! Too many arguments." >&2 exit 1 fi usage $(basename "$0") exit 0 ;; esac done if [ $# -eq 1 -a -z "$1" ] then usage $(basename "$0") exit 0 fi if [ $# -gt $((OPTIND+1)) ] then echo "Error! Too many arguments." >&2 exit 1 fi # Test email arguments eval test_email "\$$OPTIND" emailuser="$user" emaildomain="$domain" if [ $# -eq $((OPTIND+1)) ] then eval test_email "\$$((OPTIND+1))" aliasuser="$user" aliasdomain="$domain" if [ "$emaildomain" != "$aliasdomain" ] then echo "Error! Domain of the alias must be identical to the domain of the email account." >&2 exit 1 fi fi } # Are we root? must_sudo () { uid="$(id -u)" [ ! $uid -eq 0 ] return $? } # This function tries to become root with sudo and execute this script. # NB: This function doesn't return. try_sudo () { local - ret echo "You aren't root. Trying to use sudo to become root…" sudo $0 "$@" # Try to execute the script with sudo ret=$? if [ ! $ret -eq 0 ] then echo "Error! You must be root or being able to become root by sudo without password to create an email account or add an email alias." >&2 fi exit $ret } # Test cyrus password is OK. test_cyrus_admin () { local - cyruser=$(grep -E "^[[:blank:]]*admins:" /etc/imapd.conf | cut -d ':' -f 2- | sed -r "s/[[:alnum:]]+@[[:alnum:].]+//g" | tr -d ' ') cyrpwd=$(cat /root/passwords/cyrus_main_admin_password) testsaslauthd -u $cyruser -p $cyrpwd > /dev/null || { echo "Error! Password for main cyrus admin has changed but has not been modified in /root/" >&2 && exit 1 ; } } # Ask a password to the user and create the entry in sasldb database. ask_and_create_password () { local - newemailpassword emailpassword="" newemailpassword="different" while [ "$emailpassword" != "$newemailpassword" ] do emailpassword="" echo "What password do you want for your email account?" while [ -z "$emailpassword" ] do echo -n "Password: " read emailpassword done newemailpassword="" echo "Please re-enter your password to ensure you typed it correctly." while [ -z "$newemailpassword" ] do echo -n "Password (bis): " read newemailpassword done if [ "$emailpassword" != "$newemailpassword" ] then echo "Passwords don't match!" fi done echo "$emailpassword" | saslpasswd2 -p -u "$emaildomain" "$emailuser" } # Check if a password already exists in the sasldb database. # Then ask a password to the user and create the entry in sasldb # database if password doesn't exist are user want to change it. create_sasldb_password () { local - first_ask next_ask sasldblistusers2 | grep "$emailuser@$emaildomain" > /dev/null if [ $? -eq 0 ] then first_ask="There is already a password for this user, do you want to change it?" next_ask="Do you want to change the existing password for your email account?" ask_user_default_no "$first_ask" "$next_ask" if [ $? -eq 0 ] then ask_and_create_password else emailpassword="has remained unchanged" fi else ask_and_create_password fi } # Add the domain of the email account to the list of virtual domain # in cyrus and postfix if necessary. update_virtual_domains () { local - grep "loginrealms:" /etc/imapd.conf | grep "$emaildomain" > /dev/null if [ ! $? -eq 0 ] then sed -r -i "s/^([[:blank:]]*loginrealms): ?(.*)$/\1: $emaildomain \2/" /etc/imapd.conf fi grep -E "virtual_mailbox_domains[[:blank:]]*=" /etc/postfix/main.cf | grep "$emaildomain" /dev/null if [ ! $? -eq 0 ] then sed -r -i "s/^([[:blank:]]*virtual_mailbox_domains[[:blank:]]*)= ?(.*)$/\1= $emaildomain \2/" /etc/postfix/main.cf fi } # Print the first parameter and then the second parameter at a distance # of $3 tabs. echo_with_tabs () { local - nbchars nbtabs nbchars=$(echo "$1" | wc -m) nbtabs=$(($3-nbchars/8)) [ $nbtabs -lt 1 ] && nbtabs=1 echo -n "$1" for i in $(seq $nbtabs) do echo -n "\t" done echo "$2" } # Add requested alias to postfix virtual_alias_maps. update_email_aliases () { local - grep -E "[[:blank:]]*$aliasuser@$aliasdomain[[:blank:]]+$emailuser@$emaildomain" /etc/postfix/virtual > /dev/null if [ ! $? -eq 0 ] then echo_with_tabs "$aliasuser@$aliasdomain" "$emailuser@$emaildomain" 4 >> /etc/postfix/virtual fi postmap /etc/postfix/virtual } display_infos () { local - imap imaps imapret imapsret security set +u grep "defaultdomain:" /etc/imapd.conf | grep "$emaildomain" > /dev/null if [ $? -eq 0 ] then echo "Your email login is $emailuser and your password $emailpassword" else echo "Your email login is $emailuser@$emaildomain and your password $emailpassword" fi echo -n "Supported mechanism for imap authentification are: " sed -r -n "{s/sasl_mech_list: *(.*)$/\1/;t success;T;: success;p}" /etc/imapd.conf echo -n "The imap server listen on port " imap=$(grep -E "^[^#]+cmd=\"imapd[^#]+listen=\"imap\"" /etc/cyrus.conf) imapret=$? if [ $imapret -eq 0 ] then security=$(echo "$imap" | sed -r -n "{s/^.*cmd=\".* -s.*$/(SSL\/TLS)/;t success;T;: success;p}") if [ -z "$security" ] then security="(STARTTLS)" fi echo -n "143 ($security)" fi imaps=$(grep -E "^[^#]+cmd=\"imapd[^#]+listen=\"imaps\"" /etc/cyrus.conf) imapsret=$? if [ $imapsret -eq 0 ] then security=$(echo "$imaps" | sed -r -n "{s/^.*cmd=\".* -s.*$/(SSL\/TLS)/;t success;T;: success;p}") if [ -z "$security" ] then security="(STARTTLS)" fi if [ $imapret -eq 0 ] then echo -n " and on port " fi echo -n "993 ($security)" fi echo "." } # Ask Postfix and Cyrus to reload their configuration reload_config () { local - invoke-rc.d --quiet cyrus2.2 reload if [ ! $? -eq 0 ] then echo "Error! Cyrus failed to reload its configuration." >&2 exit 1 fi invoke-rc.d --quiet postfix reload if [ ! $? -eq 0 ] then echo "Error! Postfix failed to reload its configuration." >&2 exit 1 fi } # Create the mailbox create_mail_account () { local - nblines nblines=$(echo "listmailbox user/$emailuser@$emaildomain" | cyradm --user $cyruser --pass $cyrpwd localhost | wc -l) if [ $nblines -eq 0 ] then echo "Mailbox for $emailuser@$emaildomain doesn't exist, creating it…" echo "createmailbox user/$emailuser@$emaildomain" | cyradm --user $cyruser --pass $cyrpwd localhost fi } main () { local - ret # This test should be useless if rights on this file are corrects # (that is 770 for root:gt owner) test_args "$@" if must_sudo then try_sudo "$@" fi test_cyrus_admin create_sasldb_password update_virtual_domains aliasuser="${aliasuser:-""}" if [ -n "$aliasuser" ] then update_email_aliases fi create_mail_account display_infos reload_config return 0 } main "${@:-""}" exit $?