- Change mount point from var/mail to home/vmail for proper Dovecot integration
- Update virtual_mailbox_base from /var/mail to /home/vmail
- Create Maildir/{cur,new,tmp} structure matching Dovecot's mail_location
- Fix vmailbox entries to include Maildir/ suffix
- Update vmail user home directory to /home/vmail
This resolves the issue where emails delivered by Postfix were not visible
in Roundcube because Dovecot was looking in ~/Maildir/ subdirectory.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
173 lines
4.9 KiB
Bash
173 lines
4.9 KiB
Bash
#!/bin/sh
|
|
# Mail Server User Management
|
|
|
|
CONFIG="mailserver"
|
|
|
|
get_container() {
|
|
uci -q get $CONFIG.main.container || echo "mailserver"
|
|
}
|
|
|
|
get_data_path() {
|
|
uci -q get $CONFIG.main.data_path || echo "/srv/mailserver"
|
|
}
|
|
|
|
# Add mail user
|
|
user_add() {
|
|
local email="$1"
|
|
local password="$2"
|
|
|
|
[ -z "$email" ] && { echo "Usage: user_add <email@domain>"; return 1; }
|
|
|
|
local user=$(echo "$email" | cut -d@ -f1)
|
|
local domain=$(echo "$email" | cut -d@ -f2)
|
|
local container=$(get_container)
|
|
local data_path=$(get_data_path)
|
|
|
|
# Validate
|
|
echo "$email" | grep -qE '^[^@]+@[^@]+\.[^@]+$' || { echo "Invalid email format"; return 1; }
|
|
|
|
# Create mailbox directory with Maildir structure
|
|
# Dovecot expects ~/Maildir/ so we create $domain/$user/Maildir/
|
|
local maildir="$data_path/mail/$domain/$user/Maildir"
|
|
mkdir -p "$maildir"/{cur,new,tmp}
|
|
chown -R 1000:1000 "$data_path/mail/$domain/$user"
|
|
|
|
# Add to virtual mailbox map (path relative to virtual_mailbox_base)
|
|
# Must end with Maildir/ to match Dovecot's mail_location = maildir:~/Maildir
|
|
local vmailbox="$data_path/config/vmailbox"
|
|
touch "$vmailbox"
|
|
grep -q "^$email" "$vmailbox" || echo "$email $domain/$user/Maildir/" >> "$vmailbox"
|
|
|
|
# Generate password hash
|
|
if [ -z "$password" ]; then
|
|
echo "Enter password for $email:"
|
|
read -s password
|
|
fi
|
|
|
|
# Add to dovecot users
|
|
local passfile="$data_path/config/users"
|
|
local hash=$(lxc-attach -n "$container" -- doveadm pw -s SHA512-CRYPT -p "$password" 2>/dev/null)
|
|
[ -z "$hash" ] && hash=$(openssl passwd -6 "$password")
|
|
|
|
grep -v "^$email:" "$passfile" > "${passfile}.tmp" 2>/dev/null
|
|
echo "$email:$hash" >> "${passfile}.tmp"
|
|
mv "${passfile}.tmp" "$passfile"
|
|
|
|
# Sync to Dovecot inside container
|
|
local dovecot_entry="$email:$hash:1000:1000::/home/vmail/$domain/$user"
|
|
lxc-attach -n "$container" -- sh -c "grep -v '^$email:' /etc/dovecot/users > /tmp/users.tmp 2>/dev/null; echo '$dovecot_entry' >> /tmp/users.tmp; mv /tmp/users.tmp /etc/dovecot/users"
|
|
|
|
# Postmap
|
|
lxc-attach -n "$container" -- postmap /etc/postfix/vmailbox 2>/dev/null
|
|
|
|
echo "User added: $email"
|
|
}
|
|
|
|
# Delete mail user
|
|
user_del() {
|
|
local email="$1"
|
|
[ -z "$email" ] && { echo "Usage: user_del <email@domain>"; return 1; }
|
|
|
|
local container=$(get_container)
|
|
local data_path=$(get_data_path)
|
|
|
|
# Remove from vmailbox
|
|
local vmailbox="$data_path/config/vmailbox"
|
|
sed -i "/^$email /d" "$vmailbox" 2>/dev/null
|
|
|
|
# Remove from passfile
|
|
local passfile="$data_path/config/users"
|
|
sed -i "/^$email:/d" "$passfile" 2>/dev/null
|
|
|
|
# Postmap
|
|
lxc-attach -n "$container" -- postmap /etc/postfix/vmailbox 2>/dev/null
|
|
|
|
echo "User deleted: $email (mailbox preserved)"
|
|
}
|
|
|
|
# List mail users
|
|
user_list() {
|
|
local data_path=$(get_data_path)
|
|
local passfile="$data_path/config/users"
|
|
|
|
if [ -f "$passfile" ]; then
|
|
echo "Mail Users:"
|
|
cut -d: -f1 "$passfile" | while read email; do
|
|
local domain=$(echo "$email" | cut -d@ -f2)
|
|
local user=$(echo "$email" | cut -d@ -f1)
|
|
local maildir="$data_path/mail/$domain/$user"
|
|
local size=$(du -sh "$maildir" 2>/dev/null | awk '{print $1}')
|
|
printf " %-40s %s\n" "$email" "${size:-0}"
|
|
done
|
|
else
|
|
echo "No users configured"
|
|
fi
|
|
}
|
|
|
|
# Change user password
|
|
user_passwd() {
|
|
local email="$1"
|
|
local password="$2"
|
|
|
|
[ -z "$email" ] && { echo "Usage: user_passwd <email@domain> [new_password]"; return 1; }
|
|
|
|
local container=$(get_container)
|
|
local data_path=$(get_data_path)
|
|
local passfile="$data_path/config/users"
|
|
|
|
grep -q "^$email:" "$passfile" || { echo "User not found: $email"; return 1; }
|
|
|
|
if [ -z "$password" ]; then
|
|
echo "Enter new password for $email:"
|
|
read -s password
|
|
fi
|
|
|
|
local hash=$(lxc-attach -n "$container" -- doveadm pw -s SHA512-CRYPT -p "$password" 2>/dev/null)
|
|
[ -z "$hash" ] && hash=$(openssl passwd -6 "$password")
|
|
|
|
# Update host passfile
|
|
sed -i "s|^$email:.*|$email:$hash|" "$passfile"
|
|
|
|
# Sync to Dovecot inside container
|
|
local user=$(echo "$email" | cut -d@ -f1)
|
|
local domain=$(echo "$email" | cut -d@ -f2)
|
|
local dovecot_entry="$email:$hash:1000:1000::/home/vmail/$domain/$user"
|
|
lxc-attach -n "$container" -- sh -c "grep -v '^$email:' /etc/dovecot/users > /tmp/users.tmp 2>/dev/null; echo '$dovecot_entry' >> /tmp/users.tmp; mv /tmp/users.tmp /etc/dovecot/users"
|
|
|
|
echo "Password changed for: $email"
|
|
}
|
|
|
|
# Add alias
|
|
alias_add() {
|
|
local alias="$1"
|
|
local target="$2"
|
|
|
|
[ -z "$alias" ] || [ -z "$target" ] && { echo "Usage: alias_add <alias@domain> <target@domain>"; return 1; }
|
|
|
|
local container=$(get_container)
|
|
local data_path=$(get_data_path)
|
|
local valias="$data_path/config/valias"
|
|
|
|
touch "$valias"
|
|
grep -q "^$alias " "$valias" || echo "$alias $target" >> "$valias"
|
|
|
|
lxc-attach -n "$container" -- postmap /etc/postfix/valias 2>/dev/null
|
|
|
|
echo "Alias added: $alias → $target"
|
|
}
|
|
|
|
# List aliases
|
|
alias_list() {
|
|
local data_path=$(get_data_path)
|
|
local valias="$data_path/config/valias"
|
|
|
|
if [ -f "$valias" ]; then
|
|
echo "Aliases:"
|
|
cat "$valias" | while read alias target; do
|
|
printf " %-40s → %s\n" "$alias" "$target"
|
|
done
|
|
else
|
|
echo "No aliases configured"
|
|
fi
|
|
}
|