#!/usr/local/bin/python

#
# (c) 2004 Epigenomics AG
#
# written by Robert Sander <robert.sander@epigenomics.com>
# added virtual handling by Frank Tegtmeyer <fte@fte.to>
# reduce memory use Frank Tegtmeyer <fte@fte.to>
#
# released under GPL
#
#
# This script collects nearly all possible
# local parts for emails on a qmail system.
#
# It is for use with the RECIPIENTS extension
# from http://www.fehcom.de/qmail/qmail.html
#
# It reads users from the system password database,
# /var/qmail/users/assign, aliases from /etc/aliases (fastforward)
# and /var/qmail/alias. It also reads virtual users for domains
# managed by vpopmail, if they exist.
# Relaydomains are handled by wildcards.
#
# The script covers standard qmail behaviour.
#
# Use at your own risk.
#

import pwd, os, os.path, sys, string

localsfile   = "/var/qmail/control/locals"
virtualsfile = "/var/qmail/control/virtualdomains"
rcptfile     = "/var/qmail/control/rcpthosts"
mrcptfile    = "/var/qmail/control/morercpthosts"

locals   = map(lambda x: x.strip(), open(localsfile).readlines())
virtuals = []
if os.path.isfile(virtualsfile):
    virtuals = map(lambda x: x.strip(), open(virtualsfile).readlines())
rcpt     = map(lambda x: x.strip(), open(rcptfile).readlines())
if os.path.isfile(mrcptfile):
    rcpt += map(lambda x: x.strip(), open(mrcptfile).readlines())

localusers = []
for user in pwd.getpwall():
    try:
        st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime = os.stat(user[5])
        if st_uid and st_uid == user[2] and user[0] == user[0].lower():
            localusers.append(user[0])
    except OSError:
        pass

# users/assign
try:
    assigns = open("/var/qmail/users/assign").readlines()
except IOError:
    assigns = []

def get_relaydomains():
    virtualdomains = map(lambda x: string.split(x, ':')[0], virtuals)
    for host in rcpt:
	if not host in locals and not host in virtualdomains:
	   print "!@%s" % (host)

def get_users():
    for user in localusers:
        print "\n".join(map(lambda x: "%s@%s" % (user, x), locals))

def get_users_assign_fixed():
    names = []
    for line in assigns:
        if line[0] == "=":
            name, user, uid, gid, homedir, dash, ext, rem = line.strip().split(":")
	    names.append(name[1:])
    for name in names:
        # strip unnecessary extensions
        components = name.split('-')
        name = components[0]
	found = False
	for c in components[1:]:
	    if name in names:
		found = True
		break
	    name += '-' + c
	# shortest version found?
	if not found:
            print '\n'.join(map(lambda x: "%s@%s" % (name, x), locals))

def get_aliases(directory,base,domains):
    # ignore .qmail-default - it is handled somewhere else
    aliasentries = filter(lambda x: x != 'default' , \
		 map(lambda x: x[7:], \
	          filter(lambda x: x[:7] == '.qmail-', os.listdir(directory)) \
		 ) \
		)
    if len(base) == 0:
	input = aliasentries
    else:
        input = map(lambda x: x[len(base)+1:], filter(lambda x: x.find(base) == 0, aliasentries))
    input.sort()
    for alias in input:
        if alias[-8:] == "-default":
            if alias[:-8] in input:
                continue
	    # strip internally used ezmlm extensions
            if alias[-15:] == "-accept-default" or alias[-15:] == "-reject-default" or alias[-15:] == "-return-default":
                if alias[:-15] in input:
                    continue
	    # don't include -default extensions in output
            alias = alias[:-8]
	# strip unnecessary extensions
	components = alias.split('-')
        alias = components[0]
	found = False
	for c in components[1:]:
	    if alias in input:
		found = True
		break
	    alias += '-' + c
	# shortest version found?
	if not found:
            print '\n'.join(map(lambda x: "%s@%s" % (alias, x), domains))

def get_etcaliases(host):
    try:
        for alias in open("/etc/aliases").readlines():
            if alias[0] != "#":
                local, rem = alias.split(":")
                print '\n'.join(map(lambda x: "%s@%s" % (local, x), locals))
    except IOError:
        pass

# default-status:
#   results   -2 : all addresses are accepted, no vpopmail
#             -1 : all addresses are accepted, vpopmail
#              1 : vpopmail without catch-all account
#              2 : no default delivery file
def default_status(domains, directory, user):
    if user != '':
	user += '-'
    defaultfile = os.path.join(directory,'.qmail-%sdefault' % (user))
    if os.path.isfile(defaultfile):
       # vpopmail check
       delivery = filter (lambda x: x[0] == '|', open(defaultfile).readlines())
       delivery = filter (lambda x: x.find('vdelivermail') >= 0, delivery)
       if len(delivery) == 0:
	   # default file, no vpopmail
           print '\n'.join(map(lambda x: '!@%s' % (x), domains))
	   return -2
       delivery = filter (lambda x: x.find('bounce-no-mailbox') >= 0, delivery)
       if len(delivery) == 0:
           print '\n'.join(map(lambda x: '!@%s' % (x), domains))
	   return -1
       # vpopmail without catchall
       return 1
    # no default file
    return 2

def handle_vpopmaildomain(domain,domaindir):
    vpasswdfile = os.path.join(domaindir, 'vpasswd')
    print '\n'.join(map (lambda x: "%s@%s" % (x.split(':')[0],domain), \
			   open(vpasswdfile).readlines()))
    return

def get_virtuals():
    # get users/assign once
    usersassign = []
    usersassign_homedirs = {}
    for line in assigns:
        if line[0] == "+":
            name, user, uid, gid, homedir, dash, ext, rem = line.strip().split(":")
	    name = name[1:]
	    if name[-1] == '-':
	       name = name[:-1]
	    usersassign.append(name)
	    usersassign_homedirs[name] = homedir
    # check all virtualdomains
    for virtualdomain in virtuals:
        domain, user = virtualdomain.strip().split(":")
        if user in usersassign:
	    status = default_status([domain], usersassign_homedirs[user], '')
	    if status == 2:
                get_aliases(usersassign_homedirs[user], '', [domain])
	    if status == 1:
	        handle_vpopmaildomain(domain, usersassign_homedirs[user])
                get_aliases(usersassign_homedirs[user], '', [domain])
	elif user in localusers:
            homedir = pwd.getpwnam(user)[5]
	    status = default_status([domain], homedir, '')
	    if status == 2:
                get_aliases(homedir, '', [domain])
	    if status == 1:
	        handle_vpopmaildomain(domain, homedir)
                get_aliases(homedir, '', [domain])
	else:   # try aliases
	    status = default_status([domain], '/var/qmail/alias', user)
	    if status == 2:
                get_aliases('/var/qmail/alias', user, [domain])
	    if status == 1:
		# I will not handle such a crazy setup, so ...
                print '!@%s' % (domain)

get_relaydomains()
get_users()
get_users_assign_fixed()
# check /var/qmail/alias/.qmail-default
if default_status(locals, '/var/qmail/alias', '') > 0:
    get_aliases('/var/qmail/alias','',locals)
get_virtuals()

