#!/usr/bin/python import os import errno import sys import shutil import sqlite3 import optparse from datetime import date,datetime database = "domains.db" homedir = "/var/www" def main(argv): """domainctl is a tool for managing domains, subdomains, users, and email addresses.""" parser = optparse.OptionParser() parser.add_option("-a", "--add", nargs=4, dest="add_domain", help="add a DOMAIN", metavar="DOMAIN USER EMAIL IP") parser.add_option("-c", "--create", dest="create_database", help="create the database", action="store_true") parser.add_option("-d", "--delete-domain", nargs=1, dest="delete_domain", help="delete a DOMAIN", metavar="DOMAIN") parser.add_option("-D", "--delete-user", nargs=1, dest="delete_user", help="delete a USER", metavar="USER") parser.add_option("-e", "--domain-email", nargs=2, dest="domain_email", help="set EMAIL for DOMAIN", metavar="DOMAIN EMAIL") parser.add_option("-E", "--user-email", nargs=2, dest="user_email", help="set EMAIL for USER", metavar="USER EMAIL") parser.add_option("-g", "--generate", nargs=1, dest="generate", help="generate zonefiles, vhost, and dirs for DOMAIN", metavar="DOMAIN") parser.add_option("-G", "--generate-all", dest="generate_all", help="generate all files for all domains", action="store_true") parser.add_option("-i", "--domain-ip", nargs=2, dest="ip", help="set IP for DOMAIN", metavar="DOMAIN IP") parser.add_option("-l", "--list", dest="list", help="list domains and users", action="store_true") parser.add_option("-s", "--dirs", nargs=1, dest="build_dirs", help="build dir structure for DOMAIN", metavar="DOMAIN") parser.add_option("-u", "--domain-user", nargs=2, dest="domain_user", help="set USER for DOMAIN", metavar="DOMAIN USER") parser.add_option("-v", "--vhost", nargs=1, dest="build_vhost", help="build vhost for DOMAIN", metavar="DOMAIN") parser.add_option("-z", "--zone", nargs=1, dest="build_zone", help="build zonefile for DOMAIN", metavar="DOMAIN") options, args = parser.parse_args() if not sys.argv[1:]: parser.print_help() elif options.add_domain: subdomain, domain = split_args(options.add_domain[0]) user = options.add_domain[1] email = options.add_domain[2] ip = options.add_domain[3] add_domain(subdomain, domain, user, email, ip) elif options.create_database: create_db() elif options.delete_domain: subdomain, domain = split_args(options.delete_domain) if subdomain: delete_subdomain(subdomain, domain) else: delete_domain(domain) elif options.delete_user: delete_user(options.delete_user) elif options.domain_email: subdomain, domain = split_args(options.domain_email[0]) email = options.domain_email[1] set_domain_email(subdomain, domain, email) elif options.user_email: user = options.user_email[0] email = options.domain_email[1] set_user_email(user, email) elif options.generate: subdomain, domain = split_args(options.generate) build_vhost(subdomain, domain) build_zone(subdomain, domain) build_structure(subdomain, domain) elif options.generate_all: generate_all() elif options.ip: subdomain, domain = split_args(options.ip[0]) ip = options.ip[1] set_domain_ip(subdomain, domain, ip) elif options.list: list_domains() elif options.build_dirs: subdomain, domain = split_args(options.build_dirs) build_structure(subdomain, domain) elif options.domain_user: subdomain, domain = split_args(options.domain_user[0]) user = options.domain_user[1] set_domain_user(subdomain, domain, user) elif options.build_vhost: subdomain, domain = split_args(options.build_vhost) build_vhost(subdomain, domain) elif options.build_zone: subdomain, domain = split_args(options.build_zone) build_zone(subdomain, domain) sys.exit(0) def split_args(args): """Splits the subdomain from a domain and returns both.""" if args.count(".") < 2: subdomain, domain = ("", args) else: subdomain, domain = args.split(".", 1) return subdomain, domain def add_domain(subdomain, domain, user, email, ip): """Adds a domain (or subdomain) to the db.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select 1 from store where domain = ? and subdomain = '' """, (domain,)).fetchall() if subdomain and not resultset: print ("Cannot create subdomain '%s.%s' without matching domain." % (subdomain,domain)) sys.exit(1) resultset = db.execute(""" select 1 from store where domain = ? and subdomain = ? """, (domain, subdomain,)).fetchall() if resultset: if subdomain: print "Subdomain '%s.%s' already exists." % (subdomain, domain) else: print "Domain '%s' already exists." % (domain) db.execute(""" insert into store (user, subdomain, domain, email, ip) values (?, ?, ?, ?, ?) """, (user.lower(), subdomain.lower(), domain.lower(), email.lower(), ip.lower(),)) db.commit() except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def create_db(): """Creates the database for tracking control info.""" try: db = sqlite3.connect(database) db.execute(""" create table store ( id integer primary key, user text not null, subdomain text not null, domain text not null, email text not null, ip text not null ) """) except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def delete_user(user): """Drops a user entry, and all associated domains, from the database.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select subdomain, domain from store where user = ? """, (user,)).fetchall() if len(resultset) == 0: print "User '%s' does not exist." % (user) sys.exit(1) print "WARNING! You are about to delete the user '%s' and" % (user) print "the following domains:" for subdomain, domain in resultset: if subdomain != '': domain = subdomain + "." + domain print domain answer = raw_input("Are you sure that you want to do this? (y/N): ") if answer.lower() == "y" or answer.lower() == "yes": db.execute(""" delete from store where user = ? """, (user,)) db.commit() else: print "Aborted." except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def delete_domain(domain): """Drops a domain (or subdomain), and all associated subdomains, from the database. """ try: db = sqlite3.connect(database) resultset = db.execute(""" select 1 from store where domain = ? """, (domain,)).fetchall() if not resultset: print "Domain '%s' does not exist." % (domain) sys.exit(1) resultset = db.execute(""" select subdomain, domain from store where domain = ? and subdomain != '' """, (domain,)).fetchall() print "WARNING! You are about to delete the domain '%s' and" % (domain) print "the following subdomains:" for subdomain, domain in resultset: print "%s.%s" % (subdomain, domain) answer = raw_input("Are you sure that you want to do this? (y/N): ") if answer.lower() == "y" or answer.lower() == "yes": db.execute(""" delete from store where domain = ? """, (domain,)) db.commit() else: print "Aborted." except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def delete_subdomain(subdomain, domain): """Drops a subdomain from the database.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select 1 from store where domain = ? and subdomain = ? """, (domain, subdomain,)).fetchall() if len(resultset) == 0: print "Subdomain '%s.%s' does not exist." % (subdomain, domain) sys.exit(1) print "WARNING! You are about to delete the subdomain '%s.%s'." % (subdomain, domain) answer = raw_input("Are you sure that you want to do this? (y/N): ") if answer.lower() == "y" or answer.lower() == "yes": db.execute(""" delete from store where domain = ? and subdomain = ? """, (domain, subdomain,)) else: print "Aborted." except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def generate_all(): """Generate all files for all domains.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select domain, subdomain from store """).fetchall() except OperationalError as e: print "error: %s" % (e) sys.exit(1) for domain, subdomain in resultset: build_structure(subdomain, domain) build_vhost(subdomain, domain) build_zone(subdomain, domain) def list_domains(): """Prints the stored domains in a visually helpful manner.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select user, subdomain, domain, ip, email from store """).fetchall() print "%s%s%s%s" % ("user".ljust(16), "email".ljust(32), "domain".ljust(32), "ip") for user, subdomain, domain, ip, email in resultset: if subdomain != "": domain = subdomain + "." + domain print "%s%s%s%s" % (user.ljust(16), email.ljust(32), domain.ljust(32), ip) except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def build_zone(subdomain, domain): """Builds the zonefile from a template.""" zone_file = "%s.zone" % domain try: db = sqlite3.connect(database) resultset = db.execute(""" select user from store where domain = ? and subdomain = '' """, (domain,)).fetchall() user = resultset[0][0] resultset = db.execute(""" select subdomain, ip from store where domain = ? """, (domain,)).fetchall() except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) if os.path.isfile(zone_file): shutil.copy(zone_file, zone_file + ".old") template = open("zone_template.txt", "r") zone = open(zone_file, "w") line = template.readline() while line: line = line.replace("[user]", user) line = line.replace("[domain]", domain) line = line.replace("[serial]", date.strftime(datetime.today(), format="%Y%m%d%M")) zone.write(line) line = template.readline() zone.seek(0, os.SEEK_END) for subdomain, ip in resultset: if ip == "cname" and subdomain: zone.write("%sIN CNAME %s.\n" % (subdomain.ljust(32), domain)) elif subdomain: zone.write("%sIN A %s\n" % (subdomain.ljust(32), ip)) elif not subdomain: zone.write("%sIN A %s\n" % ("@".ljust(32), ip)) zone.close() template.close() def set_domain_user(subdomain, domain, user): """Set the user associated with a given domain.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select 1 from store where domain = ? and subdomain = ? """, (domain, subdomain,)).fetchall() if len(resultset) == 0: if subdomain: print "Subdomain '%s.%s' does not exist." % (subdomain, domain) else: print "Domain '%s' does not exist." % (domain) sys.exit(1) resultset = db.execute(""" update store set user = ? where domain = ? and subdomain = ? """, (email, domain, subdomain,)) db.commit() except sqlite2.OperationalError as e: print "error: %s" % e sys.exit(1) def build_vhost(subdomain, domain): """Build the apache vhost file from a template.""" if subdomain: vhost_file = "%s.%s" % (subdomain, domain) else: vhost_file = domain try: db = sqlite3.connect(database) resultset = db.execute(""" select user from store where domain = ? and subdomain = ? limit 1 """, (domain, subdomain,)).fetchall() user = resultset[0][0] except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) if os.path.isfile(vhost_file): shutil.copy(vhost_file, vhost_file + ".old") template = open("vhost_template.txt", "r") vhost = open(vhost_file, "w") line = template.readline() while line: if not subdomain: line = line.replace("[subdomain].[domain]", domain) line = line.replace("[ServerAlias]", "ServerAlias www.%s" % domain) else: line = line.replace("[ServerAlias]", "") line = line.replace("[user]", user) line = line.replace("[domain]", domain) line = line.replace("[subdomain]", subdomain) line = line.replace("//", "/") vhost.write(line) line = template.readline() vhost.close() template.close() def build_structure(subdomain, domain): """Build the dir structure for hosting the domain or subdomain.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select user from store where domain = ? and subdomain = ? limit 1 """, (domain, subdomain,)).fetchall() if not resultset: if subdomain: print "Subdomain '%s.%s' does not exist." % (subdomain, domain) else: print "Domain '%s' does not exist." % domain sys.exit(1) user = resultset[0][0] if not subdomain: os.makedirs(name="%s/%s/%s/public_html" % (homedir, user, domain), mode=0755) os.makedirs(name="%s/%s/%s/cgi-bin" % (homedir, user, domain), mode=0755) else: os.makedirs(name="%s/%s/%s/%s/public_html" % (homedir, user, domain, subdomain), mode=0755) os.makedirs(name="%s/%s/%s/%s/cgi-bin" % (homedir, user, domain, subdomain), mode=0755) except OSError as exc: if exc.errno == errno.EEXIST: pass else: raise except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def set_domain_email(subdomain, domain, email): """Set the email associated with a given domain.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select 1 from store where domain = ? and subdomain = ? limit 1 """, (domain, subdomain,)).fetchall() if len(resultset) == 0: if subdomain: print "Subdomain '%s.%s' does not exist." % (subdomain, domain) else: print "Domain '%s' does not exist." % (domain) sys.exit(1) resultset = db.execute(""" update store set email = ? where domain = ? and subdomain = ? """, (email, domain, subdomain,)) db.commit() except sqlite2.OperationalError as e: print "error: %s" % e sys.exit(1) def set_user_email(user, email): """Set the email associated with a given user.""" try: db = sqlite3.connect(database) resultset = db.execute(""" select subdomain, domain from store where user = ? """, (user,)).fetchall() if len(resultset) == 0: print "User '%s' does not exist." % (user) print "WARNING! You are about to change the email address for user '%s'." print "This will affect the following domains:" for subdomain, domain in resultset: if subdomain != '': domain = subdomain + "." + domain print domain answer = raw_input("Are you sure that you want to do this? (y/N): ") if answer.lower() == "y" or answer.lower() == "yes": db.execute(""" update store set email = ? where user = ? """, (email, user,)) db.commit() else: print "Aborted." except sqlite3.OperationalError as e: print "error: %s" % e sys.exit(1) def usage(): """Print usage info.""" print "domainctl