2005-09-28 19:57:51 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
import psycopg, pwd, grp, smtplib, os
|
|
|
|
from email.MIMEText import MIMEText
|
|
|
|
import Settings, PasswordTools
|
2005-09-28 19:57:51 +02:00
|
|
|
|
|
|
|
class InvalidDomain(Exception):
|
2005-09-29 00:02:42 +02:00
|
|
|
"""This exception is raised if an invalid domain is used."""
|
2005-09-28 19:57:51 +02:00
|
|
|
def __init__(self, domain):
|
|
|
|
self.domain = domain
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return repr("Invalid domain %s" % (self.domain))
|
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
class NoSysuserForDomain(Exception):
|
|
|
|
"""This exception is raised if no system user is associated with a domain."""
|
|
|
|
def __init__(self, domain):
|
|
|
|
self.domain = domain
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return repr("No system user for domain %s" % (self.domain))
|
|
|
|
|
2005-09-29 15:47:47 +02:00
|
|
|
class InvalidPopUser(Exception):
|
|
|
|
"""This exception is raised if an invalid POP3/IMAP user has been specified."""
|
|
|
|
def __init__(self, domain, username):
|
|
|
|
self.domain = domain
|
|
|
|
self.username = username
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "Invalid POP3/IMAP user %s in domain %s." % (self.username, self.domain)
|
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
class Domain:
|
|
|
|
"""A Domain representation object with service methods."""
|
|
|
|
def __init__(self, domain):
|
|
|
|
self.cnx = psycopg.connect("user=%(dbuser)s password=%(dbpassword)s dbname=%(dbname)s" % Settings.dbsettings)
|
|
|
|
self.domain = domain
|
|
|
|
self.validate_domain()
|
|
|
|
|
|
|
|
def validate_domain(self):
|
|
|
|
"""This function validates whether the given domain is allowed.
|
|
|
|
That means that the domain needs to be registered in the database.
|
2005-09-28 19:57:51 +02:00
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
If the domain is invalid InvalidDomain is raised."""
|
|
|
|
cr = self.cnx.cursor()
|
|
|
|
|
|
|
|
cr.execute("SELECT * FROM domain WHERE domainname=%(name)s" %
|
|
|
|
{'name': psycopg.QuotedString(self.domain)})
|
|
|
|
self.cnx.commit()
|
|
|
|
|
|
|
|
result = cr.fetchall()
|
|
|
|
if (not result):
|
|
|
|
raise InvalidDomain(self)
|
|
|
|
|
|
|
|
def __str__(self):
|
2005-09-29 15:47:47 +02:00
|
|
|
return str(self.domain)
|
2005-09-29 00:02:42 +02:00
|
|
|
|
|
|
|
def getSysuser(self):
|
|
|
|
"""Gets the system user id of the domain."""
|
|
|
|
cr = self.cnx.cursor()
|
|
|
|
|
|
|
|
cr.execute("""SELECT sysuser.name FROM domain, sysuser
|
|
|
|
WHERE domain.domainname=%(name)s
|
|
|
|
AND domain.sysuserid=sysuser.sysuserid""" %
|
|
|
|
{'name': psycopg.QuotedString(self.domain)})
|
|
|
|
self.cnx.commit()
|
|
|
|
|
|
|
|
result = cr.fetchall()
|
|
|
|
if (not result):
|
|
|
|
raise NoSysuserForDomain(self)
|
|
|
|
# return row 0, field 0
|
|
|
|
return result[0][0]
|
|
|
|
|
|
|
|
def getNextPopUser(self):
|
|
|
|
"""Gets the user id of the next available POP3/IMAP-user for the domain."""
|
|
|
|
cr = self.cnx.cursor()
|
|
|
|
|
|
|
|
sysuser = self.getSysuser()
|
|
|
|
|
|
|
|
cr.execute("""SELECT max(id) FROM mailpasswd WHERE
|
|
|
|
id LIKE %(username)s""" %
|
|
|
|
{'username': psycopg.QuotedString(sysuser+'%')})
|
|
|
|
self.cnx.commit()
|
|
|
|
|
|
|
|
result = cr.fetchall()
|
|
|
|
if (not result):
|
|
|
|
return sysuser + "p01"
|
|
|
|
|
|
|
|
maxpopuser = result[0][0]
|
|
|
|
num = int(maxpopuser[len(sysuser)+1:])+1
|
|
|
|
return "%sp%d" % (sysuser, num)
|
|
|
|
|
|
|
|
def makePopUser(self, password):
|
|
|
|
"""Creates a new POP3/IMAP-user for the domain using the given password."""
|
|
|
|
cr = self.cnx.cursor()
|
|
|
|
|
|
|
|
sysuser = self.getSysuser()
|
|
|
|
popaccount = self.getNextPopUser()
|
|
|
|
crypted = PasswordTools.md5_crypt_password(password)
|
|
|
|
uid = pwd.getpwnam(sysuser)[2]
|
|
|
|
gid = grp.getgrnam(Settings.popgroup)[2]
|
|
|
|
homedir = Settings.pophome + popaccount
|
2005-09-28 19:57:51 +02:00
|
|
|
|
2005-09-29 11:45:48 +02:00
|
|
|
os.mkdir(homedir, 0755)
|
2005-09-29 00:02:42 +02:00
|
|
|
os.system("maildirmake \"%s/Maildir\"" % (homedir))
|
2005-09-29 11:45:48 +02:00
|
|
|
os.system("chown -R %s.%s %s" % ( sysuser, Settings.popgroup, homedir ))
|
2005-09-28 19:57:51 +02:00
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
cr = self.cnx.cursor()
|
|
|
|
cr.execute("""INSERT INTO mailpasswd (id, crypt, clear, uid, gid, home)
|
|
|
|
VALUES (%(id)s, %(crypt)s, %(clear)s, %(uid)d, %(gid)d, %(home)s)""" % {
|
|
|
|
'id': psycopg.QuotedString(popaccount),
|
|
|
|
'crypt': psycopg.QuotedString(crypted),
|
|
|
|
'clear': psycopg.QuotedString(password),
|
|
|
|
'uid': uid, 'gid': gid,
|
|
|
|
'home': psycopg.QuotedString(homedir)})
|
|
|
|
self.cnx.commit()
|
2005-09-28 19:57:51 +02:00
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
text = """A new POP3/IMAP account has been created
|
|
|
|
Domain: %(domain)s
|
|
|
|
User: %(user)s
|
|
|
|
Password: %(password)s""" % {'domain': self.domain,
|
|
|
|
'user': popaccount,
|
|
|
|
'password': password}
|
|
|
|
themail = MIMEText(text)
|
|
|
|
themail['Subject'] = "A new POP3/IMAP account has been created"
|
|
|
|
themail['From'] = Settings.mailsender
|
|
|
|
themail['To'] = Settings.mailreceiver
|
2005-09-28 19:57:51 +02:00
|
|
|
|
2005-09-29 00:02:42 +02:00
|
|
|
s = smtplib.SMTP()
|
|
|
|
s.connect()
|
|
|
|
s.sendmail(Settings.mailsender, [Settings.mailreceiver], themail.as_string())
|
|
|
|
s.close()
|
2005-09-29 15:47:47 +02:00
|
|
|
|
|
|
|
def listPopUsers(self):
|
|
|
|
sysuser = self.getSysuser()
|
|
|
|
|
|
|
|
cr = self.cnx.cursor()
|
|
|
|
cr.execute("SELECT id FROM mailpasswd WHERE id LIKE %(user)s" % {
|
|
|
|
'user': psycopg.QuotedString(sysuser + '%')})
|
|
|
|
self.cnx.commit()
|
|
|
|
|
|
|
|
result = cr.fetchall()
|
|
|
|
return [line[0] for line in result]
|
|
|
|
|
|
|
|
def hasPopUser(self, username):
|
|
|
|
"""Checks whether the specified POP3/IMAP user exists in the domain."""
|
2005-10-07 06:43:12 +02:00
|
|
|
return ([user for user in self.listPopUsers() if (user == username)])
|
2005-09-29 15:47:47 +02:00
|
|
|
|
|
|
|
def updatePopPassword(self, username, password=PasswordTools.generate_password()):
|
|
|
|
"""Updates the password of the given POP3/IMAP user."""
|
|
|
|
if self.hasPopUser(username):
|
2005-10-07 06:43:12 +02:00
|
|
|
crypted = PasswordTools.md5_crypt_password(password)
|
|
|
|
|
|
|
|
cr = self.cnx.cursor()
|
|
|
|
cr.execute("UPDATE mailpasswd SET clear=%(clear)s, crypt=%(crypt)s WHERE id=%(user)s" % {
|
|
|
|
'clear': psycopg.QuotedString(password),
|
|
|
|
'crypt': psycopg.QuotedString(crypted),
|
|
|
|
'user': psycopg.QuotedString(username)})
|
|
|
|
self.cnx.commit()
|
|
|
|
print("updated password of user %s to %s" % (username, password))
|
2005-09-29 15:47:47 +02:00
|
|
|
else:
|
|
|
|
raise InvalidPopUser(self, username)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
domain = Domain('efs-sohland.de')
|
|
|
|
# check for not existing user
|
|
|
|
try:
|
|
|
|
domain.updatePopPassword('usr03p2', 'test')
|
|
|
|
except InvalidPopUser, ipu:
|
|
|
|
print ipu
|
|
|
|
domain.updatePopPassword('usr05p2')
|