#!/usr/bin/env python3
# coding:utf-8

import os
import time
import re
from cplCommand import LocalClient, SSHClient

msg0001 = "something, that isn't a directory exist at '{0}'"
msg0002 = "successfully moved already existing home '{0}' to '{1}'"
msg0003 = "fixed ownership of '{0}'"
msg0004 = "failed to fix ownership of '{0}'"
msg0005 = "fixed permissions of '{0}'"
msg0006 = "failed to fix permissions of '{0}'"
msg0007 = "copied skeleton '{0}' to '{1}'"
msg0008 = "failed to copy skeleton '{0}' to '{1}'"
msg0009 = "failed to create directory '{0}'"
msg0010 = "setting folder permissions failed on '{0}'"
msg0011 = "failed to move existing folder '{0}' with wrong uid out of the way"
msg0012 = "quotatool reported: {0}"
msg0013 = "quota set on '{0}' for '{1}' to {2}/{3}, {4}/{5}"
msg0014 = "unable to figure home directory path out" 
msg0015 = "home directory '{0}' is already gone"
msg0016 = "successfully moved home of '{0}' to archive"
msg0017 = "failed to move home of '{0}' to archive"
msg0018 = "no home for '{0}' exists, auto creating new home"
msg0019 = "no home for '{0}' exists, auto creation not allowed by config"
msg0020 = "fetching configuration for: '{0}'"
msg0021 = "failed to connect to ssh host: '{0}'"
msg0022 = "successfully removed home of '{0}'"
msg0023 = "failed to remove home of '{0}'"

class UserHome():


	def __init__(self, postcmd):

		# get kadmin configuration
		self.userhome_config = self.__getUserHomeConfig()

		self.uh = self.userhome_config['handleuserhome']
		self.uq = self.userhome_config['handleuserquota']
		if not self.uh: return
		self.uh_cmd = self.__getUHClient()

		# for archives
		self.timestamp = time.strftime('%Y-%m-%d_%H-%M-%S')

		# exit here if we don't have any ldap data
		if postcmd.action == 'remove': return

		# get users homedir
		if self.userhome_config['homebase']:
			self.hbase = self.userhome_config['homebase']
			self.home = os.path.join(self.hbase, self.cn)
		elif self.ldap_data['homeDirectory']:
			self.home = self.ldap_data['homeDirectory'][0].decode()
			self.hbase = os.sep.join(self.home.split(os.sep)[:-1])
		else:
			self.exit(1, msg0014)

		# ensure existence of user home dir base
		if self.uh_cmd.exists(self.hbase):
			if not self.uh_cmd.isdir(self.hbase):
				self.exit(1, msg0001.format(self.hbase))
		else:
			self.uh_cmd.mkdir(self.hbase, 0o0755, 'root', 'root')

		# ensure existence of orphan dir
		if self.userhome_config['orphandir']:
			self.orphandir = self.userhome_config['orphandir']
		else:
			self.orphandir = os.path.join(self.hbase, \
				self.userhome_config['orphandefault'])

		self.oowner = self.userhome_config['orphanowner']
		self.ogroup = self.userhome_config['orphangroup']
		self.omaskd = self.userhome_config['orphanmaskd']
		self.omaskf = self.userhome_config['orphanmaskf']

		if self.uh_cmd.exists(self.orphandir):
			if not self.uh_cmd.isdir(self.orphandir):
				self.exit(1, msg0001.format(self.orphandir))

			self.checkPermissions(self.orphandir, self.oowner, \
				self.ogroup, self.omaskd, self.omaskf)
		else:
			self.uh_cmd.mkdir(self.orphandir, self.omaskd, \
				self.oowner, self.ogroup)

		# ensure existence of archive dir
		if self.userhome_config['archivedir']:
			self.archivedir = self.userhome_config['archivedir']
		else:
			self.archivedir = os.path.join(self.hbase, \
				self.userhome_config['archivedefault'])

		self.aowner = self.userhome_config['archiveowner']
		self.agroup = self.userhome_config['archivegroup']
		self.amaskd = self.userhome_config['archivemaskd']
		self.amaskf = self.userhome_config['archivemaskf']

		if self.uh_cmd.exists(self.archivedir):
			if not self.uh_cmd.isdir(self.archivedir):
				self.exit(1, msg0001.format(self.archivedir))

			self.checkPermissions(self.archivedir, self.aowner, \
				self.agroup, self.amaskd, self.amaskf)
		else:
			self.uh_cmd.mkdir(self.archivedir, self.amaskd, \
				self.aowner, self.agroup)

		# set some vars
		self.uid = self.ldap_data['uidNumber'][0].decode()
		self.gid = self.ldap_data['gidNumber'][0].decode()
		self.skel = self.userhome_config['skeleton']
		self.hmaskd = self.userhome_config['hmaskd']
		self.hmaskf = self.userhome_config['hmaskf']


	def __getUserHomeConfig(self):

		cfg_def = {
			'handleuserhome' : False,
			'handleuserquota' : False,
			'sshhost' : None,
			'sshuser' : None,
			'sshpwd' : None,
			'sshkey' : None,
			'sshport' : None,
			'sshhkeysave' : False,
			'sshhkeypol' : 'reject',
			'skeleton' : '/etc/skel',
			'autohome' : True,
			'homebase' : '',
			'orphandir' : '',
			'orphandefault' : '_OrphanHomes',
			'orphanowner' : 'root',
			'orphangroup' : 'root',
			'orphanmaskd' : 0o0750,
			'orphanmaskf' : 0o0640,
			'archivedir' : '',
			'archivedefault' : '_ArchiveHomes',
			'archiveowner' : 'root',
			'archivegroup' : 'root',
			'archivemaskd' : 0o0750,
			'archivemaskf' : 0o0640,
			'hmaskd' : 0o0700,
			'hmaskf' : 0o0600,
			'softquota' : 20000000,
			'hardquota' : 24000000,
			'homedirs' : (),
			'archivehome' : False,
			'striphome' : False,
			'stripdirs' : (
				'Library',
				'profile.v6/AppData',
				'profile.v6/Graphisoft'
			),
			}
		cfg_ver = {
			'handleuserhome' : ('bool', ),
			'handleuserquota' : ('bool', ),
			'sshhost' : ('str', ),
			'sshuser' : ('str', ),
			'sshpwd' : ('str', ),
			'sshkey' : ('str', ),
			'sshport' : ('int', ),
			'sshhkeysave' : ('bool', ),
			'sshhkeypol' : ('str', ),
			'skeleton' : ('str', ),
			'autohome' : ('bool', ),
			'homebase' : ('str', ),
			'orphandir' : ('str', ),
			'orphandefault' : ('str', ),
			'orphanowner' : ('str', ),
			'orphangroup' : ('str', ),
			'orphanmaskd' : ('int', ),
			'orphanmaskf' : ('int', ),
			'archivedir' : ('str', ),
			'archivedefault' : ('str', ),
			'archiveowner' : ('str', ),
			'archivegroup' : ('str', ),
			'archivemaskd' : ('int', ),
			'archivemaskf' : ('int', ),
			'hmaskd' : ('int', ),
			'hmaskf' : ('int', ),
			'softquota' : ('int', ),
			'hardquota' : ('int', ),
			'homedirs' : ('list', 'str'),
			'archivehome' : ('bool', ),
			'striphome' : ('bool', ),
			'stripdirs' : ('list', 'str'),
			}

		# get kadmin connection configuration
		self.logger.debug(msg0020.format('UserHome'))
		self.config.updateDefaults('UserHome', cfg_def)
		self.config.updateVerify(cfg_ver)

		return self.config.getSection('UserHome')


	def __getUHClient(self):

		mod_name = self.class_name+'.__getUHClient'

		if self.userhome_config['sshhost']:

			# using ssh client
			cmd_client = SSHClient(self.logger)
			# connect to ssh host
			value = cmd_client.Connect(
					host = self.userhome_config['sshhost'],
					username = self.userhome_config['sshuser'],
					password = self.userhome_config['sshpwd'],
					keyfile = self.userhome_config['sshkey'],
					port = self.userhome_config['sshport'],
					hkeysave = self.userhome_config['sshhkeysave'],
					hkeypol = self.userhome_config['sshhkeypol'],
				)

			if value != 0:
				self.exit(1, msg0021.\
					format(self.userhome_config['sshhost']))

			return cmd_client
		else:
			# using local client
			return LocalClient(self.logger)


	def checkPermissions(self, path, uid, gid, dmode, fmode, recursive=True):

		fail = False
		path_info = self.uh_cmd.fileInfo(path)

		if isinstance(uid, int):
			uid_attr = 'uid'
		else:
			uid_attr = 'owner'

		if isinstance(gid, int):
			gid_attr = 'gid'
		else:
			gid_attr = 'group'

		if path_info[uid_attr] != uid or \
		   path_info[gid_attr] != gid:

			if self.uh_cmd.chown(path, uid, gid, recursive):
				self.logger.info(msg0003.format(path))
			else:
				self.logger.warn(msg0004.format(path))
				fail = True

		if path_info['mode'] != dmode:

			if self.uh_cmd.chmod(path, dmode, fmode, recursive):
				self.logger.info(msg0005.format(path))
			else:
				self.logger.warn(msg0006.format(path))
				fail = True

		if fail:
			self.exit(1, msg0010.format(path))


	def createFolder(self, path):

		# copy skeleton
		if self.uh_cmd.cp(self.skel, path):
			self.logger.info(msg0007.format(self.skel, path))
		else:
			self.exit(1, msg0008.format(self.skel, path))

		res = True

		# create certain folders
		dirs = ['', ]
		dirs.extend(self.userhome_config['homedirs'])

		for d in dirs:

			newdir = os.path.join(path, d)
			if self.uh_cmd.exists(newdir): continue

			if not self.uh_cmd.mkdir(newdir, self.hmaskd, self.uid, self.gid):

				self.logger.warn(msg0009.format(newdir))
				res = False

		return res


	def getQuota(self, username=None, mount=None):

		mod_name = self.class_name+'.getQuota'

		if username == None: username = self.cn
		if mount == None: mount = self.hbase

		quota_cmd = 'quotatool -u "{0}" -d "{1}"'.format(username, mount)

		quota_re_str = r'^\s*(?P<uid>\d+)\s+(?P<mount>[./\w-]+)\s+'
		quota_re_str += r'(?P<bused>\d+)\s+(?P<bsoft>\d+)\s+(?P<bhard>\d+)\s+(?P<bgrace>-?\d+)\s+'
		quota_re_str += r'(?P<iused>\d+)\s+(?P<isoft>\d+)\s+(?P<ihard>\d+)\s+(?P<igrace>-?\d+)\s*$'
		quota_re = re.compile(quota_re_str)

		value, std_out, std_err = self.uh_cmd.runCmd(quota_cmd)
		if value != 0:
			self.logger.warn(msg0012.format(std_err))
			return None

		for l in std_out:
			quota = quota_re.match(l).groupdict()
			if quota: break

		if not quota:
			self.logger.warn(msg0013.format(self.cn))
			return None

		return quota


	def setQuota(self, username=None, bsoft=None, bhard=None,
		mount=None, isoft=0, ihard=0):

		mod_name = self.class_name+'.setQuota'
		if not self.uq: return

		if username == None: username = self.cn
		if mount == None: mount = self.hbase
		if bsoft == None: bsoft = self.userhome_config['softquota']
		if bhard == None: bhard = self.userhome_config['hardquota']

		quota_cmd = 'quotatool -u "{0}" -b -q {1} -l {2} -i -q {3} -l {4} "{5}"'. \
			format(username, bsoft, bhard, isoft, ihard, mount)

		value, std_out, std_err = self.uh_cmd.runCmd(quota_cmd)
		if value != 0:
			self.logger.warn(msg0012.format(std_err))
			return False

		self.logger.info(msg0013.format(mount, username, bsoft, bhard, isoft, ihard))
		return True


	def expandQuota(self):

		mod_name = self.class_name+'.expandQuota'
		pass


	def shrinkQuota(self):

		mod_name = self.class_name+'.shrinkQuota'
		pass


	def createUserHome(self):

		mod_name = self.class_name+'.__addUser'
		if not self.uh: return

		# move existing folder out of the way
		if self.uh_cmd.exists(self.home):

			home_info = self.uh_cmd.fileInfo(self.home)

			if home_info['uid'] != self.uid or \
			   not self.uh_cmd.isdir(self.home):

				dst = os.path.join(self.orphandir, \
					self.cn + '_' + self.timestamp)

				if self.uh_cmd.mv(self.home, dst):
					self.logger.warn(msg0002.format(self.home, dst))
				else:
					self.exit(1, msg0011.format(self.home))
			
				# copy from skel and create mandatory subfolders
				if not self.createFolder(self.home):
					self.exit(1, msg0012.format(self.home))
		else:
			# copy from skel and create mandatory subfolders
			if not self.createFolder(self.home):
				self.exit(1, msg0012.format(self.home))

		# ensure home permissions
		self.checkPermissions(self.home, self.uid, \
			self.gid, self.hmaskd, self.hmaskf)

		# TODO: hide smb/afp files
		# TODO: do we still implement profile links?
		self.setQuota()


	def removeUserHome(self):

		mod_name = self.class_name+'.__delUser'
		if not self.uh: return

		# move existing folder out of the way
		if not self.uh_cmd.exists(self.home):
			self.exit(0, msg0015.format(self.home))

		if self.userhome_config['archivehome']:

			if self.userhome_config['striphome']:

				self.uh_cmd.noerr=True
				for d in self.userhome_config['stripdirs']:
					self.uh_cmd.rm(os.path.join(self.home, d))
				self.uh_cmd.noerr=False

			dst = os.path.join(self.archivedir, \
				self.cn + '_' + self.timestamp)

			if self.uh_cmd.mv(self.home, dst):
				self.logger.info(msg0016.format(self.cn))
			else:
				self.logger.warn(msg0017.format(self.cn))

			self.checkPermissions(dst, self.aowner, \
				self.agroup, self.amaskd, self.amaskf)
		else:
			if self.uh_cmd.rm(self.home, recursive=True, force=True):
				self.logger.info(msg0022.format(self.cn))
			else:
				self.logger.warn(msg0023.format(self.cn))


	def renameUserHome(self):

		mod_name = self.class_name+'.__renUser'
		if not self.uh: return

		pass


	def modifyUserHome(self):

		mod_name = self.class_name+'.__modUser'
		if not self.uh: return

		if self.uh_cmd.exists(self.home):

			self.checkPermissions(self.home, self.uid, \
				self.gid, self.hmaskd, self.hmaskf)

		elif self.userhome_config['autohome']:

			self.logger.info(msg0018.format(self.cn))
			self.createUserHome()

		else:
			self.logger.warn(msg0019.format(self.cn))

