<?php
/**
 * Clases de uso común por el resto de los scripts
 *
 * @author Jesús Algeciras <jes.algeciras@gmail.com>
 * @version 2010-10-24
 *
 * @author Alberto Martín <betolapalma@gmail.com>
 * @version 
 */

/**
 * Configuración
 */
require_once('configuracion.php');

/**
 * Decorador para la clase 'mysqli'
 *
 * Los métodos pertenecientes a 'mysqli' devuelven FALSE en caso
 * de que se produzca un error durante su ejecución. Esta clase
 * actua como decorador llamando a los métodos de la clase 'mysqli',
 * añadiendo la capacidad de lanzar excepciones del tipo DBException
 * en caso de error
 */
class Database {

	/**
	 * Objeto Database
	 * @var Database
	 */
	private static $objeto;

	/**
	 * Objeto mysqli
	 * @var mysqli
	 */
	private $db;

	/**
	 * Constructor. Obtiene la información necesaria del archivo de
	 * configuración e instancia un objeto de la clase mysqli. Es
	 * empleado el patron singleton para evitar crear una conexión
	 * distinta a la base de datos por cada objeto que quiera acceder
	 * a la misma
	 */
	private function __construct() {
		$this->db = @new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT);
		if (mysqli_connect_errno())
			throw new DBException(mysqli_connect_error(), mysqli_connect_errno($this->db));
	}

	/**
	 * Se emplea el destructor para cerrar automaticamente la conexión con
	 * la base de datos
	 */
	function __destruct() {
		@self::$objeto->close();
	}

	/**
	 * Método estático que me devuelve la instancia de la clase
	 * @return Database
	 */
	static function conectar() {
		if (!self::$objeto instanceof self)
			self::$objeto = new self;

		return self::$objeto;
	}

	/**
	 * Método que permite capturar la invocación de métodos no existentes.
	 * Este método nos permitirá llamar a los métodos de la clase mysqli
	 * como si pertenecieran a la clase Database
	 * @param $metodo nombre del método que se invoca
	 * @param $argumentos matriz que contiene los argumentos pasados
	 * @return mixed
	 */
	function __call($metodo, $argumentos) {
		// Si es un método válido de la clase 'mysqli' lo ejecuta
		if (method_exists($this->db, $metodo)) {
			$resultado = call_user_func_array(array($this->db, $metodo), $argumentos);
			if (@mysqli_errno($this->db)) {
				throw new DBException(mysqli_error($this->db), mysqli_errno($this->db));
			} else {
				return $resultado;
			}
		}
	}
}




/**
 * Validación de formularios
 *
 * Contiene una serie de métodos que se usarán para validar
 * si el contenido de los distintos campos de los formularios
 * es el esperado
 */
class Validacion {

	/**
	 * Contiene los errores en el formulario: $errores['campo'] = 'error'
	 * @var array
	 */
	private $errores;

	function __construct() {
		$this->errores = array();
	}

	function setErrores($input, $error) {
		$this->errores[$input] = $error;
	}

	function getErrores() {
		return $this->errores;
	}

	/**
	 * Comprueba si el campo esta vacio
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function noNulo ($input, $value) {
		if ($value == NULL) {
			$this->errores[$input] = 'El campo esta vacio';
			return FALSE;
		}
		return TRUE;
	}

	/**
	 * Comprueba si el valor del campo es 0 o 1
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esBinario($input, $value) {
		if ($this->noNulo($input, $value)) {
			if ($value == 0 || $value == 1) {
				return TRUE;
			} else {
				$this->errores[$input] = 'Formato erroneo';
				return FALSE;
			}
		}
	}

	/**
	 * Comprueba si el valor del campo es un entero
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esEntero($input, $value) {
		if ($this->noNulo($input, $value)) {
			if (preg_match('/^([0-9]+)$/', $value)) {
				return TRUE;
			} else {
				$this->errores[$input] = 'Formato erroneo';
				return FALSE;
			}
		}
	}

	/**
	 * Comprueba si el valor del campo es una hora válida
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esHora($input, $value) {
		if ($this->noNulo($input, $value)) {
			$aux = array();
			if (preg_match('/^([0-9]{1,2}):([0-9]{2})$/', $value, $aux) && $aux[1] < 24 && $aux[2] < 60) {
				return TRUE;
			} else {
				$this->errores[$input] = 'Formato erroneo';
				return FALSE;
			}
		}
	}

	/**
	 * Comprueba si el valor del campo es una fecha válida
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esFecha($input, $value) {
		if ($this->noNulo($input, $value)) {
			$aux = array();
			if (preg_match('/^([0-9]{4})\-([0-9]{1,2})\-([0-9]{1,2})$/', $value, $aux) && checkdate($aux[2],$aux[3],$aux[1])) {
				return TRUE;
			} else {
				$this->errores[$input] = 'Formato erroneo';
				return FALSE;
			}
		}
	}

	/**
	 * Comprueba si el valor del campo contiene caracteres no válidos
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esTexto($input, $value) {
		if ($this->noNulo($input, $value)) {
			if (preg_match('/^([a-zA-Z0-9áéíóúñ_]{4,})$/', $value)) {
				return TRUE;
			} else {
				$this->errores[$input] = 'Formato erroneo';
				return FALSE;
			}
		}
	}

	/**
	 * Comprueba si el valor del campo es una dirección de ip válida
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esIP($input, $value) {
		if ($this->noNulo($input, $value)) {
			if (preg_match('/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', $value)) {
				return TRUE;
			} else {
				$this->errores[$input] = 'Formato erroneo';
				return FALSE;
			}
		}
	}

	/**
	 * Comprueba si el valor del campo es una mascara de red válida
	 * @param $input campo del formulario
	 * @param $value valor del campo
	 * @return boolean
	 */
	function esMascara($input, $value) {
		// Mascara en formato x.x.x.x
		if ($this->esIP($input,$value) && preg_match('/^([1]{1,30})([0]{2,31})$/', sprintf("%032b",ip2long($value)))) {
			return TRUE;
			// Mascara en formato /xx
		} elseif (preg_match('/(30|2[0-9]|1?[0-9])/', $value)) {
			return TRUE;
		} else {
			$this->errores[$input] = 'Formato erroneo';
			return FALSE;
		}
	}

	/**
	 * Comprueba si la ip forma parte de la subred de administración
	 * @param $ip dirección IP
	 * @param $mascara mascara de red
	 * @return boolean
	 */
	function esAdministracion($ip, $mascara = NULL) {
		// Datos de la red de administración
		global $cnxn_admin_red;
		$fip = sprintf("%032b",ip2long($cnxn_admin_red['ip']));
		$fmascara = preg_match_all('/1/',sprintf("%032b",ip2long($cnxn_admin_red['mascara'])), $bits);

		$ip = sprintf("%032b",ip2long($ip));
		// Obtenemos la mascara menor
		if ($mascara) {
			// Si la mascara esta en formato x.x.x.x la pasamos a un entero
			if (preg_match('/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/i', $mascara))
				$mascara = preg_match_all('/1/',sprintf("%032b",ip2long($mascara)), $bits);
			$mascara = $mascara > $fmascara ? $fmascara : $mascara;
		} else {
			$mascara = $fmascara;
		}

		if ($mascara != 0 && substr($ip, 0, $mascara) === substr($fip, 0, $mascara)) {
			return TRUE;
		} else {
			return FALSE;
		}
	}

	/**
	 * Comprueba si es una configuración válida de PC
	 * @param $config datos de la configuración
	 * @return boolean
	 */
	function esPC($config) {
		$error = FALSE;
		$error = $error || !$this->esIP('ip', $config['ip']);
		if ($this->esIP('mascara', $config['mascara'])) {
			$error = $error || !$this->esMascara('mascara', $config['mascara']);
		} else {
			$error = TRUE;
		}
		$error = $error || !$this->esIP('puerta', $config['puerta']);
		$error = $error || !$this->esIP('dns1', $config['dns1']);
		$error = $error || !$this->esIP('dns2', $config['dns2']);
		// Se comprueba que la puerta de enlace pertenezca a la misma red que el PC
		if (!$error) {
			$mascara = preg_match_all('/1/',sprintf("%032b",ip2long($config['mascara'])), $bits);
			$ipb = sprintf("%032b",ip2long($config['ip']));
			$gatewayb = sprintf("%032b",ip2long($config['puerta']));
			if (!($mascara != 0 && substr($ipb, 0, $mascara) === substr($gatewayb, 0, $mascara))) {
				$this->errores['puerta'] = 'No en la misma red del PC';
				$error = TRUE;
			}
		}
		// Se comprueba que las IPs no pertenecen a la red de administración
		if (!$error) {
			if ($this->esAdministracion($config['ip'], $config['mascara'])) {
				$this->errores['ip'] = 'Valor no permitido';
				$error = TRUE;
			}
			if ($this->esAdministracion($config['ip'])) {
				$this->errores['puerta'] = 'Valor no permitido';
				$error = TRUE;
			}
			if ($this->esAdministracion($config['dns1'])) {
				$this->errores['dns1'] = 'Valor no permitido';
				$error = TRUE;
			}
			if ($this->esAdministracion($config['dns2'])) {
				$this->errores['dns2'] = 'Valor no permitido';
				$error = TRUE;
			}
			if ($error) {
				$log = LogFile::abrirLog();
                        	$log->log('Intento de uso de valores restringidos a la red de administración', LOG_WARNING);
			}
		}
		return !$error;
	}

	/**
	 * Comprueba si es una configuración válida de conmutador
	 * @param $input campo del formulario
	 * @param $idred identificador de la red
	 * @param $idconmutador identificador del conmutador
	 * @param $config datos de la configuración
	 * @return boolean
	 */
	function esConmutador($input, $idred, $idconmutador, $config) {
		// Expresiones regulares usadas posteriormente
		// -Listado de puertos o vlan ids
		$listreg = '(?:(?:all|(?:(?:[0-9]+\s*)(?:-\s*[0-9]+)?))(?:\s*,(?:all|(?:(?:[0-9]+\s*)(?:-\s*[0-9]+)?)))*)';
		// -Dirección IP
		$ipreg = '(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))';
		// -Mascara de red
		$maskreg = '(?:'.$ipreg.'|(?:\/(?:30|2[0-9]|1?[0-9])))';
			
		// Datos de la subred de practicas
		global $cnxn_admin_red;

		// Lista de puertos prohibidos
		$fports = $cnxn_admin_red['puertos_conm'];
		$fports[] = 'all';

		// VLAN ID prohibido
		$fvlan[] = $cnxn_admin_red['vlan'];

		// Lista de comandos prohibidos
		$fcoms = array('aaa', 'arp-protect', 'auto-tftp', 'authorized-managers', 'banner', 'cdp',
    				'clock', 'console', 'crypto', 'dhcp', 'dhcp-relay', 'dhcp-snooping', 'fastboot',
    				'fault-finder', 'front-panel-security', 'forward-protocol', 'helper-address',
    				'igmp', 'monitor', 'key-chain', 'link-keepalive', 'lldp', 'lockout-mac',
    				'logging', 'management-vlan', 'max-vlans', 'mirror-port', 'password',
    				'port-security', 'radius-server', 'snmp-server', 'snmpv3', 'sntp', 'ssh',
    				'stack', 'static-mac', 'tacacs-server', 'telnet-server', 'tftp', 'time',
    				'source-binding', 'timep', 'timesync', 'web-management');


		// Lista de comandos permitidos
		// 'access-group', 'access-list', 'arp', 'arp-age', 'gvrp', 'hostname', 'interface', 'ip',
		// 'mac-age-time', 'qos', 'qos-passthrough-mode', 'spanning-tree', 'unknown-vlans', 'vlan'


		// Lista de comandos a comprobar (Puertos y VLAN Ids)
		$ccoms = array('loop-protect' => $fports,
    				'interface' => $fports,
    				'spanning-tree(?:\s+instance\s+(?:1[0-6]|[1-9]|ist))?' => $fports,
    				'trunk' => $fports,
    				'auto' => $fports, //'vlan <VLAN-IDs> forbid'
                    'forbid' => $fports, //'vlan <VLAN-IDs> auto'
    				'tagged' => $fports, //'vlan <VLAN-IDs> tagged'
    				'untagged' => $fports, //'vlan <VLAN-IDs> untagged'
    				'primary-vlan' => $fvlan,
    				'static' => $fvlan,
    				'vlan' => $fvlan);

		// Lista de comandos a comprobar (Direcciones IP)
		$cipcoms = array(
				// 'qos device-priority <ip> [mask]'
    			'qos device-priority' => 'qos\s+device-priority\s+('.$ipreg.')\s*('.$maskreg.')?',
				// ip address <ip> <mask>
				'ip address' => 'ip\s+address\s+('.$ipreg.')\s*('.$maskreg.')',
				// ip default-gateway <ip>
				'ip default-gateway' => 'ip\s+default-gateway\s+('.$ipreg.')',
				// ip route <ip> <mask> <ip>
				'ip route' => 'ip\s+route\s+('.$ipreg.')\s*('.$maskreg.')\s+('.$ipreg.')');
		
		$valido = TRUE;
		$log = LogFile::abrirLog();
		
		if ($input ==  'archivo') {
			// Configuración completa en un archivo
			$texto = Archivo::leer($config);
		} else {
			// Configuración introducida linea a linea en el formulario
			$texto = $config;
		}
		
		// Se comprueba la aparición de los comandos no permitidos
		foreach ($fcoms as $fcom) {
			if (preg_match('/\s'.$fcom.'\s/', $texto)) {
				$this->errores[$input][] = 'Uso del comando no permitido "'.$fcom.'"';
				$valido = FALSE;
			}
		}
		
		// Para que no lea el "untagged" del "no untagged"
		$texto = preg_replace('no untagged','no-untagged',$texto);
		
		// Se comprueba que no se altere los puertos y VLANs no permitidas
		foreach ($ccoms as $ccom => $ids) {
			$aux = array();
			$matches = array();
			preg_match_all('/(?:\b('.$ccom.')\s+(?:(?:e|ethernet)\s+)?)('.$listreg.')\b/i', $texto, $matches);

			foreach ($matches[2] as $match) {
				preg_match_all('/all|(?:[0-9]+)(?:-[0-9]+)?\b/i', $match, $valores);
				foreach ($valores[0] as $valor) {
					if (preg_match('/([0-9]+)-([0-9]+)/', $valor, $rango)) {
						$aux = array_merge($aux,range($rango[1], $rango[2]));
					} else {
						$aux[] = $valor;
					}
				}
			}
			$erroneos = array_intersect($ids, array_unique($aux));
			if ($erroneos) {
				$ccom = preg_replace('/spanning-tree.*/', 'spanning-tree', $ccom);
				$this->errores[$input][] = 'Valores no permitidos para el comando "'.$ccom.'": '.implode(',', $erroneos);
				$valido = FALSE;
			}
		}


		// Se comprueba que no se empleen direcciones de la subred de administración
		foreach ($cipcoms as $name => $cipcom) {
			$aux = array();
			$matches = array();
			preg_match_all('/\b'.$cipcom.'\b/i', $texto, $matches, PREG_SET_ORDER);
	
			foreach ($matches as $match) {
				$erroneos = '';
				$ip = $match[1];
				$mask = $match[2] ? str_replace('/', '', $match[2]) : NULL;
				$gate = $match[3] ? $match[3] : NULL;
				if ($this->esAdministracion($ip, $mask))
					$erroneos = $match[1].' '.$match[2];
				if ($gate && $this->esAdministracion($gate))
					$erroneos = $erroneos ? $erroneos.' '.$match[3] : $match[3];
				if ($erroneos) {
					$this->errores[$input][] = 'Valores no permitidos para el comando "'.$name.'": '.$erroneos;
					$valido = FALSE;
				}
			}
		}

		if (!$valido) {
			$log = LogFile::abrirLog();
                        $log->log('Intento de uso de valores restringidos a la red de administración', LOG_WARNING);
			return FALSE;
		} else {
			return TRUE;
		}
	}
}




/**
 * Control de acceso de los usuarios
 *
 * Contiene métodos para controlar el acceso de los usuarios
 * a la página web, reserva de turnos y aplicación así como para
 * la obtención de información acerca de estos desde la base de datos
 */
class Usuario {

	/**
	 * Valida los campos username y password y los comprueba en la base de datos
	 * Si son validos se inicia la sessión y se almacenan los datos que usarán
	 * posteriormente a lo largo de esta.
	 * @param $username
	 * @param $password
	 * @return boolean
	 */
	static function login($usuario, $contrasena) {
		$validacion = new Validacion;
		$validacion->esTexto('usuario', $usuario);
		$validacion->esTexto('contrasena', $contrasena);
		if (count($validacion->getErrores())) {
			return FALSE;
		} else {
			$db = Database::conectar();
			$respuesta = $db->query('SELECT * FROM usuario NATURAL LEFT JOIN reserva 
										WHERE usuario.usuario="'.$usuario.'" AND contrasena="'.$contrasena.'" LIMIT 1');
			if ($respuesta->num_rows > 0) {
				$datos = $respuesta->fetch_assoc();
				$respuesta->free();

				// Se cambia el id de sesión por motivos de seguridad
				session_regenerate_id();

				$_SESSION['idusuario'] = $datos['idusuario'];
				$_SESSION['usuario'] = $datos['usuario'];
				$_SESSION['administrador'] = $datos['administrador'] ? TRUE : FALSE;

				// Si el usuario ya tiene un turno asignado se almecena la información sobre este
				if (isset($datos['idred']))
					$_SESSION['idred'] = $datos['idred'];

				if (isset($datos['idturno'])) {
					$respuesta = $db->query('SELECT * FROM turno WHERE idturno='.$datos['idturno']);
					$datos = $respuesta->fetch_assoc();
					$respuesta->free();
					$_SESSION['idturno'] = $datos['idturno'];
					$_SESSION['turno_dia'] = $datos['dia'];
					$_SESSION['turno_inicio'] = $datos['inicio'];
					$_SESSION['turno_fin'] = $datos['fin'];
					setcookie('turno_fin',$datos['fin'], time() + 3600*24, '/');
				}
				return TRUE;
			} else {
				$respuesta->free();
				return FALSE;
			}
		}
	}

	/**
	 * Finaliza la sesión del usuario y borra los datos de la misma
	 */
	static function logout() {
		$_SESSION = array();
		setcookie('turno_fin','', time() - 7200, '/');
		session_destroy();
	}

	/**
	 * Comprueba si el usuario ha iniciado la sesión
	 * @return boolean
	 */
	static function logged() {
		return isset($_SESSION['idusuario']);
	}

	/**
	 * Comprueba si el usuario tiene permisos de administración
	 * @return boolean
	 */
	static function esAdmin() {
		return (isset($_SESSION['administrador']) && $_SESSION['administrador']);
	}

	/**
	 * Comprueba que se esta dentro del periodo de reservas
	 * @return boolean
	 */
	static function accesoReserva() {
		$reservar = FALSE;
		$db = Database::conectar();
		$respuesta = $db->query('SELECT * FROM acceso WHERE idacceso="reservas"');
		if ($respuesta->num_rows > 0) {
			$datos = $respuesta->fetch_assoc();
			$respuesta->free();
			if(isset($_SESSION['idusuario']) && time() > strtotime($datos['inicio']) && time() < strtotime($datos['fin']))
				$reservar = TRUE;
		}
		return $reservar;
	}

	/**
	 * Realiza la reserva de un turno
	 * @param $idturno
	 * @param $idred
	 * @return boolean
	 */
	static function reservar($idturno, $idred) {
		$validacion = new Validacion;

		if (self::accesoReserva() && $validacion->esEntero('idturno', $idturno) && $validacion->esEntero('idred', $idred)) {
			$db = Database::conectar();
			// Se comprueba que el turno no este ya reservado
			$respuesta = $db->query('SELECT * FROM reserva WHERE idturno='.$idturno.' AND idred='.$idred);
			if ($respuesta->num_rows != 0) {
				$respuesta->free();
				return FALSE;
			} else {
				$respuesta->free();
				// Se realiza una nueva reserva o se actualiza la ya realizada
				$respuesta = $db->query('SELECT * FROM reserva WHERE idusuario='.$_SESSION['idusuario']);
				if ($respuesta->num_rows == 0) {
					$db->query('INSERT INTO reserva (idusuario,idred,idturno) VALUES ('.$_SESSION['idusuario'].','.$idred.','.$idturno.')');
				} else {
					$db->query('UPDATE reserva SET idturno='.$idturno.', idred='.$idred.' WHERE idusuario='.$_SESSION['idusuario']);
				}
				$respuesta->free();
				// Se actualizan los datos de la sesión
				$respuesta = $db->query('SELECT * FROM turno NATURAL LEFT JOIN reserva WHERE turno.idturno='.$idturno);
				$datos = $respuesta->fetch_assoc();
				$respuesta->free();
				$_SESSION['idturno'] = $idturno;
				$_SESSION['turno_dia'] = $datos['dia'];
				$_SESSION['turno_inicio'] = $datos['inicio'];
				$_SESSION['turno_fin'] = $datos['fin'];
				$_SESSION['idred'] = $datos['idred'];
				setcookie('turno_fin',$datos['fin'], time() + 3600*24, '/');
				return TRUE;
			}
		}
	}

	/**
	 * Cancela la reserva del turno
	 */
	static function cancelarReserva() {
		$db = Database::conectar();
		$db->query('DELETE FROM reserva WHERE idusuario='.$_SESSION['idusuario']);
		unset($_SESSION['idturno']);
		unset($_SESSION['turno_dia']);
		unset($_SESSION['turno_inicio']);
		unset($_SESSION['turno_fin']);
		unset($_SESSION['idred']);
		setcookie('turno_fin','', time() - 7200, '/');
	}

	/**
	 * Comprueba si el usuario puede iniciar la aplicación, esto es,
	 * si este se encuentra dentro del periodo de acceso, si es su
	 * turno y si la aplicación no se encuentra ya en uso
	 * @return boolean
	 */
	static function accesoAplicacion() {
		$acceso = FALSE;
			
		if ($acceso = isset($_SESSION['idturno'])) {
			$inicio = mktime(substr($_SESSION['turno_inicio'], 0, 2), substr($_SESSION['turno_inicio'], 3, 2), 0);
			$fin = mktime(substr($_SESSION['turno_fin'], 0, 2), substr($_SESSION['turno_fin'], 3, 2), 0);
		}
		// Se comprueba que el usuario halla iniciado la sesión
		$acceso = $acceso && self::logged();

		// Se comprueba que se esté dentro de las fechas establecidas para el acceso a la aplicación
		$db = Database::conectar();
		$respuesta = $db->query('SELECT * FROM acceso WHERE idacceso="aplicacion"');
		if ($respuesta->num_rows > 0) {
			$fecha = $respuesta->fetch_assoc();
			$respuesta->free();
			$acceso = $acceso && (time() > strtotime($fecha['inicio'])) && (time() < strtotime($fecha['fin']));
		}
		// Se comprueba que sea el turno del usuario
		$acceso = $acceso && ($_SESSION['turno_dia'] == date('w')) && (time() > $inicio) && ($fin > time());

		if ($acceso) {
			// Se comprueba que la aplicación no se encuentre actualmente ya en uso con ese id
			// de usuario desde otra maquina.
			$respuesta = $db->query('SELECT * FROM red WHERE idred='.$_SESSION['idred']);
			$datos = $respuesta->fetch_assoc();
			$respuesta->free();

			if ($datos['en_uso']) {
				// Si el usuario activo no es el actual es a causa de que el prinero no cerró la aplicación correctamente.
				if ($datos['ult_usuario'] != $_SESSION['idusuario']) {
					$db->query('UPDATE red SET en_uso=0 WHERE idred='.$_SESSION['idred']);
				} elseif($_SESSION['ult_acceso']) {
					// Si el usuario activo es el actual se comprueba la fecha de ultimo acceso a la red.
					// Si es distinta a la suya es porque otro miembro del grupo ha accedido.
					if (strtotime($datos['ult_acceso']) > strtotime($_SESSION['ult_acceso'])) {
						$acceso = FALSE;
						unset($_SESSION['red']);
						unset($_SESSION['ult_acceso']);
						foreach($_COOKIE as $name => $value)
							setcookie($name, $value, 1, '/aplicacion/');
					}
				} else {
					// Si un nuevo miembro del grupo accede se expulsa al anterior
					$db->query('UPDATE red SET en_uso=0 WHERE idred='.$_SESSION['idred']);
				}
			}
		}
		return $acceso;
	}

	/**
	 * Actualiza el estado del usuario a activo
	 */
	static function accedeRed() {
		// Si se le concede el acceso a la aplicación se actualiza su estado a activo
		$db = Database::conectar();
		$db->query('UPDATE red SET en_uso=1,ult_acceso="'.date("Y-m-d H:i:s").'",ult_usuario='.$_SESSION['idusuario'].' WHERE idred='.$_SESSION['idred']);
		$_SESSION['ult_acceso'] = date("Y-m-d H:i:s");
	}

	/**
	 * Actualiza el estado del usuario a no activo
	 */
	static function abandonaRed() {
		$db = Database::conectar();
		$db->query('UPDATE red SET en_uso=0 WHERE idred='.$_SESSION['idred']);
		unset($_SESSION['ult_acceso']);
	}
}




/**
 * Manejo de archivos
 *
 * Contiene métodos para el manejo de archivos con la capacidad
 * de generar excepciones del tipo FileException en caso de error
 */
class Archivo {

	/**
	 * Devuelve el contenido del archivo leido en una cadena
	 * @param $archivo
	 * @return string
	 */
	static function leer($archivo) {
		$texto = '';
		if (!($texto = @file_get_contents($archivo))) {
			throw new FileException('Error al intentar leer el archivo "'.$archivo.'"');
		} else {
			return $texto;
		}
	}
	
	/**
	 * Devuelve el contenido del archivo leido en un array
	 * @param $archivo
	 * @return array
	 */
	static function lineas($archivo) {
		$lineas = array();
		if (!($lineas = @file($archivo))) {
			throw new FileException('Error al intentar leer el archivo "'.$archivo.'"');
		} else {
			return $lineas;
		}
	}

	/**
	 * Añade el texto pasado a la función al final
	 * del archivo indicado.
	 * @param $archivo
	 * @param $texto
	 * @return boolean
	 */
	static function escribir($archivo, $texto) {
		
		if(!($file = @fopen($archivo, 'a'))) {
			throw new FileException('Error al intentar abrir el archivo "'.$archivo.'"');
		} else {
			if(!@fwrite($file, $texto))
				throw new FileException('Error al intentar modificar el archivo "'.$archivo.'"');
			@fclose($file);
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * Hace una copia de un archivo
	 * @param $origen
	 * @param $destino
	 * @return boolean
	 */
	static function copiar($origen, $destino) {
		if (!(@copy($origen, $destino))) {
			throw new FileException('Error al intentar copiar el archivo "'.$origen.'" a "'.$destino.'"');
		} else {
			return TRUE;
		}
	}

	/**
	 * Crea un archivo. Si este ya existe lo vacia.
	 * @param $archivo
	 * @return boolean
	 */
	static function crea($archivo) {
		if (!($file = @fopen($archivo, 'w+'))) {
			throw new FileException('Error al intentar crear el archivo "'.$archivo.'"');
		} else {
			@fclose($file);
		}
		return TRUE;
	}

	/**
	 * Borra una archivo
	 * @param $archivo
	 * @return boolean
	 */
	static function borrar($archivo) {
		if (file_exists($archivo)) {
			if (!(@unlink($archivo)))
				throw new FileException('Error al intentar borrar el archivo "'.$archivo.'"');
		}
		return TRUE;
	}

	/**
	 * Copia un archivo subido a un directorio permanente
	 * @param $archivo
	 * @param $destino
	 * @return boolean
	 */
	static function almacenar($archivo, $destino) {
		if (!(@move_uploaded_file($archivo, $destino))) {
			throw new FileException('Error al intentar almacenar el archivo "'.$archivo.'" a la carpeta "'.$destino.'"');
		} else {
			return TRUE;
		}
	}
}


/**
 * Log de la aplicación
 *
 * Clase que se emplea para el registro de eventos en el
 * archivo de log
 */
class LogFile {

	/**
	 * Objeto LogFile
	 * @var LogFile
	 */
	private static $objeto;

	/**
	 * El archivo abierto
	 * @var mixed
	 */
	protected $archivo;

	/**
	 * Estado del archivo
	 * @var boolean
	 */
	protected $abierto = FALSE;
	
	/**
	 * Nivel de verbose
	 * @var int
	 */
	protected $verbose;

	/**
	 * Constructor. Obtiene el nombre del archivo de log
	 * del archivo de configuración
	 */
	private function __construct($verbose) {
		if (!file_exists(FILES_LOG)) {
			if (!@touch(FILES_LOG))
				throw new Exception('No ha sido posible la creación del archivo de log: "'.FILES_LOG.'"');
			if (!@chmod(FILES_LOG, 0664))
				throw new Exception('Error al intentar cambiar los permisos del archivo de log: "'.FILES_LOG.'"');
		}

		if (!$this->abierto) {
			if (!($this->archivo = @fopen(FILES_LOG, 'a')))
				throw new Exception('Error al intentar abrir el archivo "'.FILES_LOG.'"');
			$this->abierto = TRUE;
		}
		
		$this->verbose = $verbose;
	}

	function __destruct(){
		// Cierra el archivo si aun permanece abierto
		if ($this->abierto)
			$this->cerrarLog();
	}

	/**
	 * Método estático que me devuelve la instancia de la clase
	 * en caso de poder abrir correctamente el archivo de log
	 * @return LogFile
	 */
	static function abrirLog($verbose = LOG_INFO) {
		if (!self::$objeto instanceof self)
			self::$objeto = new self($verbose);

		return self::$objeto;
	}

	/**
	 * Cierra el archivo apuntado por 'archivo'
	 * @return boolean
	 */
	function cerrarLog() {
		if ($this->abierto)
			@fclose($this->archivo);
		$this->abierto = FALSE;
		return ($this->abierto == FALSE);
	}

	/**
	 * Crea una entrada en el archivo de log
	 * @param string $mensaje
	 * @param string $prioridad Valores validos: LOG_EMERG, LOG_ALERT,
	 * 							LOG_CRIT, LOG_ERR, LOG_WARNING,
	 * 							LOG_NOTICE, LOG_INFO, LOG_DEBUG
	 * @return boolean
	 */
	function log($mensaje, $prioridad = LOG_INFO) {
		if ($prioridad <= $this->verbose) {
			$tiempo = strftime('%Y-%m-%d %H:%M:%S');
			$usuario = $_SESSION['usuario'].' (ID: '.$_SESSION['idusuario'].')';
			$info = array(
				0 => 'emergency',
				1 => 'alert',
				2 => 'critical',
				3 => 'error',
				4 => 'warning',
				5 => 'notice',
				6 => 'info',
				7 => 'debug'
			);
			$linea = '['.$tiempo.'] ['.$usuario.'] ['.$info[$prioridad].'] '.$mensaje."\n";
	
			flock($this->archivo, LOCK_EX);
			if (!(@fwrite($this->archivo, $linea))) {
				flock($this->archivo, LOCK_UN);
				throw new Exception('Error al intentar modificar el archivo "'.$this->path.'"');
			}
			flock($this->archivo, LOCK_UN);
		}
		return TRUE;
	}
}

/**
 * Excepción que quedará resgistrada en el log
 *
 * Las excepciones que hereden de este clase serán
 * almecenadas en un archivo de log
 */
class LoggedException extends Exception {

	function __construct($message = null, $code = 0) {
		try {
			$log = LogFile::abrirLog();
			$log->log(get_class($this).': '.$message, LOG_ERR);
		}
		catch (Exception $excepcion) {
			$message = $message."(No ha podido registrarse en el log)";
		}
		parent::__construct($message, $code);
	}

	/**
	 * Reperesentación de la excepción como cadena de texto
	 */
	public function __toString() {
		return get_class($this).': '.$this->message;
	}
}


/**
 * Excepción realacionada con la base de datos
 *
 * Excepción lanzada cuando se ha producido un error en la conexión
 * o comunicación con la base de datos
 */
class DBException extends LoggedException {}


/**
 * Excepción realacionada con el manejo de archivos
 *
 * Excepción lanzada cuando se ha producido un error en el acceso,
 * copia o modificación de un archivo
 */
class FileException extends LoggedException {}


/**
 * Excepción ralacionada con la conexión con nodos de la red
 *
 * Excepción lanzada cuando se ha producido un error al intentar
 * cambiar la configuración de algun nodo de la red
 */
class NetworkException extends LoggedException {}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es">
	<head>
            <title>e-REdING. Biblioteca de la Escuela Superior de Ingenieros de Sevilla.</title>

                <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="description" content="Buscador de proyectos ETSI" />
<meta name="author" content="Daniel Callejo"/>
<meta name="author" content="Federico L&oacute;pez"/>
<meta name="author" content="Juli&aacute;n P&eacute;rez"/>
<meta name="author" content="Jos&eacute; Mar&iacute;a Vidal Vidal"/>
<base href="/bibing/proyectos/" /><link rel="shortcut icon" href="./favicon.ico" />
<link rel="stylesheet" href="./publico/css/estructura.css" type="text/css" />
<link rel="stylesheet" href="./publico/css/bootstrap.css" type="text/css" media="screen"/>
<link rel="stylesheet" href="./publico/css/enlaces-capas.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/pestanas.css" type="text/css"  /> 
<link rel="stylesheet" href="./publico/css/formularios.css" type="text/css" />
<link rel="stylesheet" href="./publico/css/lightbox.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/autocompleter.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/resultados.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/milista.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/explorador.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/ultimos.css" type="text/css"  />
<link rel="stylesheet" href="./publico/css/contactar.css" type="text/css"  />
<!-- <link rel="stylesheet" href="./css/print.css" type="text/css" media="print" /> -->
<script src="./publico/javascript/jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="./publico/javascript/bootstrap.js" type="text/javascript"></script>

<script src="./publico/javascript/mootools-1.2.4-core.js" type="text/javascript"></script>
<script src="./publico/javascript/mootools-1-1.2.4.2-more.js" type="text/javascript"></script>
<script src="./publico/javascript/targetbl.js" type="text/javascript"></script>
<script src="./publico/javascript/mensajes.js" type="text/javascript"></script>
<script src="./publico/javascript/form.js" type="text/javascript"></script>
<script src="./publico/javascript/autocompleter/Observer.js" type="text/javascript"></script>
<script src="./publico/javascript/autocompleter/Autocompleter.js" type="text/javascript"></script>
<script src="./publico/javascript/autocompleter/Autocompleter.Request.js" type="text/javascript"></script>
<script src="./publico/javascript/domready.js" type="text/javascript"></script>

<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-8T1TSNC1QY"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-8T1TSNC1QY');
</script>



<script type="text/javascript">
function seleccionar_todo(){
   for (i=0;i<document.formulario_marcados.elements.length;i++)
      if(document.formulario_marcados.elements[i].type == "checkbox")
         document.formulario_marcados.elements[i].checked=1
}
function deseleccionar_todo(){
   for (i=0;i<document.formulario_marcados.elements.length;i++)
      if(document.formulario_marcados.elements[i].type == "checkbox")
         document.formulario_marcados.elements[i].checked=0
} 
</script>
	</head>
    <body style="min-width: 960px; background-color: #e2e2e2; height: 100%">	
		<a name="arriba"></a>
<div>
<div id="header">
    <table id="Tabla_01" height="105" border="0" cellpadding="0" cellspacing="0">
    <tr>
        <td>
            <img src="./publico/imagenes/cabecera/banner_new2_01.jpg" width="3" height="105" alt="">
        </td>
        <td>
            <img src="./publico/imagenes/cabecera/banner_new2_02.jpg" width="100" height="105" alt="">
        </td>
        <td width="100%" align="right"  style="min-width: 613px; background-repeat: no-repeat; background-position: top right; font-size: medium; font-weight: bold; color: white;">
            <table width="100%" height="105" border="0">
                <tr>
                    <td style=" vertical-align: top; width: 547px">
                        <!-- <a style="text-decoration: none" href="./"><img src="./publico/imagenes/cabecera/banner_new2_03.jpg"alt=""></a> -->
                        <a style="text-decoration: none" href="/bibing/proyectos/"><img src="./publico/imagenes/cabecera/banner_new2_03.jpg"alt=""></a>
                     </td>
                     <td  background="./publico/imagenes/cabecera/banner_new2_04.jpg" style="background-repeat: no-repeat; background-color: #c6d4e8;"><br><br><br>
                         &nbsp;
                        </td>
                    </tr>
             </table>
        </td>
    </tr>
    <tr style="border-top: 1px solid #ffffff;">
        <td colspan="3" id="menu_sup" style="font-size: small; text-align: right; background-color: #9c1f2f; vertical-align: bottom">
             
					<a href="/bibing/proyectos/" class="enlace " style=" color: #ffffff">Inicio&nbsp;&nbsp;|</a>
					<a href="/bibing/proyectos/acerca/" style=" color: #ffffff" class="enlace ">&nbsp;&nbsp;Acerca de&nbsp;&nbsp;|</a>
					
					<a href="/bibing/proyectos/contactar/" style=" color: #ffffff" class="enlace ">&nbsp;&nbsp;Contactar&nbsp;&nbsp;</a>
			 <!--       <a href="/bibing/proyectos/ayuda/" style=" color: #ffffff" class="enlace  ">&nbsp;&nbsp;Ayuda&nbsp;&nbsp;|</a> -->
					<!-- <form id="idioma" class=""  method="post" action="">
					<div class="inline" style="color: white">
					<input type="submit" class="tipo_letra " style=" color: #ffffff"name="idioma" value="Castellanossss" />&nbsp;&nbsp;
					</div>	
					</form> -->
			        </td>

    </tr>
    </table>
</div>
<br><br>
<div class="colmask rightmenu">
<div class="panel panel-default" style="margin:20px">
            <div class="panel-heading"><h4>DESARROLLO DE UN ENTORNO DE PRÁCTICAS PARA REDES MULTISERVICIO</h4><h5 style="color:grey"><span class="glyphicon glyphicon-user""></span> : <b>MARTÍN REDONDO, ALBERTO JESÚS</b><br><span class="glyphicon glyphicon-book""></span>  :<b> Ingenier&iacute;a Telecomunicación</b></h5></div><div class="panel-body"><div class="contenido_proyecto"><div class="titulo2_explorador">Contenido del proyecto:</div><div class="ruta"><a href="use/abreproy/12106">Directorio ra&iacute;z</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href="use/abreproy/12106/direccion/ARCHIVOS%252F">ARCHIVOS</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href="use/abreproy/12106/direccion/ARCHIVOS%252Fredes%252F">redes</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href="use/abreproy/12106/direccion/ARCHIVOS%252Fredes%252Fadmin%252F">admin</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href="use/abreproy/12106/direccion/ARCHIVOS%252Fredes%252Fadmin%252Ffunc_comunes.php%252F">func_comunes.php</a></div><div class="listing"></div></div></div></div>
</div>
</div>

        
	</body>
 
 
<!-- Inicio Google Analytics -->
<!--
	<script type="text/javascript">
		var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
		document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
	</script>
	
	<script type="text/javascript">
		try {
		var pageTracker = _gat._getTracker("UA-7789620-1");
		pageTracker._trackPageview();
	} 
		catch(err) {}
	</script>
-->
<!-- Fin Google Analytics -->

</html>

