#!/bin/sh set -u # Display usage usage () { local - progname progname=$1 echo "Usage :" echo echo "$progname emailaccount [emailalias]" echo "$progname -h | --help" echo echo "Fist form add an email account named emailaccount if it doesn't already exist" echo "and create an alias named emailalias for this email account if specified" echo echo "Second and third form print 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 "Missing user in email account" exit 1 fi domain=$(echo "$1" | cut -d '@' -f 2) if echo "$domain" | grep -E -v "^([[:alnum:]]+\.)+[[:alnum:]]+$" > /dev/null then echo "Invalid domain for email account" exit 1 fi } 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 first_ask next_ask if [ \( $# -ge 1 -a "$1" = "-h" -a $# -gt 1 \) -o $# -gt 2 ] then echo "Too many arguments" exit 1 fi if [ $# -eq 0 -o "$1" = "-h" -o "$1" = "-help" ] then usage $(basename $0) exit 0 else # Test email arguments test_email "$1" emailuser="$user" emaildomain="$domain" if [ $# -eq 2 ] then test_email "$2" aliasuser="$user" aliasdomain="$domain" if [ "$emaildomain" != "$aliasdomain" ] then echo "Domain of the alias must be identical to the domain of the email account" exit 1 fi fi fi first_ask="Be aware that you'll need to restart cyrus twice and postfix once. Are you ready ?" next_ask="Incorrect answer. Are you ready to restart cyrus and postfix ?" ask_user_default_no "$first_ask" "$next_ask" if [ $? -eq 1 ] then echo "Ok, exiting_" exit 1 fi } # Are we root ? must_sudo () { uid="$(id -u)" [ ! $uid -eq 0 ] return $? } # This function try 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 "$@" ret=$? if [ ! $ret -eq 0 ] then echo "You must be root or being able to become root by sudo without password to create an email account or add an email alias" fi exit $ret } # Test cyrus password is ok test_cyrus_admin () { local - cyruser=$(grep -E "^[[:space:]]*admins:" /etc/imapd.conf | cut -d ':' -f 2- | sed -r "s/[[:alnum:]]+@[[:alnum:].]+//g" | tr -d ' ') cyrpwd=$(cat /root/cyrus_main_admin_password) testsaslauthd -u $cyruser -p $cyrpwd > /dev/null || { echo "Password for main cyrus admin has changed but has not been modified in /root/" && exit 1 ; } } # Save cyrus state and make it cyradm ready, that is save and change # allowplaintext and virtdomain and make it listen on localhost:imap # instead of *:imap save_cyrus_state_and_become_cyradm_ready () { local - sed -r -i "s/^(.*cmd=\"imapd.* listen=\")(imap\".*)$/\1localhost:\2/" /etc/cyrus.conf sed -r -i "s/^([[:space:]]*allowplaintext:.*)$/#\1\nallowplaintext: 1/" /etc/imapd.conf sed -r -i "s/^([[:space:]]*virtdomains:.*)$/#\1\nvirtdomains: userid/" /etc/imapd.conf invoke-rc.d cyrus2.2 restart if [ ! $? -eq 0 ] then echo "Restarting cyrus failed. The requested action coudn't be performed." echo "Look at the logs and restart Cyrus manually." return 1 fi return 0 } # 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 "Re-enter your password to ensure you type it in correctly" while [ -z "$newemailpassword" ] do echo -n "Password (bis) : " read newemailpassword done if [ "$emailpassword" != "$newemailpassword" ] then echo "Password don't match" fi done echo "$emailpassword" | saslpasswd2 -p -u "$emaildomain" "$emailuser" } # Check if a password already exist 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 "defaultdomain:" /etc/imapd.conf | grep "$emaildomain" > /dev/null if [ ! $? -eq 0 ] then grep "loginrealms:" /etc/imapd.conf | grep "$emaildomain" > /dev/null if [ ! $? -eq 0 ] then sed -r -i "s/^([[:space:]]*loginrealms): ?(.*)$/\1: $emaildomain \2/" /etc/imapd.conf fi fi grep "mydomain[[:space:]]*=" /etc/postfix/main.cf | grep "$emaildomain" > /dev/null if [ ! $? -eq 0 ] then grep "virtual_mailbox_domains[[:space:]]*=" /etc/postfix/main.cf | grep "$emaildomain" /dev/null if [ ! $? -eq 0 ] then sed -r -i "s/^([[:space:]]*virtual_mailbox_domains[[:space:]]*)= ?(.*)$/\1= $emaildomain \2/" /etc/postfix/main.cf fi 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 email to postfix virtual_mailbox_maps update_postfix_email_account () { local - grep "^[[:space:]]*$emailuser@$emaildomain" /etc/postfix/vmailbox > /dev/null if [ ! $? -eq 0 ] then echo_with_tabs "$emailuser@$emaildomain" "whatever" 4 >> /etc/postfix/vmailbox fi if [ ! $# -eq 0 ] then grep "^[[:space:]]*$aliasuser@$aliasdomain" /etc/postfix/vmailbox > /dev/null if [ ! $? -eq 0 ] then echo_with_tabs "$aliasuser@$aliasdomain" "whatever" 4 >> /etc/postfix/vmailbox fi fi postmap /etc/postfix/vmailbox } # Add requested alias to postfix virtual_alias_maps update_email_aliases () { local - grep "$aliasuser@$aliasdomain[[:space:]]+$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 } # Restore cyrus state as it was before making it cyradm ready, that is # restore allowplaintext, virtdomain and make it listen on *:imap # instead of localhost:imap restore_cyrus_state () { local - sed -r -i "s/^(.*cmd=\"imapd.* listen=\")localhost:(imap\".*)$/\1\2/" /etc/cyrus.conf # Ok let's explain that sed line # First you must understand that for each line all the commands inside # the {} block and separated by ; are executed # When the allowplaintext commented line is found, the # is removed by # a s/pattern/replacement/ command. Then, as this substitution is # successful, we jump at success which copy the substituted line in # the hold space, get the next line in the pattern space and replace # it by the substituted line. We've just deleted the next line. Then, # we print the line in the pattern space. If from the beginning we # have a non allowplaintext line, then T makes us jump to fail and # print the line in the pattern space (that is the current line) sed -r -n -i "{s/^#([[:space:]]*allowplaintext:.*)$/\1/;t success;T fail;: success;h;n;g;: fail;p}" /etc/imapd.conf # Same as for allowplaintext above sed -r -n -i "{s/^#([[:space:]]*virtdomains:.*)$/\1/;t success;T fail;: success;h;n;g;: fail;p}" /etc/imapd.conf invoke-rc.d --quiet cyrus2.2 restart if [ ! $? -eq 0 ] then echo "Restarting cyrus failed" exit 1 fi invoke-rc.d --quiet postfix reload if [ ! $? -eq 0 ] then echo "Reloading postfix failed" exit 1 fi } # Create the mailbox create_mail_account () { local - nblines nblines=$(echo "listmailbox user.$emailuser@$emaildomain" | cyradm --user $cyruser --auth login --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 --auth login --pass $cyrpwd localhost fi } main () { ret=0 # 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 save_cyrus_state_and_become_cyradm_ready if [ $? -eq 0 ] then create_sasldb_password update_virtual_domains aliasuser="${aliasuser:-""}" if [ -n "$aliasuser" ] then update_postfix_email_account 1 update_email_aliases else update_postfix_email_account fi create_mail_account display_infos else ret=1 fi restore_cyrus_state return $ret } main "$@" exit $?