<%#
Opennet Firmware

Copyright 2010 Rene Ejury <opennet@absorb.it>
Copyright 2014 Lars Kruse <devel@sumpfralle.de>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

-%>

<%
	require("luci.model.opennet.funcs")
	require("luci.model.opennet.urls")

	local sorting = on_function("get_service_sorting")

	local parallel_processes = 3
	-- Geraete mit weniger als 32 MB zeigen sonst gelegentlich ein out-of-memory-Verhalten
	if tonumber(on_function("get_memory_size")) <= 32 then
		parallel_processes = 1
	end
%>

<%+header%>

<!-- erforderlich fuer "String.format" -->
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[

	// **************** asynchrone Abfrage der Gateway-Details *********************

	// wir moechten einen Hinweis ausgeben, falls ein Sprung vom aktuellen Gateway auf einen besseren Gateway bevorsteht
	var best_gateway;
	var current_gateway;
	var service_dict = {};

	// loese die Anfrage nach allen Dienst-IDs aus - diese werden anschliessend einzeln nacheinander abgefragt
	XHR.get('<%= on_url("mig_openvpn", "status", "vpn_gateway_list") %>', null, build_table);

	function uci_to_yn(token) {
		if (!token) {
			return "";
		}
		var token = token.toLowerCase();
		if ((token == "1") || (token == "yes") || (token == "y") || (token == "on") || (token == "true")) {
			return "y";
		} else {
			return "n";
		}
	}

	function build_table(dummy, services) {
		// splitte die Liste der Dienste in mehrere parallele Ablaeufe
		// ein Wert groesser als drei scheint keine Beschleunigung zu bringen (mit einem TL-WDR4300)
		var RUN_IN_PARALLEL = <%=parallel_processes%>;
		var result = "";
		var target_table = document.getElementById('on-gateway-list');
		var in_progress_spinner = document.getElementById('gateway-list-spinner');
		if (!target_table) return;
		// Tabellenzeilen erzeugen
		for (var index = 0; index < services.length; index++) {
			result += String.format('<div id="%s" class="tr cbi-rowstyle-1 cbi-opennet-gateways" disabled="n">', services[index]);
			result += String.format('<div class="td col-1 left"><div id="cbi-opennet-gateways-count">%d</div></div>', index + 1);
			result += '</div>';
			service_dict[services[index]] = index;
		}
		target_table.innerHTML += result;
		// Aktivitaetsindikator verstecken
		if (in_progress_spinner) in_progress_spinner.style.display = 'none';
		// die Anfragen in parallelen Wellen ausloesen
		var parallels = [];
		// Listen vorbereiten
		while (parallels.length < RUN_IN_PARALLEL) parallels.push([]);
		parallels = services.reduce(function(list, current, index) {
				list[index % RUN_IN_PARALLEL].push(current);
				return list;
			}, parallels);
		for (var index = 0; index < parallels.length; index++) {
			schedule_next_service(parallels[index]);
		};
	}

	function schedule_next_service(remaining_services) {
		if (remaining_services && (remaining_services.length > 0)) {
			XHR.get('<%= on_url("mig_openvpn", "status", "vpn_gateway_info") %>/' + remaining_services[0], null,
				function(x, info) { if (info) { insert_gateway(info, remaining_services.slice(1)); }});
		}
	}

	// Uebertragen eines Gateway-Objekts in eine Tabellenzeile fuer einen Service
	// Der Inhalt des Gateway-Objekts wird in der Funktion "gateway_info" festgelegt.
	function insert_gateway(gateway, remaining_services) {
		// Fuege eine neue Tabellenzeile ein
		var target_row = document.getElementById(gateway.id);
		if (!target_row) return;
		var remote;
		var distance;
		var speed;
		var speed_abbr;
		var unreachable;
		var sorting = "<%= sorting %>";
		var result = "";
		// "remote" menschenfreundlich setzen
		remote = gateway.host;
		if (gateway.port != <%=on_function("get_variable", {"DEFAULT_MIG_PORT"})%>) {
			remote += ":" + (gateway.port || "?");
		}
		if (gateway.distance || (gateway.distance==0)) {
		       unreachable = false;
		} else {
		       unreachable = true;
		}
		// Zeile visuall als unbenutzbar markieren
		if (gateway.disabled || unreachable) {
			target_row.setAttribute("disabled", "y");
		}
		// "active" als laeuft/kaputt/inaktive setzen
		if (gateway.connection_state && (gateway.connection_state != "")) {
			current_gateway = gateway.id;
			update_gateway_change_indicator();
			if (gateway.connection_state == "active") {
				active = "y";
			} else {
				// Verbindung ist im Aufbau - bisher wird dies als "laeuft noch nicht" visualisiert
				active = "n";
			}
		} else {
			active = null;
		}
		// best_gateway setzen, falls noch ungesetzt oder weiter oben, als der bisher beste
		if ((uci_to_yn(gateway.status) == "y") && !gateway.disabled && !gateway.unreachable
				&& (!best_gateway || (service_dict[best_gateway] > service_dict[gateway.id]))) {
			best_gateway = gateway.id;
			update_gateway_change_indicator();
		}
		result += String.format('<div class="col-1 left td"><div id="cbi-opennet-gateways-active" active="%s">&nbsp;</div></div>', active);
		result += String.format('<div class="col-1 left td"><strong>%s</strong></div>', remote);
		if (gateway.public_ugw_server) {
			result += String.format('<div class="col-1 left td">%s</div>', gateway.public_ugw_server.split(".")[0]);
		} else {
			result += '<div class="col-1 left td"></div>';
		}
		if (unreachable) {
			// unerreichbare Knoten werden als solche markiert
			result += '<div class="col-1 left td"><%:unreachable%></div>';
		} else if (sorting == "manual") {
			// im manuellen Modus ist die Entfernung egal - also ignorieren wir den Offset
			result += String.format('<div class="col-1 left td">%.2f</div>', gateway.distance);
		} else {
			result += '<div class="col-1 left td">';
			if (gateway.offset != 0) {
				result += String.format('(%d)<input class="cbi-button cbi-button-clear cbi-button-small" type="submit" value="%s" title="<%:reset offset%>" name="reset_service_offset" />&nbsp;', gateway.offset, gateway.id, gateway);
			}
			<% if sorting == "etx" then %>
				distance = gateway.distance;
			<% elseif sorting == "hop" then %>
				distance = gateway.hop_count;
			<% end %>
			result += "<a href=\"https://map.opennet-initiative.de/?route=<%=on_function("get_main_ip")%>," + filter_traceroute_for_api( gateway.traceroute ) + "\" target=\"_blank\">";
			if (distance < 1) {
				// die olsrd2-Peers haben eine Entfernung zwischen Null und Eins - stelle sie genauer dar
				result += String.format('%.6f', distance + gateway.offset);
			} else {
				result += String.format('%.2f', distance + gateway.offset);
			}
			result += "</a></div>";
		}
		result += '<div class="col-1 left td">';
		if (gateway.wan_speed_upload || gateway.wan_speed_download) {
			speed = String.format('%s kbps / %s kbps', gateway.wan_speed_upload || "?", gateway.wan_speed_download || "?");
			speed_abbr = String.format('<%:upload to Internet %s kbit/s; download from Internet %s kbit/s%>', gateway.wan_speed_upload || "?", gateway.wan_speed_download || "?");
			result += String.format('<div class="vpn-wan-speed" name="speed" id="cbi-opennet-gateways-speed" ><abbr title="%s">%s</abbr></div>', speed_abbr, speed);
		}
		result += '</div>';
		result += String.format('<div class="col-1 left td" title="<%:tested %d minutes ago%>"><div id="cbi-opennet-gateways-status" status="%s">&nbsp;</div></div>', gateway.age, uci_to_yn(gateway.status));
		result += '<div class="col-1 left td"><div id="cbi-opennet-gateways-actions">';
		if (gateway.disabled) {
			result += String.format('<input class="cbi-button cbi-button-enable cbi-button-small" type="submit" value="%s" title="<%:enable%>" name="enable_service" />', gateway.id);
		} else {
			// die Verschiebe-Aktionen sind nur auf erreichbare Knoten oder im Fall manueller Sortierung anwendbar
			if ((sorting == "manual") || !unreachable) {
				result += String.format('<input class="cbi-button cbi-button-top cbi-button-small" type="submit" value="%s" title="<%:move to top and connect immediately%>" name="move_service_top" />', gateway.id);
				result += String.format('<input class="cbi-button cbi-button-up cbi-button-small" type="submit" value="%s" title="<%:move up%>" name="move_service_up" />', gateway.id);
				result += String.format('<input class="cbi-button cbi-button-down cbi-button-small" type="submit" value="%s" title="<%:move down%>" name="move_service_down" />', gateway.id);
			}
			// den Loeschen-Knopf deaktivieren wir visuell, falls die Quelle nicht "manuell" sein sollte
			if (gateway.source != "manual") {
				// Loeschen ist "disabled" - im tooltip ist eine Erklaerung enthalten
				result += String.format('<input class="cbi-button cbi-button-del cbi-button-small" type="submit" value="%s" title="<%:auto-discovered services cannot be deleted%>" name="delete_service" disabled="disabled" />', gateway.id);
			} else {
				// Loeschen ist moeglich
				result += String.format('<input class="cbi-button cbi-button-del cbi-button-small" type="submit" value="%s" title="<%:delete%>" name="delete_service" />', gateway.id);
			}
			result += String.format('<input class="cbi-button cbi-button-disable cbi-button-small" type="submit" value="%s" title="<%:disable%>" name="disable_service" />', gateway.id);
		}
		result += '</div>';
		result += '</div>'; //tr
		target_row.innerHTML += result;
		// loese die naechste Anfrage aus (falls weitere vorhanden sind)
		// Wir sollten diese Anfrage nicht frueher ausloesen, weil beispielsweise die
		// letzte Zeile ("add_table_footer") sonst hochrutscht (sie ist zu schnell abgeschlossen).
		schedule_next_service(remaining_services);
	}

	// ausserdem wird die Nachricht "Gateway-Wechsel steht bevor" ausgegeben
	function update_gateway_change_indicator() {
		var change_label = document.getElementById('on-waiting-for-gateway-change');
		if (change_label && current_gateway && best_gateway) {
			if (current_gateway != best_gateway) {
				change_label.style.display = 'block';
			} else {
				change_label.style.display = 'none';
			}
		}
	}

	// konvertiere traceroute IP Liste fuer API Anfrage
	function filter_traceroute_for_api(traceroute) {
		var ret_traceroute="";
		var ip_list = String(traceroute).split(",");
		for (var i=0; i < ip_list.length; i++) { //durchlaufe alle IPs
			var quadrupel = String(ip_list[i]).split(".");
			var ip = "";
			
			//10.x.y.z -> ignorieren, weil physischer Ort unklar
			if (quadrupel[0] == "10") {
				continue;	    
			}
			//192.168.0.x -> ignorieren, weil physischer Ort unklar
			if (quadrupel[0] == "192" && quadrupel[1] == "168" && quadrupel[2] == "0") {
                                continue;           
                        }
			
			//Konvertiere IPs zusätzlicher Interfaces auf primaere Node IP
			//192.168.[$x].y -> wenn $x=>11, dann lösche Zehnerpotenz; Bsp: 192.168.21.3->192.168.1.3 
			if (quadrupel[2] > 10) {
				for (var j=0; j < quadrupel.length; j++) { //zerlegen
					if (j == 2) {
						ip += quadrupel[j] % 10; //Rest von modulo 10
					} else {
						ip += quadrupel[j]; 
					}
					if (j <= 2) {
						ip += ".";
					}
				}
			} else {
				ip = ip_list[i];
			}
			ret_traceroute += ip + ",";
		}
		//wenn Komma am Ende, dann löschen
		if (ret_traceroute[ret_traceroute.length-1] == ",") {
			ret_traceroute = ret_traceroute.substr(0,ret_traceroute.length-1);
		}
		return ret_traceroute;
	}

//]]></script>

<%# Ein paar Infos zu Gateways im Allgemeinen %>
<h2><a id="content" name="content"><%:Opennet OpenVPN Gateways%></a></h2>
<%= html_display_message_list(on_errors, "error") %>

<div class="cbi-map">
	<fieldset class="cbi-section">
		<div class="cbi-section-comment">
			<p><%:Here you can configure the IP-Addresses of your choosen Gateways.%></p>
			<div id="infotext_gateway_short" style="display:none;">
				<a href="#" onclick="document.getElementById('infotext_gateway_long').style.display='block';document.getElementById('infotext_gateway_short').style.display='none';">...more Information...</a>
                        </div>
			<div id="infotext_gateway_long" >                                                                                                                          
				<p><%:Take a look at the Opennet wiki, if you would like to know more about the Gateway system. Usually the first found Gateway in your list is th
e one you are going to use. The Gateway will be changed automatically, if the current Gateway does not work for approximately 120 seconds.%></p>
				<p><%:Your router periodically tries to connect to all available Gateways in the Opennet network. The results of these tests are indicated in the 
"Status" column below.%></p>
				<p><%:The "distance" column indicates the routing distance to this Gateway. The selection of your preferred gateway is based on this value.%></p>
				<p><%:Additionally you may choose to rank specific gateways higher or lower than their routing distance would indicate. Please be careful with this manual intervention since it could force your connection through an inefficient path. Usually you should just keep the default values.%></p>
				<p><%:If you notice a severe slow-down of your connection, you should try to remove your manual priorities first. Use the small "clear" icon in th
e "offset/distance" column for this purpose.%></p>
                        </div>                                                                                      
                        <script type="text/javascript">                                                 
                                //by default show short description                                     
                                document.getElementById('infotext_gateway_long').style.display='none';                                                                             
                                document.getElementById('infotext_gateway_short').style.display='block';                            
                                //Why is this show/hide feature so complicated here?                                                
                                // 1. If you open the webpage without having Javascript activated the long description will be show.                                               
                                //    That's good because the javascript to show long description would not work.          
                                // 2. If you open the webpage with activated Javascript the long description will be hidden                                                        
                                //    and will only be visible by clicking on "more information"
                        </script>
		</div>
	</fieldset>
	<% if not on_bool_function("has_mig_openvpn_credentials") then %>
		<fieldset class="cbi-section"><div class="cbi-section-comment"><div class="alert-message warning">
			<p><%:The required certificate and key for your internet access via Opennet seems to be missing%>
			(<%:see%> <a href="<%= on_url("mig_openvpn", "zertifikat") %>"><%:Certificate management%></a>).</p>
		</div></div></fieldset>
	<% end %>
</div>


<%# Dynamisch erzeugte Gateway-Liste, sowie Eingabe manueller Gateways %>
<div class="cbi-map">
	<fieldset class="cbi-section">
		<legend><%:Gateway-List%></legend>
		<div class="cbi-section-comment"><%=luci.i18n.translatef('You can change the sorting mechanism of gateways in the <a href="%s">Service Settings section</a>.', on_url("basis", "einstellungen"))%></div>
		<fieldset class="cbi-section-node">
			<form name="gateways" method="post" action="<%=REQUEST_URI%>">
			<div class="table" id="on-gateway-list">
			<div class="tr cbi-section_table_titles">
					<div class="th col-1 left"></div>
					<div class="th col-1 center"><%:active%></div>
					<div class="th col-1 left"><%:IP-Address%></div>
					<div class="th col-1 left">
						<abbr title="<%:Short name of the public gateway server (if known)%>">
							<%:Gateway Server%></abbr></div>
					<div class="th col-1 left">
						<% if sorting ~= "manual" then %>
							<%# es gibt keinen Offset im manual Modus %>
							<abbr title="<%:This offset is added to the values before use/display%>">Offset</abbr> /
						<% end %>
						<% if sorting == "hop" then %>
							<abbr title="<%:Number of Hops to the Gateway%>"><%:Distance%></abbr>
						<% else %>
							<%# im manual und etx Modus zeigen wir den ETX-Wert an %>
							<abbr title="<%:Expected Transmission Count - Quality of the Connection to the Gateway regarding OLSR%>"><%:ETX%></abbr>
						<% end %>
					</div>

					<div class="th col-1 left"><abbr title="<%:Announced Upload / Download Speed.%>"><%:Speed%></abbr></div>

					<div class="th col-1 left"><%:VPN-Status%></div>
					<div class="th col-1 left"><%:Actions%></div>
			</div> <%# tr %>

			</div> <%# table %>
			</form>
			<center><%= get_html_loading_spinner('gateway-list-spinner', 'vertical-align:middle;') %></center>
			<p id="on-waiting-for-gateway-change" style="display:none">
				<%:The first reachable Gateway is currently not in use. The Tunnel will be restarted and the Gateway will be changed in a few minutes. This change will be shown after a reload of this page.%>
			</p>
		</fieldset>
	</fieldset>


	<%# Manuelles Hinzufügen weiterer Gateways %>
	<fieldset class="cbi-section">
	<legend><%:Manually add a Gateway%></legend>
		<div class="cbi-section-comment">
			<p><%:Developers or experimenting users may want to add custom gateways here.%></p>
		</div>
		<form name="gateways" method="post" action="<%=REQUEST_URI%>">
		<div class="cbi-value">
			<input type="hidden" name="service_type" value="gw" />
			<input type="hidden" name="service_path" value="/" />
			<input type="hidden" name="service_scheme" value="openvpn" />
			<input type="hidden" name="service_protocol" value="udp" />
			<input type="hidden" name="service_details" value="" />
			<label class="cbi-value-title" for="service_host"><%:Add Gateway manually%>:</label>
			<div class="cbi-value-field">
				<input class="cbi-input-text" name="service_host" id="service_host" size="16" />
			</div>
			<label class="cbi-value-title" for="service_port"><%:Port%>:</label>
			<div class="cbi-value-field">
				<input class="cbi-input-text" name="service_port"
					id="service_port" size="5"
					value="<%=on_function("get_variable", {"DEFAULT_MIG_PORT"})%>" />
				<input class="cbi-button cbi-button-apply" type="submit"
					id="add_service" name="add_service" value="<%:Add%>" />
			</div>
		</div>
		</form>
	</fieldset>
</div>
<%+footer%>
