コード例 #1
0
ファイル: mail.py プロジェクト: monofox/flscp
	def updateSenderAccess(self, oldMail = None, oldDomain = None):
		conf = FLSConfig.getInstance()
		
		mailAddr = '%s@%s' % (self.mail, self.domain)
		if oldMail is None:
			oldMail = self.mail
		if oldDomain is None:
			oldDomain = self.domain
		mailOldAddr = '%s@%s' % (oldMail, oldDomain)

		cnt = []
		with open(conf.get('mailserver', 'senderaccess'), 'r') as f:
			cnt = f.read().split('\n')

		cnt = [f for f in cnt if (('\t' in f and f[0:f.index('\t')] != mailOldAddr) or f[0:1] == '#') and len(f.strip()) > 0]

		# now add data:
		if self.state in (MailAccount.STATE_CHANGE, MailAccount.STATE_CREATE):
			if self.enabled:
				cnt.append('%s\t%s' % (mailAddr, 'OK'))
			else:
				cnt.append('%s\t%s' % (mailAddr, '4508q 4.2.1 User is disabled at the moment'))

		# now sort file
		cnt.sort()

		# now write back
		try:
			with open(conf.get('mailserver', 'senderaccess'), 'w') as f:
				f.write('\n'.join(cnt))
		except:
			return False
		else:
			# postmap
			return hashPostFile(conf.get('mailserver', 'senderaccess'), conf.get('mailserver', 'postmap'))
コード例 #2
0
ファイル: mail.py プロジェクト: monofox/flscp
	def updateMailboxes(self, oldMail = None, oldDomain = None):
		conf = FLSConfig.getInstance()
		
		mailAddr = '%s@%s' % (self.mail, self.domain)
		if oldMail is None:
			oldMail = self.mail
		if oldDomain is None:
			oldDomain = self.domain
		mailOldAddr = '%s@%s' % (oldMail, oldDomain)

		cnt = []
		with open(conf.get('mailserver', 'mailboxes'), 'r') as f:
			cnt = f.read().split('\n')

		cnt = [f for f in cnt if (('\t' in f and f[0:f.index('\t')] != mailOldAddr) or f[0:1] == '#') and len(f.strip()) > 0]

		# now add data:
		if self.state in (MailAccount.STATE_CHANGE, MailAccount.STATE_CREATE):
			if self.type == MailAccount.TYPE_ACCOUNT:
				cnt.append('%s\t%s%s%s%s' % (mailAddr, self.domain, os.sep, self.mail, os.sep))

		# now sort file
		cnt.sort()

		# now write back
		try:
			with open(conf.get('mailserver', 'mailboxes'), 'w') as f:
				f.write('\n'.join(cnt))
		except:
			return False
		else:
			# postmap
			return hashPostFile(conf.get('mailserver', 'mailboxes'), conf.get('mailserver', 'postmap'))
コード例 #3
0
ファイル: mail.py プロジェクト: monofox/flscp
	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()
コード例 #4
0
ファイル: mail.py プロジェクト: monofox/flscp
	def updateCredentials(self):
		conf = FLSConfig.getInstance()
		if not conf.getboolean('features', 'sasldb'):
			return None

		db = SaslDatabase.getInstance()

		if self.state == MailAccount.STATE_DELETE or len(self.hashPw.strip()) <= 0:
			db.delete(self.credentialsKey())
		else:
			if db.exists(self.credentialsKey()):
				db.update(self.credentialsKey(), self.pw)
			else:
				db.add(self.credentialsKey(), self.pw)
コード例 #5
0
ファイル: mail.py プロジェクト: monofox/flscp
	def recalculateQuota(self):
		log = logging.getLogger('flscp')
		conf = FLSConfig.getInstance()

		cmd = shlex.split('%s quota recalc -u %s' % (conf.get('mailserver', 'doveadm'), '%s@%s' % (self.mail, self.domain)))
		state = True
		with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
			out = p.stdout.read()
			err = p.stderr.read()
			if len(out) > 0:
				log.info(out)
			if len(err) > 0:
				log.warning(err)
				state = False

		self.setState(MailAccount.STATE_OK)
		return state
コード例 #6
0
ファイル: mail.py プロジェクト: monofox/flscp
	def updateAliases(self, oldMail = None, oldDomain = None):
		conf = FLSConfig.getInstance()
		
		mailAddr = '%s@%s' % (self.mail, self.domain)
		if oldMail is None:
			oldMail = self.mail
		if oldDomain is None:
			oldDomain = self.domain
		mailOldAddr = '%s@%s' % (oldMail, oldDomain)

		cnt = []
		with open(conf.get('mailserver', 'aliases'), 'r') as f:
			cnt = f.read().split('\n')

		cnt = [f for f in cnt if (('\t' in f and f[0:f.index('\t')] != mailOldAddr) or f[0:1] == '#') and len(f.strip()) > 0]

		# now add data:
		if self.state in (MailAccount.STATE_CHANGE, MailAccount.STATE_CREATE):
			forward = copy.copy(self.forward)
			# remove all empty things
			i = 0
			for f in forward:
				if len(f.strip()) <= 0:
					del(forward[i])

				i += 1
				
			if self.type == MailAccount.TYPE_ACCOUNT:
				forward.insert(0, mailAddr)
			forward = list(set(forward))
			cnt.append('%s\t%s' % (mailAddr, ','.join(forward)))

		# now sort file
		cnt.sort()

		# now write back
		try:
			with open(conf.get('mailserver', 'aliases'), 'w') as f:
				f.write('\n'.join(cnt))
		except:
			return False
		else:
			# postmap
			return hashPostFile(conf.get('mailserver', 'aliases'), conf.get('mailserver', 'postmap'))
コード例 #7
0
ファイル: mail.py プロジェクト: monofox/flscp
	def authenticate(self, mech, pwd, cert = None):
		conf = FLSConfig.getInstance()
		log = logging.getLogger('flscp')
		data = {
			'userdb_user': '',
			'userdb_home': '',
			'userdb_uid': '',
			'userdb_gid': '',
			'userdb_mail': '',
			'quota_rule': '',
			'nopassword': 1
		}
		localPartDir = os.path.join(conf.get('mailserver', 'basemailpath'), 'virtual')
		homeDir = os.path.join(localPartDir, self.domain, self.mail)
		username = ('%s@%s' % (self.mail, self.domain)).lower()
		if self.hashPw == '_no_':
			log.debug('User %s can not login, because password is disabled!' % (self.getMailAddress(),))
			return False
		
		s = SaltEncryption()

		if mech in ['PLAIN', 'LOGIN']:
			state = s.compare(pwd, self.hashPw)
		elif mech in ['EXTERNAL']:
			state = (cert.lower() == 'valid' and pwd == '')
		else:
			log.debug('User %s can not login: unsupported auth mechanism "%s"' % (self.getMailAddress(), mech))
			state = False

		if state:
			data['userdb_user'] = username
			data['userdb_home'] = self.getHomeDir()
			data['userdb_uid'] = conf.get('mailserver', 'uid')
			data['userdb_gid'] = conf.get('mailserver', 'gid')
			data['userdb_mail'] = self.getMailDirFormat()
			data['quota_rule'] = '*:storage=%sb' % (self.quota,)

			return data

		else:
			return False
コード例 #8
0
ファイル: mail.py プロジェクト: monofox/flscp
	def __init__(self):
		conf = FLSConfig.getInstance()
		self.id = None
		self.type = MailAccount.TYPE_ACCOUNT
		self.state = MailAccount.STATE_OK
		if conf is not None:
			self.quota = conf.getint('userdefault', 'quota')
		else:
			# 100 MB
			self.quota = 104857600
		self.quotaSts = 0.0
		self.mail = ''
		self.domain = ''
		self.pw = ''
		self.hashPw = ''
		self.genPw = False
		self.altMail = ''
		self.forward = []
		self.authCode = None
		self.authValid = None
		self.enabled = True
コード例 #9
0
ファイル: mail.py プロジェクト: monofox/flscp
	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
コード例 #10
0
ファイル: mail.py プロジェクト: monofox/flscp
	def getHomeDir(self):
		conf = FLSConfig.getInstance()
		return os.path.join(conf.get('mailserver', 'basemailpath'), 'virtual', self.domain, self.mail)
コード例 #11
0
ファイル: flscpserver.py プロジェクト: monofox/flscp
__author__  = 'Lukas Schreiner'
__copyright__ = 'Copyright (C) 2013 - 2015 Website-Team Friedrich-List-Schule-Wiesbaden'
__version__ = '0.8'

FORMAT = '%(asctime)-15s %(message)s'
formatter = logging.Formatter(FORMAT, datefmt='%b %d %H:%M:%S')
log = logging.getLogger('flscp')
log.setLevel(logging.INFO)
hdlr = ColorizingStreamHandler()
hdlr.setFormatter(formatter)
log.addHandler(hdlr)

workDir = os.path.dirname(os.path.realpath(__file__))

# search for config
conf = FLSConfig()
fread = conf.read(
		[
			'server.ini', os.path.expanduser('~/.flscpserver.ini'), os.path.expanduser('~/.flscp/server.ini'),
			os.path.expanduser('~/.config/flscp/server.ini'), '/etc/flscp/server.ini', '/usr/local/etc/flscp/server.ini'
		]
	)
if len(fread) <= 0:
	sys.stderr.write(
			'Missing config file in one of server.ini, ~/.flscpserver.ini, ~/.flscp/server.ini, ~/.config/flscp/server.ini, \
			/etc/flscp/server.ini or /usr/local/etc/flscp/server.ini!\n'
		)
	sys.exit(255)
else:
	log.debug('Using config files "%s"' % (fread.pop(),))
コード例 #12
0
ファイル: database.py プロジェクト: monofox/flscp
	def __init__(self):
		super().__init__()
		SaslDatabase.__instance = self
		self.conf = FLSConfig.getInstance()
		self.log = logging.getLogger('flscp')