def delete(self): log = logging.getLogger('flscp') conf = FLSConfig.getInstance() # delete! # 1. remove credentials # 2. remove entry from /etc/postfix/fls/aliases # 3. remove entry from /etc/postfix/fls/mailboxes # 4. remove entry from /etc/postfix/fls/sender-access # 5. remove entry from mail_users # 7. remove complete mails in /var/mail/,... directory # 6. postmap all relevant entries self.updateCredentials() self.updateMailboxes() self.updateAliases() self.updateSenderAccess() if self.exists(): db = MailDatabase.getInstance() cx = db.getCursor() query = ('SELECT mail_id, mail_addr FROM mail_users WHERE mail_id = %s') cx.execute(query, (self.id,)) for (mail_id, mail_addr,) in cx: (mail, domain) = mail_addr.split('@') path = '%s/%s/%s/' % (conf.get('mailserver', 'basemailpath'), domain, mail) if os.path.exists(path): try: os.removedirs(path) except Exception as e: log.warning('Error when removing directory: %s' % (e,)) query = ('DELETE FROM mail_users WHERE mail_id = %s') cx.execute(query, (self.id,)) cx.close()
def getDomains(self): data = [] db = MailDatabase.getInstance() cursor = db.getCursor() query = ( 'SELECT domain_id, domain_parent, domain_name, ipv6, ipv4, domain_gid, domain_uid, domain_created, \ domain_last_modified, domain_srvpath, domain_status FROM domain' ) cursor.execute(query) for (domain_id, domain_parent, domain_name, ipv6, ipv4, gid, uid, created, modified, srvpath, state) in cursor: data.append( { 'id': domain_id, 'name': domain_name, 'ipv6': ipv6, 'ipv4': ipv4, 'gid': gid, 'uid': uid, 'srvpath': srvpath, 'parent': domain_parent, 'created': created, 'modified': modified, 'state': state } ) cursor.close() return data
def getDns(self, domain = None): data = [] db = MailDatabase.getInstance() cursor = db.getCursor() if domain is None: query = ( 'SELECT dns_id, domain_id FROM dns' ) cursor.execute(query) else: query = ( 'SELECT dns_id, domain_id FROM dns WHERE domain_id = %s' ) cursor.execute(query, (domain,)) dnsIds = [] for (dns_id, domain_id) in cursor: dnsIds.append(dns_id) for i in dnsIds: dns = Dns(i) dns.load() data.append(dns.toDict()) del(dns) cursor.close() return data
def create(self): # 1. create entry in domain # 2. insert the things in domain file of postfix # 3. hash the domain file # 4. create default dns entries? # 5. generate a bind file # 6. reload bind if self.exists(): raise KeyError('Domain "%s" already exists!' % (self.name,)) # is it a valid domain? if len(self.name) <= 0: raise ValueError('No valid domain given!') self.created = time.time() self.modified = time.time() db = MailDatabase.getInstance() cx = db.getCursor() self.state = Domain.STATE_CREATE query = ( 'INSERT INTO domain (domain_parent, domain_name, ipv6, ipv4, domain_gid, domain_uid, domain_created, \ domain_last_modified, domain_srvpath, domain_status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)' ) cx.execute( query, ( self.parent, self.name, self.ipv6, self.ipv4, self.gid, self.uid, self.created, self.modified, self.srvpath, self.state ) ) db.commit()
def setState(self, state): db = MailDatabase.getInstance() cx = db.getCursor() query = ('UPDATE mail_users SET status = %s WHERE mail_id = %s') cx.execute(query, (state, self.id)) db.commit() cx.close() self.state = state
def exists(self): # check if entry exists already in mail_users! db = MailDatabase.getInstance() cx = db.getCursor() query = ('SELECT mail_id FROM mail_users WHERE mail_addr = %s') cx.execute(query, ('%s@%s' % (self.mail, self.domain),)) exists = len(cx.fetchall()) > 0 cx.close() return exists
def exists(self): exists = False db = MailDatabase.getInstance() cx = db.getCursor() query = ('SELECT domain_id FROM domain WHERE domain_name = %s and domain_parent = %s') cx.execute(query, (self.name, self.parent)) exists = len(cx.fetchall()) > 0 cx.close() return exists
def getByEMail(self, mail): log = logging.getLogger('flscp') ma = MailAccount() db = MailDatabase.getInstance() cx = db.getCursor() query = ('SELECT mail_id, mail_acc, mail_pass, mail_forward, domain_id, mail_type, sub_id, status, quota, mail_addr, alternative_addr, authcode, authvalid, enabled FROM mail_users WHERE mail_addr = %s') cx.execute(query, (mail.lower(),)) if cx is None: log.warning('Execution failed in MailAccount::getByEMail(%s).' % (mail,)) return None try: resultRow = cx.fetchone() except: log.critical('Got error in MailAccount::getByEMail: %s' % (e,)) try: cx.close() except: pass return None else: if resultRow is None: log.info('No user found by mail %s' % (mail,)) return None try: (mail_id, mail_acc, mail_pass, mail_forward, domain_id, mail_type, sub_id, status, quota, mail_addr, alternative_addr, authcode, authvalid, enabled) = resultRow ma.id = mail_id ma.quota = quota ma.mail = mail_acc ma.hashPw = mail_pass ma.domain = mail_addr.split('@')[1] ma.altMail = alternative_addr ma.forward = mail_forward.split(',') ma.type = MailAccount.TYPE_ACCOUNT if mail_type == 'fwdsmtp': ma.type = MailAccount.TYPE_FWDSMTP elif mail_type == 'forward': ma.type = MailAccount.TYPE_FORWARD ma.status = status ma.authCode = authcode ma.authValid = authvalid ma.enabled = bool(enabled) except Exception as e: log.critical('Got error in MailAccount::getByEMail: %s' % (e,)) cx.close() return None else: cx.close() self = ma return self
def changePassword(self, pwd): self.pw = pwd self.hashPassword() db = MailDatabase.getInstance() try: cx = db.getCursor() query = ( 'UPDATE mail_users SET mail_pass = %s, authcode = NULL, authvalid = NULL WHERE mail_id = %s' ) cx.execute(query, (self.hashPw, self.id)) db.commit() cx.close() except: return False else: self.updateCredentials() return True
def createAuthCode(self): self.authCode = hashlib.md5(str(hash(random.SystemRandom().uniform(0, 1000))).encode('utf-8')).hexdigest() self.authValid = datetime.datetime.now() + datetime.timedelta(hours=2) db = MailDatabase.getInstance() try: cx = db.getCursor() query = ( 'UPDATE mail_users SET authcode = %s, authvalid = %s WHERE mail_id = %s' ) cx.execute(query, (self.authCode, self.authValid.strftime('%Y-%m-%d %H:%M:%S'), self.id)) db.commit() cx.close() except: return False else: return True
def update(self, oldDomain = None): # is it a valid domain? if len(self.name) <= 0: raise ValueError('No valid domain given!') self.modified = time.time() db = MailDatabase.getInstance() cx = db.getCursor() self.state = Domain.STATE_OK query = ( 'UPDATE domain SET ipv6 = %s, ipv4 = %s, domain_gid = %s, domain_uid = %s, domain_last_modified = %s, \ domain_srvpath = %s, domain_status = %s WHERE domain_id = %s' ) cx.execute( query, ( self.ipv6, self.ipv4, self.gid, self.uid, self.modified, self.srvpath, self.state, self.id ) ) db.commit() # if we have updated, we now have to move the data folder? if oldDomain.srvpath != self.srvpath and len(oldDomain) > 0 and len(self.srvpath) > 0: parentFolder = self.srvpath if parentFolder == '/': parentFolder = parentFolder[:-1] parentFolderSplit = parentFolder.split('/') parentFolder = '/'.join(parentFolderSplit[:-1]) if not os.path.exists(parentFolder): os.makedirs(parentFolder) os.rename(oldDomain.srvpath, self.srvpath) elif len(self.srvpath) > 0: if not os.path.exists(self.srvpath): os.makedirs(self.srvpath) os.chmod(self.srvpath, 0o750) os.chown(self.srvpath, self.uid, self.gid) # now create the default structure os.makedirs(os.path.join(self.srvpath, 'htdocs')) os.chmod(os.path.join(self.srvpath, 'htdocs'), 0o750) os.chown(os.path.join(self.srvpath, 'htdocs'), self.uid, self.gid)
def getByName(dom, name): log = logging.getLogger('flscp') db = MailDatabase.getInstance() cx = db.getCursor() query = ('SELECT domain_id, domain_name FROM domain WHERE domain_name = %s') try: cx.execute(query, (name,)) (domain_id, domain_name) = cx.fetchone() dom = Domain() dom.id = domain_id dom.name = domain_name except Exception as e: dom = None log.warning('Could not find domain.') raise KeyError('Domain "%s" could not be found!' % (name,)) finally: cx.close() self = dom return self
def getMails(self): db = MailDatabase.getInstance() domainsRaw = self.getDomains() domains = {} for f in domainsRaw: domains[f['id']] = f['name'] data = [] cursor = db.getCursor() query = ( 'SELECT m.mail_id, m.mail_acc, m.mail_addr, m.mail_type, m.mail_forward, m.quota, m.status, m.domain_id, m.alternative_addr, \ m.enabled, q.bytes FROM mail_users m LEFT JOIN quota_dovecot q ON m.mail_addr = q.username' ) cursor.execute(query) for (mail_id, mail_acc, mail_addr, mail_type, mail_forward, quota, status, domain_id, alternative_addr, enabled, usedBytes) in cursor: quotaSts = 0.00 if usedBytes is not None: if usedBytes > 0 and quota > 0: quotaSts = round(usedBytes*100/quota, 2) data.append( { 'id': mail_id, 'mail': mail_acc, 'altMail': alternative_addr if alternative_addr is not None else '', 'forward': mail_forward.split(',') if mail_forward != '_no_' else [], 'domain': domains[domain_id], 'domainId': domain_id, 'state': status, 'type': mail_type, 'pw': '', 'genPw': False, 'enabled': bool(enabled), 'quota': quota, 'quotaSts': quotaSts } ) cursor.close() return data
def load(self): log = logging.getLogger('flscp') if self.id is None: log.info('Can not load data for a domain with no id!') return False state = False db = MailDatabase.getInstance() cx = db.getCursor() query = ( 'SELECT domain_id, domain_parent, domain_name, ipv6, ipv4, domain_gid, domain_uid, domain_srvpath, \ domain_created, domain_last_modified, domain_status FROM domain WHERE domain_id = %s LIMIT 1' ) try: cx.execute(query, (self.id,)) for (did, parent, domain_name, ipv6, ipv4, gid, uid, srvpath, created, modified, state) in cx: self.id = did self.parent = parent self.name = domain_name self.ipv6 = ipv6 self.ipv4 = ipv4 self.gid = gid self.uid = uid self.srvpath = srvpath self.created = created self.modified = modified self.state = state except Exception as e: log.warning('Could not load the domain %s because of %s' % (self.id, str(e))) state = False else: state = True finally: cx.close() return state
def create(self): log = logging.getLogger('flscp') # create: # 1. update mail_users # 2. update credentials, if given # 3. update /etc/postfix/fls/mailboxes # 4. update aliases # 5. update sender-access (we could later be implement to restrict sending!) # postmap all relevant entries if self.exists(): # already exists! raise KeyError('Mail "%s@%s" already exists!' % (self.mail, self.domain)) # get domain id! (if not exist: create!) try: d = Domain.getByName(self.domain) except KeyError: raise # pw entered? if len(self.pw.strip()) > 0: self.hashPassword() db = MailDatabase.getInstance() cx = db.getCursor() query = ( 'INSERT INTO mail_users (mail_acc, mail_pass, mail_forward, domain_id, mail_type, status, quota, mail_addr, alternative_addr, enabled) ' \ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' ) cx.execute( query, ( self.mail, self.hashPw, ','.join(self.forward), d.id, self.type, self.state, self.quota, '%s@%s' % (self.mail, self.domain), self.altMail, str(int(self.enabled)) ) ) db.commit() log.debug('executed mysql statement: %s' % (cx.statement,)) id = cx.lastrowid if id is None: cx.close() return False else: self.id = id # update credentials... self.updateCredentials() # now update mailboxes files! if not self.updateMailboxes(): cx.close() return False # update aliases if not self.updateAliases(): # remove entry from updateMailboxes? cx.close() return False # update sender-access if not self.updateSenderAccess(): # remove entry from updateMailboxes and Aliases ? cx.close() return False cx.close() # all best? Than go forward and update set state,... self.setState(MailAccount.STATE_OK) # notify if len(self.altMail) > 0: m = Mailer(self) state = False if self.type == MailAccount.TYPE_ACCOUNT \ or self.type == MailAccount.TYPE_FWDSMTP: state = m.newAccount() else: state = m.newForward() if state: log.info('User is notified about account change!') else: log.warning('Unknown error while notifying user!') else: log.info('User is not notified because we have no address of him!') # reset info self.pw = '' self.hashPw = '' self.genPw = False
def save(self): log = logging.getLogger('flscp') conf = FLSConfig.getInstance() if self.state == MailAccount.STATE_CREATE: self.create() return elif self.state == MailAccount.STATE_DELETE: self.delete() return elif self.state == MailAccount.STATE_QUOTA: self.recalculateQuota() return # now save! # -> see create - but if key changed (mail address!) remove # all entries before and rename folder in /var/mail,... directory # get original data! if not self.exists(): self.create() # get domain id! (if not exist: create!) try: d = Domain.getByName(self.domain) except KeyError: raise # pw entered? if len(self.pw.strip()) > 0: log.info('Hash password for user %s' % (self.mail,)) self.hashPassword() db = MailDatabase.getInstance() cx = db.getCursor() query = ('SELECT mail_id, mail_addr, mail_type FROM mail_users WHERE mail_id = %s') cx.execute(query, (self.id,)) (mail_id, mail_addr, mail_type) = cx.fetchone() (mail, domain) = mail_addr.split('@') cx.close() cx = db.getCursor() if (self.type == MailAccount.TYPE_ACCOUNT and self.hashPw != '') \ or (self.type == MailAccount.TYPE_FWDSMTP and self.hashPw != '') \ or self.type == MailAccount.TYPE_FORWARD: query = ( 'UPDATE mail_users SET mail_acc = %s, mail_pass = %s, mail_forward = %s, ' \ 'domain_id = %s, mail_type = %s, status = %s, quota = %s, mail_addr = %s, ' \ 'alternative_addr = %s, enabled = %s WHERE mail_id = %s' ) params = ( self.mail, self.hashPw, ','.join(self.forward), d.id, self.type, self.state, self.quota, '%s@%s' % (self.mail, self.domain), self.altMail, str(int(self.enabled)), self.id ) else: query = ( 'UPDATE mail_users SET mail_acc = %s, mail_forward = %s, ' \ 'domain_id = %s, mail_type = %s, status = %s, quota = %s, mail_addr = %s, ' \ 'alternative_addr = %s, enabled = %s WHERE mail_id = %s' ) params = ( self.mail, ','.join(self.forward), d.id, self.type, self.state, self.quota, '%s@%s' % (self.mail, self.domain), self.altMail, str(int(self.enabled)), self.id ) cx.execute( query, params ) db.commit() log.debug('executed mysql statement: %s' % (cx.statement,)) # update credentials... # if pw was entered or type changed if mail_type != self.type or self.pw.strip() != '': self.updateCredentials() # now update mailboxes files! if not self.updateMailboxes(oldMail=mail, oldDomain=domain): cx.close() return False # update aliases if not self.updateAliases(oldMail=mail, oldDomain=domain): # remove entry from updateMailboxes? cx.close() return False # update sender-access if not self.updateSenderAccess(oldMail=mail, oldDomain=domain): # remove entry from updateMailboxes and Aliases ? cx.close() return False # rename folders - but only if target directory does not exist # (we had to throw fatal error if target directory exists!) oldPath = '%s/%s/%s/' % (conf.get('mailserver', 'basemailpath'), domain, mail) path = '%s/%s/%s/' % (conf.get('mailserver', 'basemailpath'), self.domain, self.mail) if os.path.exists(oldPath): if os.path.exists(path): log.error('Could not move "%s" to "%s", because it already exists!' % (path,)) else: try: os.rename(oldPath, path) except OSError as e: log.warning('Got OSError - Does directory exists? (%s)' % (e,)) except Exception as e: log.warning('Got unexpected exception (%s)!' % (e,)) cx.close() # all best? Than go forward and update set state,... self.setState(MailAccount.STATE_OK) # notify if len(self.altMail) > 0: m = Mailer(self) state = False if self.type == MailAccount.TYPE_ACCOUNT \ or self.type == MailAccount.TYPE_FWDSMTP: state = m.changeAccount() else: state = m.changeForward() if state: log.info('User is notified about account change!') else: log.warning('Unknown error while notifying user!') else: log.info('User is not notified because we have no address of him!') # reset info self.pw = '' self.hashPw = '' self.genPw = False