# -*- coding: utf-8 -*-
#
# Univention SSL
"""Bareos Client Configuration Listener Module."""
#

__package__ = ''	# workaround for PEP 366
from listener import configRegistry, setuid, unsetuid
import grp
import os
import stat

import univention.debug as ud
import subprocess
import string
import random

name = 'bareos'
description = 'Generate Bareos Configuration for Clients'
filter = '(objectClass=bareosClientHost)'
attributes = []

PATH_PREFIX='/etc/bareos/autogenerated'
FD_CONFIG_PATH=PATH_PREFIX+'/fd-configs'
SECRETS_PATH=PATH_PREFIX+'/fd-secrets'
JOBS_PATH=PATH_PREFIX+'/clients'
INCLUDES_PATH=PATH_PREFIX+'/clients.include'

JOB_DISABLED='Not'

bareos_gid=grp.getgrnam('bareos').gr_gid

def getFqdn(entry):
        if not entry.has_key('cn'):
                return None

        name = entry['cn'][0]
        if entry.has_key('associatedDomain'):
                name = name + '.' + entry['associatedDomain'][0]

        return name

def initialize():
	"""Initialize the module once on first start or after clean."""
	ud.debug(ud.LISTENER, ud.INFO, 'BAREOS: Initialize')

def handler(dn, new, old):
	"""Handle changes to 'dn'."""
	setuid(0)
	try:
		# if configRegistry['server/role'] != 'domaincontroller_master':
		#	return

	        # ud.debug(ud.LISTENER, ud.INFO, 'BAREOS: handler '+dn+' '+str(bareos_gid))

		if new and not old:
			# changeType: add
		        name=getFqdn(new)
			processClient(name,new)

		elif old and not new:
			# changeType: delete
			try:
				name = getFqdn(old)
				processClient(name,old,delete=True)
			except:
				pass
		else:
			# changeType: modify
		        name=getFqdn(new)
			processClient(name,new)
	finally:
		unsetuid()

def clean():
	"""Handle request to clean-up the module."""
	return

def postrun():
	"""Transition from prepared-state to not-prepared."""
	return


def processClient(client_name,entry,delete=False):
	if client_name==None:
		return

	client_type='generic'
	if 'univentionWindows' in entry['objectClass']:
		client_type='windows'

	if delete==True:
		removeClient(client_name,client_type)
		return

	if entry.has_key('bareosEnableJob'):
		if entry['bareosEnableJob'][0]==JOB_DISABLED:
			removeClient(client_name,client_type)
			return

	addClient(client_name,client_type)

def addClient(client_name,client_type):
	createFDConfig(client_name,client_type)
	createClientJob(client_name,client_type)
	addClientInclude(client_name)

def removeClient(client_name,client_type):
	if client_name==None:
		return
	# removeClientInclude(client_name)
	# removeClientJob(client_name)
	disableClientJob(client_name,client_type)
	addClientInclude(client_name)

def getClientSecret(client_name):
	path=SECRETS_PATH+'/'+client_name
	password=None

	try:
		f=open(path,'r')
		password=f.read().strip()
	except:
		password=createClientSecret(client_name)

	return password

def getServerName():
	server_name=configRegistry['hostname']
	domain=configRegistry['domainname']
	if domain:
		server_name=server_name+'.'+configRegistry['domainname']
	return server_name

def createFDConfig(client_name,client_type):
	path=FD_CONFIG_PATH+'/'+client_name+'.conf'
	password=getClientSecret(client_name)
	server_name=getServerName()

	templatefile=FD_CONFIG_PATH+'/'+client_type+'.template'
	os.umask(077)
	with open(templatefile,'r') as f:
		content=f.read()

	t=string.Template(content)
	with open(path,"w") as f:
		f.write(t.substitute(password=password,client_name=client_name,server_name=server_name))
	os.chown(path,-1,0)

def createClientSecret(client_name):
	path=SECRETS_PATH+'/'+client_name

	char_set = string.ascii_uppercase + string.digits + string.ascii_lowercase
	password=''.join(random.sample(char_set*40,40))
	os.umask(077)
	with open(path,'w') as f:
		f.write(password)
	os.chown(path,-1,0)
	return password


def removeClientJob(client_name):
	path=JOBS_PATH+'/'+client_name+'.include'
	os.remove(path)

def createClientJob(client_name,client_type,enable='Yes'):
	password=getClientSecret(client_name)
	server_name=getServerName()

	path=JOBS_PATH+'/'+client_name+'.include'
	templatefile=JOBS_PATH+'/'+client_type+'.template'
	os.umask(077)
	with open(templatefile,'r') as f:
		content=f.read()

	t=string.Template(content)
	with open(path,"w") as f:
		f.write(t.substitute(enable=enable, \
			password=password,client_name=client_name,server_name=server_name))
	os.chown(path,-1,bareos_gid)
	os.chmod(path,stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)

def disableClientJob(client_name,client_type):
	createClientJob(client_name,client_type,'No')

def getClientIncludePath(client_name):
	return '@'+JOBS_PATH+'/'+client_name+'.include'

def addClientInclude(client_name):
	# is the client already in the include list?
	if isClientIncluded(client_name):
		# update the timestamp on the file
		# to let the cron script know the configuration
		# has changed
		os.utime(INCLUDES_PATH,None)
		return

	# if not, add it at the end of the file
	with  open(INCLUDES_PATH,'a') as f:
		f.write(getClientIncludePath(client_name))
		f.write('\n')

def isClientIncluded(client_name):
	want=getClientIncludePath(client_name)
	with  open(INCLUDES_PATH,'r') as f:
		for l in f.readlines():
			if want in l:
				return True
	return False

def removeClientInclude(client_name):
	if not isClientIncluded(client_name):
		return

	want=getClientIncludePath(client_name)
	lines=[]
	with open(INCLUDES_PATH,'r+') as f:
		lines=f.readlines()
		f.seek(0)
		f.truncate(0)
		for l in lines:
			if want in l:
				continue
			f.write(l)
