#!/usr/bin/python
# -*- coding: utf-8 -*-
# coding=UTF-8

# iTalc master launcher using avahi
# Written by Stéphane Graber <stgraber@ubuntu.com>

from xml.dom import minidom
import subprocess, re, socket, os, sys
# The md5 module is deprecated in Python 2.5
try:
    from hashlib import md5
except ImportError:
    from md5 import md5

def getValueFromConfigFile(filename,key,default):
	file=open(filename)
	for line in file:
		if line.startswith(key):
			try:
				param=line.split("=")[1]
				param=param.strip()
				if not param.isdigit():
					param=param.strip('"')
			except:
				param=default
	file.close()
	return param
					
def getLocalIPs():
	"Scan ifconfig output for local IPV4 addresses"
	# Saving current LANG
	SYSTEM_LANG=os.environ["LANG"]
	# Apparently ifconfig has localizations according to the following line,
	# but with LANG=es_ES.UTF-8 the output looks the same as LANG=C
	os.environ["LANG"]="C" # Set environ LANG to C
	ip=[]
	output=subprocess.Popen("/sbin/ifconfig",stdout=subprocess.PIPE)
	output.wait()
	for line in output.stdout.readlines():
		line=line.strip()
		if line.startswith("inet addr"):
			ip.append(line.split(" ")[1].split(":")[1])
	# Restoring LANG
	os.environ["LANG"]=SYSTEM_LANG
	return ip

def getHostPort():
	isdhost="127.0.0.1"
	isdport=getValueFromConfigFile(configfile,"ICA_ISDPORT","5800")
	if "LTSP_CLIENT" in os.environ:
		xprop=subprocess.Popen(["/usr/bin/xprop","-root","ica_ltsp"],stdout=subprocess.PIPE)
		xprop.wait()
		if xprop.stdout.read().split(" ")[2].strip() == "1":
			isdhost=os.environ["LTSP_CLIENT"]
		else:
			isdport=str(int(os.environ["LTSP_CLIENT"].split(".")[3])+11000)
	return [isdhost,isdport]

def getSettings():
	"Find settings file and read paths"
	global section_keypathsprivate
	global section_keypathspublic
	global section_paths
	global path_adminprivatekey
	global path_adminpublickey
	global path_globalconfig
	global path_supporterprivatekey
	global path_supporterpublickey
	global path_teacherprivatekey
	global path_teacherpublickey
	global settingsfile
	file=open(settingsfile)
	mode=""
	for line in file:
		line = line.strip()
		if line == section_keypathsprivate:
			mode=section_keypathsprivate
		elif line == section_keypathspublic:
			mode=section_keypathspublic
		elif line == section_paths:
			mode=section_paths
		else:
			if mode==section_keypathsprivate:
				if line.startswith("admin"):
					try:
						path_adminprivatekey=line.split("=")[1]
						path_adminprivatekey=path_adminprivatekey.strip()
					except:
						path_adminprivatekey="/etc/italc/keys/private/admin/key"
				elif line.startswith("teacher"):
					try:
						path_teacherprivatekey=line.split("=")[1]
						path_teacherprivatekey=path_teacherprivatekey.strip()
					except:
						path_teacherprivatekey="/etc/italc/keys/private/teacher/key"
				elif line.startswith("supporter"):
					try:
						path_supporterprivatekey=line.split("=")[1]
						path_supporterprivatekey=path_supporterprivatekey.strip()
					except:
						path_supporterprivatekey="/etc/italc/keys/private/supporter/key"
			elif mode==section_keypathspublic:
				if line.startswith("admin"):
					try:
						path_adminpublickey=line.split("=")[1]
						path_adminpublickey=path_adminpublickey.strip()
					except:
						path_adminpublickey="/etc/italc/keys/public/admin/key"
				elif line.startswith("teacher"):
					try:
						path_teacherpublickey=line.split("=")[1]
						path_teacherpublickey=path_teacherpublickey.strip()
					except:
						path_teacherpublickey="/etc/italc/keys/public/teacher/key"
				elif line.startswith("supporter"):
					try:
						path_supporterpublickey=line.split("=")[1]
						path_supporterpublickey=path_supporterpublickey.strip()
					except:
						path_supporterpublickey="/etc/italc/keys/public/supporter/key"
			elif mode==section_paths:
				if line.startswith("globalconfig"):
					try:
						path_globalconfig=line.split("=")[1]
						path_globalconfig=path_globalconfig.strip()
					except:
						path_globalconfig="/etc/italc/globalconfig.xml"
	file.close()
	return
	
# openSUSE uses /etc/sysconfig/ica to define the ports
configfile="/etc/sysconfig/ica"
# Empty config file to use if it doesn't already exist
skeleton="""<?xml version="1.0"?>
<!DOCTYPE italc-config-file>
<globalclientconfig version="1.0.7" >
  <body>
  </body>
</globalclientconfig>"""

settingsfile="/etc/settings/iTALC Solutions/iTALC.conf"
section_keypathsprivate="[keypathsprivate]"
section_keypathspublic="[keypathspublic]"
section_paths="[paths]"
path_teacherpublickey="/etc/italc/keys/public/teacher/key"
path_adminpublickey="/etc/italc/keys/public/admin/key"
path_supporterpublickey="/etc/italc/keys/public/supporter/key"
path_teacherprivatekey="/etc/italc/keys/private/teacher/key"
path_adminprivatekey="/etc/italc/keys/private/admin/key"
path_supporterprivatekey="/etc/italc/keys/private/supporter/key"
path_globalconfig="/etc/italc/globalconfig.xml"

try:
	getSettings()
except:
	print "Settings not found in "+settingsfile+", using defaults."

try:
	confdir=os.environ.get("HOME")+"/.italc/"
except:
	sys.exit('Invalid or undefined env variable \"HOME\"')

try:
	file=open(path_teacherpublickey,"r")
	md5_1=md5(file.read()).hexdigest()
	file.close()

	file=open(path_adminpublickey,"r")
	md5_2=md5(file.read()).hexdigest()
	file.close()

	file=open(path_supporterpublickey,"r")
	md5_3=md5(file.read()).hexdigest()
	file.close()
except:
	sys.exit('iTalc keys not correctly installed ('+path_teacherpublickey+')('+path_adminpublickey+')('+path_supporterpublickey)

if not os.access(path_teacherprivatekey,os.R_OK):
	md5_1="0"
	if not os.access(path_adminprivatekey,os.R_OK):
		md5_2="0"
		if not os.access(path_supporterprivatekey,os.R_OK):
			md5_3="0"
			access="none"
		else:
			access="supporter"
	else:
		access="admin"
else:
	access="teacher"

try:
	xmldoc=minidom.parse(confdir+"globalconfig.xml")
	body=xmldoc.getElementsByTagName("globalclientconfig")[0].getElementsByTagName("body")[0]
	classrooms=body.getElementsByTagName("classroom")
except:
	mkdir=subprocess.Popen(["/bin/mkdir","-p",confdir])
	mkdir.wait()
	try:
		config=open(confdir+"globalconfig.xml","w+")
		try:
			file=open(path_globalconfig)
			for line in file:
				config.write(line)
		except:
			print("Globalconfig in "+path_globalconfig+" not found, using skeleton.")
			config.write(skeleton)
		config.close()
	except:
		sys.exit('Unable to write to config file')
	xmldoc=minidom.parse(confdir+"globalconfig.xml")
	body=xmldoc.getElementsByTagName("globalclientconfig")[0].getElementsByTagName("body")[0]
	classrooms=body.getElementsByTagName("classroom")

# Scan for an existing classroom and delete it
for classroom in classrooms:
	if classroom.getAttribute("name") == "Auto-detected computers":
		body.removeChild(classroom)

# Create the Auto-detected computers classroom
avahi=xmldoc.createElement("classroom")
avahi.setAttribute("name","Auto-detected computers")
avahi.setAttribute("forcevisible","yes")
body.appendChild(avahi)

# Add computers to the classroom
client_list=subprocess.Popen(["/usr/bin/avahi-browse","-trp","_italc._tcp"],stdout=subprocess.PIPE)
client_list.wait()
count=0

local_addr=getLocalIPs()
isdhost,isdport=getHostPort()
if isdhost not in local_addr:
	local_addr=[isdhost]

for line in client_list.stdout.readlines():
	if line.startswith("="):
		try:
			param=line.split(";")
			comment=re.findall('"(.*)" "(.*)" "(.*)" "(.*)"\n',param[9])[0]
			if (comment[1] == md5_1 or comment[2] == md5_2 or comment[3] == md5_3) and (param[7] not in local_addr or str(int(isdport)+100) != param[8]):
				# Make sure we have a running VNC server
				connection=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
				connection.connect((param[7],int(param[8])))
				connection.close()

				# Get MAC address
				mac=subprocess.Popen(("/sbin/arp", param[7], "-n", "-a"),stdout=subprocess.PIPE)
				mac.wait()
				mac=mac.stdout.read().strip().split(" ")[3]
				if not re.match("^..:..:..:..:..:..$",mac):
					mac=""

				# Generate the new node
				client=xmldoc.createElement("client")
				client.setAttribute("id",str(count))
				client.setAttribute("localip",param[7]+":"+param[8])
				client.setAttribute("mac",mac)
				client.setAttribute("name",comment[0])
				client.setAttribute("type","0")
				avahi.appendChild(client)
				count+=1
		except:
			print 'Ignoring a client, invalid data received'

try:
	file=open(confdir+"globalconfig.xml","w")
	xmldoc.writexml(file)
	file.close()
except:
	exit('Failed to save updated config')
#print "Starting italc as "+access+" ("+isdhost+":"+isdport+")"
subprocess.Popen(["/usr/bin/italc","-isdport",isdport,"-isdhost",isdhost,"-role",access])
