#!/usr/bin/python3

from jinja2 import Environment
from jinja2.loaders import FileSystemLoader
import configparser
import tarfile
import datetime
import os, sys, shutil
import os.path
import tempfile
import threading
import time
import n4d.server.core as n4dcore
import n4d.responses
from n4d.utils import get_backup_name


class PamnssPlugin:

	# Templates variables
	TEMPLATES_PATH="/usr/share/n4d/templates/pamnss/"
	LDAP_ENVIRONMENT_CLIENT_TEMPLATE="etc.ldap.ldap.conf"
	LDAP_TEMPLATE="etc.ldap.conf"
	NSSWITCH_TEMPLATE="etc.nsswitch.conf"
	NSLCD_TEMPLATE="nslcd.conf"
	
	# Destination variables
	LDAP_ENVIRONMENT_CLIENT_DESTINATION="/etc/ldap/ldap.conf"
	LDAP_DESTINATION="/etc/ldap.conf"
	NSSWITCH_DESTINATION="/etc/nsswitch.conf"
	NSLCD_DESTINATION="/etc/nslcd.conf"
	
	LIST_OF_FILES=["etc.ldap.conf","etc.ldap.ldap.conf","etc.nsswitch.conf"]
	
	# Backups
	BACKUP_DEFAULT_PATH="/backup/"
	BACKUP_DEFAULT_TAR=""
	
	def __init__(self):
		
		self.core=n4dcore.Core.get_core()
		self.failed={}
		self.failed[1]=False
		self.failed[2]=False
		self.failed[3]=False
		
	#def __init__
	
	def check_configured_status(self):
		
		for item in self.failed:
			if self.failed[item]:
				return False
				
		return True
	
	def uchmod(self,file,mode):
		
		prevmask = os.umask(0)
		os.chmod(file,mode)
		os.umask(prevmask)
		
	#def uchmod
		
	
	def mkdir(path):
		try:
			os.makedirs(path)
		except:
			pass
			
	#def mkdir(path):
	
	
	def is_client(self):
		
		#Old n4:if "REMOTE_VARIABLES_SERVER" in objects["VariablesManager"].variables:
		if self.core.variable_exists('REMOTE_VARIABLES_SERVER').get('return',None):
			return True
		else:
			return False
		
	#def is_client
	
	
	def startup(self,options):

		self.configure_ldap_environment_client()
		self.configure_ldap()
		self.configure_nsswitch()
		self.configure_nslcd()
			
		if self.check_configured_status():
			if os.path.exists("/usr/sbin/nscd") and options['boot']:
				os.system("nscd -i passwd")
				os.system("nscd -i group")
				os.system("nscd -i netgroup")
				os.system("nscd -i services")
				os.system("nscd -i hosts")
				os.system("service nscd restart")
		
		if "manually_launched" not in options:
			if not self.check_configured_status():
				self.retry_configuration(10)
		
					
		#Old n4d: eturn [True,True]
		return n4d.responses.build_successful_call_response()
	
	#def startup(self):

	
	def check_variables(self,dic):
		
		if len(dic)==0:
			return False
		
		for item in dic:
			if dic[item]==None:
				return False
				
		return True
		
	#check_variables
	
	def check_ldap_variables(self):
		
		'''
		if  objects.has_key("VariablesManager"):
			variables=objects["VariablesManager"].get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI"])
		'''
		variables=self.core.get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI"]).get('return',None)
		for item in variables:
			if variables[item]==False:
				return False
					
		return True
		
		#return False
		
	#def check_ldap_variables
	
	def get_local_users(self):
	
		f=open("/etc/passwd")
		lines=f.readlines()
		f.close()
		
		ret=""
		
		for line in lines:
			data=line.split(":")
			if int(data[2])<1000:
				ret+=data[0]+","
				
		ret=ret.rstrip(",")
		
		return ret
	
	#def get_local_users
	
	
	
	def retry_configuration(self,tries):
		
		t=threading.Thread(target=self._retry_thread,args=(tries,))
		t.daemon=True
		t.start()
		
	#def retry_configuration
	
	def _retry_thread(self,tries):
		
		for try_ in range(0,tries):
			if not self.check_configured_status():
				print("\t[STARTUP][PamnssPlugin] Retrying %s..."%try_)
				time.sleep(2)
				options={}
				options["controlled"]=True
				options["manually_launched"]=True
				options["boot"]=False
				self.startup(options)
			else:
				break

		
		if self.check_configured_status():
			if os.path.exists("/usr/sbin/nscd"):
				os.system("nscd -i passwd")
				os.system("nscd -i group")
				os.system("nscd -i netgroup")
				os.system("nscd -i services")
				os.system("nscd -i hosts")
				os.system("service nscd restart")	
			
		
	#def _retry_thread


	def configure_ldap_environment_client(self):

		# XMLRPC Debug
		# server=xmlrpclib.ServerProxy("https://localhost:9779")

		# XMLRPC Debug
		# if True:
		#Old n4dif  objects.has_key("VariablesManager"):
			#Old n4d ldap_environment_variables=objects["VariablesManager"].get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI"])
		ldap_environment_variables=self.core.get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI"]).get('return',None)
		if not self.check_variables(ldap_environment_variables):
			self.failed[1]=True
			#Old n4d: return [False,False]
			return n4d.responses.build_failed_call_response()
		else:
			self.failed[1]=False
							
		if os.path.exists("/var/lib/n4d/variables/LDAP_SID"):
			ldap_environment_variables["CLIENT_LDAP_URI"]="ldaps://localhost"
			
		# Temporal file creation
		path_to_work=tempfile.mkdtemp()
		filename=path_to_work+"ldap.conf"
		
		# Create temporal environment for jinja
		env = Environment(loader=FileSystemLoader(PamnssPlugin.TEMPLATES_PATH))
		tmpl = env.get_template(PamnssPlugin.LDAP_ENVIRONMENT_CLIENT_TEMPLATE)
		
		# Render the template with diferent values		
		textrendered=tmpl.render(ldap_environment_variables)
		
		# Create a temporal for nsswitch
		tmp,filename=tempfile.mkstemp()
		f = open(filename,'w')
		f.writelines(textrendered)
		f.close()
		
		# Using the ultimate chmod
		self.uchmod(filename,0o644)

		# Copy unitaria
		shutil.copy(filename,PamnssPlugin.LDAP_ENVIRONMENT_CLIENT_DESTINATION)
		os.remove(filename)
		
		#Old n4d: return [True,True]
		return n4d.responses.build_successful_call_response()
		
	# def  configure_ldap_environment_client(self):
	
	def configure_ldap(self):

		# Temporal file creation
		path_to_work=tempfile.mkdtemp()
		filename=path_to_work+"ldap.conf"
	
		# XMLRPC Debug
		# if True:
		'''
		if  objects.has_key("VariablesManager"):
			ldap_variables=objects["VariablesManager"].get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI_NOSSL"])
		'''
		ldap_variables=self.core.get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI_NOSSL"]).get('return',None)
		
		if not self.check_variables(ldap_variables):
			self.failed[2]=True
			#Old n4d: return [False,False]
			return n4d.responses.build_failed_call_response()
		else:
			self.failed[2]=False
			
				
		if os.path.exists("/var/lib/n4d/variables/LDAP_SID"):
			ldap_variables["CLIENT_LDAP_URI_NOSSL"]="ldap://localhost"
			
	
		# Create temporal environment for jinja
		env = Environment(loader=FileSystemLoader(PamnssPlugin.TEMPLATES_PATH))
		tmpl = env.get_template(PamnssPlugin.LDAP_TEMPLATE)
		
		# Render the template with diferent values
		textrendered = tmpl.render(ldap_variables)
		
		# Create a temporal for nsswitch
		tmp,filename=tempfile.mkstemp()
		f = open(filename,'w')
		f.writelines(textrendered)
		f.close()
		
		# Using the ultimate chmod
		self.uchmod(filename,0o644)
		
		# Move to the final destination
		shutil.copy(filename,PamnssPlugin.LDAP_DESTINATION)
		os.remove(filename)
		
		#Old n4d: return [True,True]
		return n4d.responses.build_successful_call_response()
		
	#def configure_ldap(self):
		
		
	def configure_nsswitch(self): 

		# XMLRPC Debug
		# server=xmlrpclib.ServerProxy("https://localhost:9779")
		
		# Get the template from templates library
		env = Environment(loader=FileSystemLoader(PamnssPlugin.TEMPLATES_PATH))
		tmpl = env.get_template('etc.nsswitch.conf')
		enable_nss_ldap={}
		if os.path.exists("/etc/sssd/sssd.conf"):
			enable_nss_ldap["ENABLE_CDC"] = "ENABLED"
		enable_nss_ldap["ENABLE_NSS_LDAP"]="ENABLED"
		# Render the template
		textrendered=tmpl.render(enable_nss_ldap)
		
		# Create a temporal for nsswitch
		tmp,filename=tempfile.mkstemp()
		f = open(filename,'w')
		f.writelines(textrendered)
		f.close()
		
		self.uchmod(filename,0o644)
		
		# Copy unitaria
		shutil.copy(filename,PamnssPlugin.NSSWITCH_DESTINATION)
		os.remove(filename)
		
		if os.path.exists("/usr/sbin/nscd"):
				os.system("nscd -i passwd")
				os.system("nscd -i group")
				os.system("nscd -i netgroup")
				os.system("nscd -i services")
				os.system("nscd -i hosts")
				os.system("service nscd restart")		
				
		
		
		#Old n4d: return [True,True]
		return n4d.responses.build_successful_call_response()
	# def configure_nsswitch
	
	def configure_nslcd(self):
		
		env = Environment(loader=FileSystemLoader(PamnssPlugin.TEMPLATES_PATH))
		tmpl = env.get_template('nslcd.conf')
		vars={}
		#Old n4d: vars=objects["VariablesManager"].get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI_NOSSL"])
		vars=self.core.get_variable_list(["LDAP_BASE_DN","CLIENT_LDAP_URI_NOSSL"]).get('return',None)
	
		if not self.check_variables(vars):
			self.failed[3]=True
			return False
		else:
			self.failed[3]=False

		
		if os.path.exists("/var/lib/n4d/variables/LDAP_SID"):
			vars["CLIENT_LDAP_URI_NOSSL"]="ldap://localhost"
				
				
		textrendered=tmpl.render(vars)
		tmp,filename=tempfile.mkstemp()
		f = open(filename,'w')
		f.writelines(textrendered)
		f.close()
		
		os.system("chmod 640 %s;chown root:nslcd %s"%(filename,filename))
		os.system("diff %s %s 1>/dev/null || { cp %s %s; systemctl restart nslcd; sleep 1; systemctl restart nslcd; } "%(filename,PamnssPlugin.NSLCD_DESTINATION,filename,PamnssPlugin.NSLCD_DESTINATION))
		os.remove(filename)
		
		return True
		
		
	#def configure_nslcd

	
	def backup(self,dir_=BACKUP_DEFAULT_PATH):
		
		file_path=dir_+"/"+get_backup_name("PamnssPlugin")
		self.backup_output=self.backup_configuration(file_path)
		return self.backup_output
		
	#def backup(self):
	
	def test(self):
		#Old n4d: return [True, True]
		return n4d.responses.build_successful_call_response()
	#def test(self):
	
	
	def restore(self,file_path=None):
		
		if file_path==None:
			for f in sorted(os.listdir("/backup"),reverse=True):
				if "PamnssPlugin" in f:
					file_path="/backup/"+f
					break			
		
		self.restore_output=self.restore_configuration(file_path)
		return self.restore_output
	#def restore(self):
	
	def restore_configuration(self, path):
		
		# Exists the file?
		if not os.path.exists(path):
			#Old n4d: return [False, "[N4D]  PamnssPlugin -Restore- Tar File is not present"]
			return n4d.responses.build_failed_call_response('','[N4D]  PamnssPlugin -Restore- Tar File is not present')
		# Extract to temporal directory
		path_to_work=tempfile.mkdtemp()
	
		tar=tarfile.open(path)
		tar.extractall(path_to_work)
		tar.close()
		
		path_to_work=path_to_work+"/"+self.__class__.__name__+"/"
		print(path_to_work)
		
		# First read the configuration from n4d-config-ini
		if not os.path.exists(path_to_work+"n4d-config.ini"):
			#Old n4d return [False, "[N4D] - PamnssPlugin -Restore- Configuration File is not present"]
			return n4d.responses.build_failed_call_response('',"[N4D] - PamnssPlugin -Restore- Configuration File is not present")

		Config = configparser.ConfigParser()
		Config.read(path_to_work+"n4d-config.ini")
		
		for section in Config.sections():
			
			# Read the config.ini
			back_file=Config.get(section,"file")
			dest_path= Config.get(section,"path")
			permissions=Config.get(section,"permissions")
			
			# Create path in destination if not exists
			if not os.path.isdir(os.path.dirname(dest_path)):
				os.makedirs(os.path.dirname(dest_path),0o755)

			# Copy the file to destination
			shutil.copy(path_to_work+back_file,dest_path)
			print (path_to_work+back_file,dest_path)

		#Old n4d. return [True, True]
		return n4d.responses.build_successful_call_response()
		
	#def restore_configuration(self, path=BACKUP_DEFAULT_PATH):
		
	def backup_configuration(self,file_path):
		
			
		# Temporal directory to work
		path_to_work=tempfile.mkdtemp()+"/"

		# Create config to backup the files
		Config = configparser.ConfigParser()
		backupCfg= open(path_to_work+"n4d-config.ini","w")

		# Copy the configuration files to the path
		try :
			
			# Copy LDAP files
			shutil.copy(PamnssPlugin.LDAP_DESTINATION,path_to_work+PamnssPlugin.LDAP_TEMPLATE)
			# Add to ini file backup info
			Config.add_section("LDAP")
			Config.set("LDAP", "FILE", PamnssPlugin.LDAP_TEMPLATE)
			Config.set("LDAP","PATH",PamnssPlugin.LDAP_DESTINATION)
			Config.set("LDAP","PERMISSIONS","0644")
			
			# Copy LDAP CLIENT
			shutil.copy(PamnssPlugin.LDAP_ENVIRONMENT_CLIENT_DESTINATION,path_to_work+PamnssPlugin.LDAP_ENVIRONMENT_CLIENT_TEMPLATE)
			# Add to ini file backup info
			Config.add_section("LDAPENVIRONMENT")
			Config.set("LDAPENVIRONMENT", "FILE", PamnssPlugin.LDAP_ENVIRONMENT_CLIENT_TEMPLATE)
			Config.set("LDAPENVIRONMENT","PATH",PamnssPlugin.LDAP_ENVIRONMENT_CLIENT_DESTINATION)
			Config.set("LDAPENVIRONMENT","PERMISSIONS","0644")
			
			# Copy nsswitch
			shutil.copy(PamnssPlugin.NSSWITCH_DESTINATION,path_to_work+PamnssPlugin.NSSWITCH_TEMPLATE)	
			# Addd to ini file backup Infoe
			Config.add_section("NSSWITCH")
			Config.set("NSSWITCH","FILE",PamnssPlugin.NSSWITCH_TEMPLATE)
			Config.set("NSSWITCH","PATH",PamnssPlugin.NSSWITCH_DESTINATION)
			Config.set("NSSWITCH","PERMISSIONS","0644")
			
			# Copy nslcd
			shutil.copy(PamnssPlugin.NSLCD_DESTINATION,path_to_work+PamnssPlugin.NSLCD_TEMPLATE)	
			# Addd to ini file backup Infoe
			Config.add_section("NSLCD")
			Config.set("NSLCD","FILE",PamnssPlugin.NSLCD_TEMPLATE)
			Config.set("NSLCD","PATH",PamnssPlugin.NSLCD_DESTINATION)
			Config.set("NSLCD","PERMISSIONS","0644")
			
			# Close the ini.file
			Config.write(backupCfg)
			backupCfg.close()

			# Create tar.gz
			date_tar_gz=datetime.date.today().__str__()
			path_to_backup=file_path
			
			# Resolve if the file is present
			if os.path.exists(path_to_backup):
				os.remove(path_to_backup)
			
			tar = tarfile.open(path_to_backup,"w:gz")
			tar.add(path_to_work,arcname=self.__class__.__name__)
			tar.close()

			#Old n4d: return [True, path_to_backup]
			return n4d.responses.build_successful_call_response(path_to_backup)
			
		except IOError as e:
			
			# something is going wrong
			#Old n4d: return [False, "[N4D] I/O error({0}): {1}".format(e.errno, e.strerror)]
			return n4d.responses.build_failed_call_response('','[N4D] I/O error({0}): {1}'.format(e.errno, e.strerror))
	#def backup_configuration(self,path=BACKUP_DEFAULT_PATH):
			

	def check_network_authentication(self):
		
		if  self.nsswitch_enable and self.ldap_enable : 
			#Old n4d: return [True,True]
			return n4d.responses.build_successful_call_response(True)
		else:
			#Old n4d: return [True,False]
			return n4d.responses.build_successful_call_response(False)
	#def check_network_authentication(self):

#class PamnssPlugin

if __name__=="__main__":
	print ("[N4D] Debug Pamnss")
	pp=PamnssPlugin()
	#print pp.backup()
	#print pp.configure_ldap()
	#print name_backup
	#print pp.restore_configuration("/backup/n4d/PamnssPlugin-2012-10-04.tar.gz")
	
	
	
