Skip to content

Instantly share code, notes, and snippets.

@lionicsheriff
Last active March 9, 2023 16:03
Show Gist options
  • Save lionicsheriff/5428076 to your computer and use it in GitHub Desktop.
Save lionicsheriff/5428076 to your computer and use it in GitHub Desktop.
postfix & dovecot virtual user administration tool
#!/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