#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3


from winswitch.util.main_loop import with_loop, loop_exit
from winswitch.util.simple_logger import Logger
from winswitch.util.process_util import twisted_exec
from winswitch.util.common import no_newlines, is_valid_exe
from winswitch.util.commands_util import SELINUXENABLED_COMMAND

logger=Logger("selinux_util", log_colour=Logger.CYAN)


SELINUX_DISABLED="SELinux is not enabled"
SELINUX_PERMISSIVE="SELinux is in permissive mode, things should be OK"
SELINUX_ENFORCING="SELinux is enabled and in enforcing mode"
SELINUX_NX="if NX seamless mode does not work, you may need to update your policy"
SELINUX_SSHFORWARDING_UNKNOWN="failed to detect ssh forwarding permissions, assuming it is allowed!"

SELINUX_SSHFORWARDING_DISABLED_WARNINGS=["SELinux configuration does not allow SSH port forwarding!",
			"Sessions on this machine will not be accessible through SSH tunnels",
			"Use this command to enabled it: sudo setsebool -P sshd_forward_ports on"]
SELINUX_SSHFORWARDING_OK="Port forwarding is allowed, SSH tunnels should work as expected"


selinux_checking = False		#check in progress?
selinux_checked = None			#cache of check result
def is_selinux_checked():
	global selinux_checked, selinux_checking
	return selinux_checked is not None or selinux_checking


def check_selinux(result_callback):
	"""
		Will call result_callback(selinux_enabled, selinux_enforcing, warning_bool, info)
	"""
	global selinux_checked, selinux_checking
	if selinux_checked is not None:
		result_callback(*selinux_checked)
		return
	selinux_checking = True

	def cache_result(enabled, enforcing, warn, info):
		try:
			selinux_checking = False
			selinux_checked = (enabled, enforcing, warn, info)
			result_callback(*selinux_checked)
		except Exception, e:
			logger.serr(None, e, enabled, enforcing, warn, info)

	if not is_valid_exe(SELINUXENABLED_COMMAND):
		cache_result(False, False, False, SELINUX_DISABLED)
		return

	def selinux_warn(messages):
		logger.slog("******************************* WARNING! ********************************")
		logger.slog(SELINUX_ENFORCING)
		logger.slog(SELINUX_NX)
		for m in messages:
			logger.serror(m)

	def enforcing():
		""" SELinux is in enforcing mode, check ssh port forwarding """
		getsebool_cmd = ["getsebool", "sshd_forward_ports"]
		def getsebool_ssh_ok(message):
			if not no_newlines(message).endswith("on"):
				selinux_warn(SELINUX_SSHFORWARDING_DISABLED_WARNINGS)
				cache_result(True, True, True, "\n".join(SELINUX_SSHFORWARDING_DISABLED_WARNINGS))
			else:
				logger.slog(SELINUX_SSHFORWARDING_OK)
				cache_result(True, True, False, SELINUX_SSHFORWARDING_OK)
		def getsebool_ssh_err(message):
			logger.serror("failed to run '%s'" % getsebool_cmd)
			cache_result(True, True, True, SELINUX_SSHFORWARDING_UNKNOWN)
		twisted_exec(getsebool_cmd, getsebool_ssh_ok, getsebool_ssh_err, log_lifecycle=False)

	def selinuxenabled_ok(message):
		logger.sdebug(None, message)
		""" if SELinux is enabled, check if it is enforcing """
		def getenforce_ok(enforce_mode):
			logger.sdebug(None, enforce_mode)
			if no_newlines(enforce_mode)=="Permissive":
				cache_result(True, False, False, SELINUX_PERMISSIVE)
			else:
				enforcing()
		def getenforce_err(err):
			logger.serror("assuming SELinux is in enforcing mode..", err)
			enforcing()
		twisted_exec(["getenforce"], getenforce_ok, getenforce_err)
	def selinuxenabled_err(err):
		result_callback(False, False, False, "SELinux is not enabled")

	twisted_exec(["selinuxenabled"], selinuxenabled_ok, selinuxenabled_err)


def main():
	def result_callback(selinux_enabled, selinux_enforcing, warning_bool, info):
		logger.slog(None, selinux_enabled, selinux_enforcing, warning_bool, info)
		loop_exit()
	def check():
		check_selinux(result_callback)
	with_loop(check)

if __name__ == "__main__":
	main()
