#!/usr/bin/python
# coding=UTF-8

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

from xml.dom import minidom
import md5, subprocess, re, socket, os, sys, urllib2

# 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>"""

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

try:
	file=open("/etc/italc/keys/public/teacher/key","r")
	md5_1=md5.new(file.read()).hexdigest()
	file.close()

	file=open("/etc/italc/keys/public/admin/key","r")
	md5_2=md5.new(file.read()).hexdigest()
	file.close()

	file=open("/etc/italc/keys/public/supporter/key","r")
	md5_3=md5.new(file.read()).hexdigest()
	file.close()
except:
	sys.exit('iTalc keys not correctly installed')

def getLocalIPs():
	"Scan ifconfig output for local IPV4 addresses"
	os.environ["LANG"]="C" # Set environ LANG to C
	ip=[]
	output=subprocess.Popen("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])
	return ip

def getHostPort():
	isdhost="127.0.0.1"
	isdport="5800"
	if "LTSP_CLIENT" in os.environ:
		xprop=subprocess.Popen(["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 getClusterHosts():
	hosts=[]
	if not os.path.exists("/etc/ltsp/directory.conf") or not "LTSP_CLIENT" in os.environ:
		return hosts
	conf=open("/etc/ltsp/directory.conf","r")
	controlcenter=conf.read().strip()
	conf.close()
	page=urllib2.urlopen(controlcenter+"/TeachTools.php?list/ip="+os.environ["LTSP_CLIENT"])
	for line in page.readlines():
		line=line.strip()
		if line and not line.startswith("CLASSROOM"):
			hosts.append(line.split(",")[0]+":5900")
	return hosts

def getAvahiHosts():
	hosts=[]
	if not os.path.exists("/usr/bin/avahi-browse"):
		return hosts
	client_list=subprocess.Popen(["avahi-browse","-trp","_italc._tcp"],stdout=subprocess.PIPE)
	client_list.wait()
	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):
					hosts.append(param[7]+":"+param[8])
			except:
				print 'Ignoring a client, invalid data received'
	return hosts

if not os.access("/etc/italc/keys/private/teacher/key",os.R_OK):
	md5_1="0"
	if not os.access("/etc/italc/keys/private/admin/key",os.R_OK):
		md5_2="0"
		if not os.access("/etc/italc/keys/private/supporter/key",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(["mkdir","-p",confdir])
	mkdir.wait()
	try:
		config=open(confdir+"globalconfig.xml","w+")
		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
clients=getAvahiHosts()+getClusterHosts()
count=0

local_addr=getLocalIPs()
isdhost,isdport=getHostPort()

if isdhost not in local_addr:
	local_addr=[isdhost]

for line in clients:
	try:
		host,port=line.split(":")
		if (host in local_addr and str(int(isdport)+100) == port):
			continue

		# Make sure we have a running VNC server
		connection=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
		connection.connect((host,int(port)))
		connection.close()

		# Get MAC address
		mac=subprocess.Popen(("arp", host, "-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",host+":"+port)
		client.setAttribute("mac",mac)
		client.setAttribute("name","client "+str(count))
		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(["italc","-isdport",isdport,"-isdhost",isdhost,"-role",access])
