Last active
March 9, 2023 16:03
-
-
Save lionicsheriff/5428076 to your computer and use it in GitHub Desktop.
postfix & dovecot virtual user administration tool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import argparse | |
import subprocess,errno | |
import os,sys | |
import fileinput | |
MAILBOX_MAP = '{{ virtual_mailbox_maps }}' | |
ALIAS_MAP = '{{ virtual_alias_maps }}' | |
USER_FILE = '{{ users_file }}' | |
PASSWORD_FILE = '{{ passwords_file }}' | |
MAIL_HOME = '{{ mail_home }}' | |
VMAIL_UID = {{ mail_user_id }} | |
VMAIL_GID = {{ mail_group_id }} | |
def fail(message): | |
print message | |
def prompt(message): | |
print message | |
def report(message): | |
print message | |
def success(message): | |
print message | |
def get_password(): | |
cmd = subprocess.Popen(["dovecotpw"], stdout=subprocess.PIPE) | |
output = cmd.communicate()[0] | |
if cmd.returncode != 0 or output == "": | |
fail('failed to get password') | |
sys.exit( 1) | |
return output | |
def safe_makedirs(path, mode): | |
try: | |
return os.makedirs(path,mode) | |
except OSError as exc: # Python >2.5 | |
if exc.errno == errno.EEXIST and os.path.isdir(path): | |
pass | |
else: raise | |
def mailbox_exists(mailbox): | |
with open(MAILBOX_MAP, 'r') as f: | |
for line in f: | |
if line.startswith(mailbox): | |
return MAILBOX_MAP; | |
with open(ALIAS_MAP, 'r') as f: | |
for line in f: | |
if line.startswith(mailbox): | |
return ALIAS_MAP; | |
return False | |
def add_mailbox(mailbox, **kwargs): | |
if mailbox_exists(mailbox): | |
fail("%s already exists" % mailbox) | |
sys.exit(1) | |
password = get_password() | |
user,domain = mailbox.split('@') | |
report("Creating %s" % mailbox) | |
maildir = "%s/%s/%s" % (MAIL_HOME,domain,user) | |
extra_folders = ['Trash', 'Drafts', 'Sent', 'Spam'] | |
report(" creating maildir in %s" % maildir) | |
safe_makedirs(maildir,0700) | |
os.chown(maildir, VMAIL_UID, VMAIL_GID) | |
os.chown(os.path.join(MAIL_HOME,domain), VMAIL_UID, VMAIL_GID) # just in case it got created... | |
report(" creating default folders (%s)" % ",".join(extra_folders)) | |
for d in extra_folders: | |
safe_makedirs(os.path.join(maildir,d), 0700) | |
for root, dirs, files in os.walk(maildir): | |
for d in dirs: | |
os.chown(os.path.join(root,d), VMAIL_UID, VMAIL_GID) | |
for f in files: | |
os.chown(os.path.join(root,f), VMAIL_UID, VMAIL_GID) | |
report(" adding %s to %s" % (mailbox,MAILBOX_MAP)) | |
with open(MAILBOX_MAP, 'a') as f: | |
f.write("%s %s/%s/\n" % (mailbox,domain,user)) | |
# rebuild mailbox hash | |
subprocess.Popen(["postmap", MAILBOX_MAP]) | |
report(" adding %s to %s" % (mailbox,USER_FILE)) | |
with open(USER_FILE, 'a') as f: | |
f.write("%s::%s:%s::%s:/usr/sbin/nologin::\n" % (mailbox,VMAIL_UID,VMAIL_GID,maildir)) | |
report(" adding %s to %s" % (mailbox,PASSWORD_FILE)) | |
with open(PASSWORD_FILE, 'a') as f: | |
f.write("%s:%s\n" % (mailbox,password)) | |
success("%s created" % mailbox) | |
def add_alias(mailbox, aliases, **kwargs): | |
for alias in aliases: | |
if mailbox_exists(alias): | |
fail("%s already exists" % alias) | |
continue | |
report("adding %s as alias for %s to %s" % (alias,mailbox,ALIAS_MAP)) | |
with open(USER_FILE, 'a') as f: | |
f.write("%s %s\n" % (alias,mailbox)) | |
# rebuild alias hash | |
subprocess.Popen(["postmap", ALIAS_MAP]) | |
success("aliases created") | |
def change_password(mailbox, **kwargs): | |
exists = mailbox_exists(mailbox) | |
if not exists == MAILBOX_MAP: | |
fail("%s not found in %s" % (mailbox, MAILBOX_MAP)) | |
sys.exit(1) | |
password = get_password() | |
report("change password for %s in %s" % (mailbox,PASSWORD_FILE)) | |
for line in fileinput.input(PASSWORD_FILE, inplace=1): | |
if line.startswith(mailbox): | |
print "%s:%s\n" % (mailbox,password) | |
else: | |
print line | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description = 'administer mailboxes') | |
subparsers = parser.add_subparsers(title = 'commands', dest = 'command') | |
add_mailbox_parser = subparsers.add_parser('add-mailbox') | |
add_mailbox_parser.set_defaults(method=add_mailbox) | |
add_mailbox_parser.add_argument('mailbox', type=str, help='email address to add') | |
add_alias_parser = subparsers.add_parser('add-alias') | |
add_alias_parser.set_defaults(method=add_alias) | |
add_alias_parser.add_argument('mailbox', type=str, help='receiving mailbox') | |
add_alias_parser.add_argument('aliases', nargs="+", type=str, help='list of email address to accept') | |
add_alias_parser = subparsers.add_parser('change-password') | |
add_alias_parser.set_defaults(method=change_password) | |
add_alias_parser.add_argument('mailbox', type=str, help='mailbox to change password for') | |
args = parser.parse_args() | |
args.method(**vars(args)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment