#!/usr/bin/python
# -*- coding: utf-8 -*-
# Project:     wifi-ltsp
# Module:     parsehostapd.py
# Purpose:     Parsea el fichero /etc/hostapd/hostapd.conf 
# Language:    Python 2.5
# Date:        26-Enero-2011.
# Ver:        07-Feb-2011.
# Author:    Francisco Mora Sánchez
# Copyright:   2011 - Francisco Mora Sánchez   <adminies.maestrojuancalero@edu.juntaextremadura.net>
#
# wifi-ltsp is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# Script2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with wifi-ltsp. If not, see <http://www.gnu.org/licenses/>.

""" Módulo que contiene la función selectbestchannel()
Función  que escanea el espectro de canales de redes
wifi y nos devuelve el canal más idóneo para ser utilizado en 
el lugar en que estamos.

La función admite dos parámetros

Utiliza la libreria pythonwifi y está basado en uno de los ejemplos
incluidos en la libreria
Debe ser ejecutado como root

El algoritmo utilizado se basa en sumar las redes que existen en cada
canal y a continuación calcular las sumas de los solapes de las redes
centradas en canales, patrón 1-6-11, escogeremos el canal cuyo valor
de las sumas sea menor.

Los canales que se solapan en el patrón 1-6-11 son los siguientes:

Canal		Canales que se solapan
1			1-5
2			1-6
3			1-7
4			1-8
5			1-9
6			2-10
7			3-11
8			4-12
9			5-13
10			6-13
11			7-13
12			8-13
13			9-13


Francisco Mora Sánchez
IES Maestro Juan Calero
adminies.maestrojuancalero@edu.juntaextremadura.net


"""

import sys
import os
import socket

#from pythonwifi.iwlibs import Wireless, getWNICnames

import escanea

def actualizadatosescaneo(datosescaneo, canal, bssid, essid, calidad, intensidad):
	""" Actualizamos diccionario con los datos recogidos del
		escaneado, comprobamos que no hemos insertado
		previamente la red, comprobando el bssid del AP
	"""
	if not datosescaneo.has_key(canal):
		#insertamos el canal inicializando los campos
		datosescaneo[canal] = [1, [[bssid,calidad,intensidad]], 0]
	else:
		#comprobemos que no existe ese bssid en el diccionario 
		#listabssid = datosescaneo[canal][1]
		listabssid = [bssids[0] for bssids in datosescaneo[canal][1]]
		if not bssid in listabssid:
			#no está en la lista, añadimos y aumentamos el contador de redes
			datosescaneo[canal][0] = datosescaneo[canal][0] + 1
			datosescaneo[canal][1].append([bssid,calidad,intensidad])

def procesadatosescaneo(datosescaneo):
	""" Sumamos las redes solapadas centradas en los canales, basándonos
		en el patrón 1-6-11
	"""
	adyacentes = {1:[1,2,3,4,5],2:[1,2,3,4,5,6],3:[1,2,3,4,5,6,7],4:[1,2,3,4,5,6,7,8],5:[1,2,3,4,5,6,7,8,9], \
		6:[2,3,4,5,6,7,8,9,10],7:[3,4,5,6,7,8,9,10,11],8:[4,5,6,7,8,9,10,11,12],9:[5,6,7,8,9,10,11,12,13], \
		10:[6,7,8,9,10,11,12,13],11:[7,8,9,10,11,12,13],12:[8,9,10,11,12,13],13:[9,10,11,12,13]}

	#Rellenamos canales faltantes
	for canal in range(1, 14):
		if not datosescaneo.has_key(canal):
			datosescaneo[canal] = [0, [], 0]

	#Sumemor las redes solapadas
	for canal in range(1, 14):
		for adyacente in adyacentes[canal]:
			datosescaneo[canal][2] = datosescaneo[canal][2] + datosescaneo[adyacente][0]
				
def seleccionacanaloptimo(datosescaneo):
	""" Procesamos diccionario y retornamos el canal óptimo,
		aquel cuyo valor de numero de redes totales solapadas
		centradas es el mínimo
    """
    #El mejor canal, es el menor valor de los solapes de las redes centradas en canales
	canaloptimo = 1
	valor = datosescaneo[1][2]
	for canal in range(1, 14):
		if datosescaneo[canal][2] < valor:
			canaloptimo = canal
			valor = datosescaneo[canal][2]
	return canaloptimo
	
def selectbestchannel(verbose = False):
	""" Creamos diccionario para almacenar datos, la key principal es 
		es el canal, y para cada canal hay una lista en
		la que se almacena el número de redes detectadas, sus BSSID junto
		a su potencia e intensidad, además se almacenará
		y el número de redes que se solapan con las redes centradas en canales
		según patrón 1-6-11.
		Controlaremos que no se dupliquen los canales cuando hay más de un interfaz wifi
		comparando los BSSID para este menester
	"""
	
	#Esta seria la estructura que obtendriamos de nuestro escaneado
	#{1:[3, [["MACAP1",70,-56], ["MACAP2",65,-61], ["MACAP3",60,-66]], 0],
	# 2:[2, [["MACAP4",75,-51], ["MACAP5",65,-61]], 0]
	# 3:[1, [["MACAP6",80,-45]], 0],
	# 4:[1, [["MACAP7",60,-66]], 0]}
	datosescaneo = {}
	if verbose:
		print "Escaneando redes WIFI..."
	index = 1
	for ifname in escanea.getWirelessInterfaces():
		if verbose:
			print "	Escaneado de la interfaz %-8.16s" % (ifname, )
		otros_datos_escaneo = escanea.ObtieneRedes(ifname)
		if len(otros_datos_escaneo.keys()) == 0:
			if verbose:
				print "%-8.16s  Escaneado sin resultados" % (ifname, )
		for bssid in otros_datos_escaneo.keys():
			if verbose:
				print "          Red: %02d - Canal: %s BSSID: %s ESSID: %s Quality: %s Signal Strength: %s" % \
			(index, otros_datos_escaneo[bssid]['canal'], bssid, otros_datos_escaneo[bssid]['essid'], otros_datos_escaneo[bssid]['calidad'], otros_datos_escaneo[bssid]['intensidad'])
			#vamos a actualizar nuestro diccionario
			actualizadatosescaneo(datosescaneo, int(otros_datos_escaneo[bssid]['canal']), bssid, otros_datos_escaneo[bssid]['essid'], otros_datos_escaneo[bssid]['calidad'], otros_datos_escaneo[bssid]['intensidad'])
			index = index +1
	#para pruebas
	#datosescaneo = {1:[2, [["01",99,-99],["02",98,-98]], 0], 6:[3, [["03",97,-97],["04",96,-96],["05",95,-95]], 0], 11:[1, ["06",94,-94], 0]}
	procesadatosescaneo(datosescaneo)
	canaloptimo = seleccionacanaloptimo(datosescaneo)
	if verbose:
		print "\nTabla de datos obtenida:"
		print datosescaneo
		print "\n"
		print "El canal óptimo es el: ", canaloptimo
	
	return canaloptimo

if __name__ == "__main__":
	""" Comprobaremos que se ha pasado la ruta al archivo hostapd.conf
		El archivo hostapd.conf debe contener la cadena 'canalseleccionado'
		que será reemplazada por la salida de la función selectchannelwifi().
		
		Además el SSID es el nombre de equipo menos la cadena '-pro', y
		la cadena a buscar en el archivo hostapd.conf es 'ssidseleccionado'.
	"""
	if len(sys.argv) == 1:
		print 'uso: ./parsehostapd.py /ruta/al/archivo/hostapd.conf'
		sys.exit(1)
	#Comprobemos la existencia del archivo
	hostapdconf = sys.argv[1]
	if not os.path.exists(hostapdconf):
		print "El archivo",hostapdconf,"no existe"
		exit(2)
	print "Procesando archivo",hostapdconf
	""" Aquí llamamos a la función  desarrollada en este módulo
		para asignar el canal de transmisión óptimo,
		si ponemos True en el parámetro podremos ver las redes 
		que se detectan...
	"""	
	canaloptimo = selectbestchannel(True)
	
	ssid = socket.gethostname()
	pos = ssid.find(".")
	if pos > -1:
		ssid = ssid[:pos]
	pos = ssid.find("-pro")
	if pos > -1:
		ssid = ssid[:pos]
	try:
		#Abre el archivo para lectura de datos
		archivoLectura = open(hostapdconf,"r")
		lineas = archivoLectura.readlines()
		archivoLectura.close()
	except (RuntimeError, TypeError, NameError, IOError):
		print "Error en la ubicacion o nombre del archivo"
		exit()
	#Modifica la cadena del channel y ssid
	encontradoChannel = False
	encontradoSsid = False
	for numlinea in range(0, len(lineas)):
		linea = lineas[numlinea]
		#comprobamos si estamos en channel o en ssid
		if linea[:7] == "channel":
			encontradoChannel = True
			lineas[numlinea] = "channel="+str(canaloptimo)+"\n"
		if linea[:4] == "ssid":
			encontradoSsid = True
			lineas[numlinea] = "ssid="+ssid+"\n"
	if not encontradoChannel:
		lineas.append("channel="+str(canaloptimo)+"\n")
	if not encontradoSsid:
		lineas.append("ssid="+ssid+"\n")
				
	#Abre el archivo para escritura de datos
	archivoEscritura = open(hostapdconf,"w")
	archivoEscritura.writelines(lineas)
	archivoEscritura.close()
	print "El canal establecido es",canaloptimo
	print "El SSID establecido es",ssid 
	print "Se ha procesado el archivo",hostapdconf 
