mirror of
https://github.com/bilde2910/Hauk.git
synced 2026-01-23 02:24:09 +00:00
Merge 33e19cb886 into 2a7e9ac0b0
This commit is contained in:
commit
ec29db19c5
9 changed files with 161 additions and 76 deletions
|
|
@ -1,7 +1,4 @@
|
|||
FROM php:apache
|
||||
COPY backend-php/ /var/www/html/
|
||||
COPY frontend/ /var/www/html/
|
||||
COPY docker/start.sh .
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev libssl-dev && \
|
||||
|
|
@ -12,7 +9,11 @@ RUN apt-get update && \
|
|||
|
||||
EXPOSE 80/tcp
|
||||
VOLUME /etc/hauk
|
||||
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
COPY docker/start.sh .
|
||||
RUN chmod +x ./start.sh
|
||||
COPY backend-php/ /var/www/html/
|
||||
COPY frontend/ /var/www/html/
|
||||
|
||||
CMD ["./start.sh"]
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ public enum Constants {
|
|||
public static final String PACKET_PARAM_SPEED = "spd";
|
||||
public static final String PACKET_PARAM_TIMESTAMP = "time";
|
||||
public static final String PACKET_PARAM_USERNAME = "usr";
|
||||
public static final String PACKET_PARAM_ALTITUDE = "alt";
|
||||
|
||||
// Packet OK response header. All valid packets start with this line.
|
||||
public static final String PACKET_RESPONSE_OK = "OK";
|
||||
|
|
|
|||
|
|
@ -5,9 +5,15 @@ import android.location.Location;
|
|||
import android.util.Base64;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import info.varden.hauk.Constants;
|
||||
import info.varden.hauk.R;
|
||||
|
|
@ -48,36 +54,17 @@ public abstract class LocationUpdatePacket extends Packet {
|
|||
super(ctx, session.getServerURL(), session.getConnectionParameters(), Constants.URL_PATH_POST_LOCATION);
|
||||
setParameter(Constants.PACKET_PARAM_SESSION_ID, session.getID());
|
||||
|
||||
if (session.getDerivableE2EKey() == null) {
|
||||
// If not using end-to-end encryption, send parameters in plain text.
|
||||
setParameter(Constants.PACKET_PARAM_LATITUDE, String.valueOf(location.getLatitude()));
|
||||
setParameter(Constants.PACKET_PARAM_LONGITUDE, String.valueOf(location.getLongitude()));
|
||||
setParameter(Constants.PACKET_PARAM_PROVIDER_ACCURACY, String.valueOf(accuracy.getMode()));
|
||||
setParameter(Constants.PACKET_PARAM_TIMESTAMP, String.valueOf(System.currentTimeMillis() / (double) TimeUtils.MILLIS_PER_SECOND));
|
||||
Cipher cipher = initCipher(session);
|
||||
|
||||
// Not all devices provide these parameters:
|
||||
if (location.hasSpeed()) setParameter(Constants.PACKET_PARAM_SPEED, String.valueOf(location.getSpeed()));
|
||||
if (location.hasAccuracy()) setParameter(Constants.PACKET_PARAM_ACCURACY, String.valueOf(location.getAccuracy()));
|
||||
} else {
|
||||
// We're using end-to-end encryption - generate an IV and encrypt all parameters.
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(Constants.E2E_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, session.getDerivableE2EKey().deriveSpec(), new SecureRandom());
|
||||
byte[] iv = cipher.getIV();
|
||||
setParameter(Constants.PACKET_PARAM_INIT_VECTOR, Base64.encodeToString(iv, Base64.DEFAULT));
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_LATITUDE, location.getLatitude(), cipher);
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_LONGITUDE, location.getLongitude(), cipher);
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_PROVIDER_ACCURACY, accuracy.getMode(), cipher);
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_TIMESTAMP, System.currentTimeMillis() / (double) TimeUtils.MILLIS_PER_SECOND, cipher);
|
||||
|
||||
setParameter(Constants.PACKET_PARAM_LATITUDE, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getLatitude()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
setParameter(Constants.PACKET_PARAM_LONGITUDE, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getLongitude()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
setParameter(Constants.PACKET_PARAM_PROVIDER_ACCURACY, Base64.encodeToString(cipher.doFinal(String.valueOf(accuracy.getMode()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
setParameter(Constants.PACKET_PARAM_TIMESTAMP, Base64.encodeToString(cipher.doFinal(String.valueOf(System.currentTimeMillis() / (double) TimeUtils.MILLIS_PER_SECOND).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
|
||||
// Not all devices provide these parameters:
|
||||
if (location.hasSpeed()) setParameter(Constants.PACKET_PARAM_SPEED, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getSpeed()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
if (location.hasAccuracy()) setParameter(Constants.PACKET_PARAM_ACCURACY, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getAccuracy()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
} catch (Exception e) {
|
||||
Log.e("Error was thrown when encrypting location data", e); //NON-NLS
|
||||
}
|
||||
}
|
||||
// Not all devices provide these parameters:
|
||||
if (location.hasSpeed()) encryptAndSetParameter(Constants.PACKET_PARAM_SPEED, location.getSpeed(), cipher);
|
||||
if (location.hasAccuracy()) encryptAndSetParameter(Constants.PACKET_PARAM_ACCURACY, location.getAccuracy(), cipher);
|
||||
if (location.hasAltitude()) encryptAndSetParameter(Constants.PACKET_PARAM_ALTITUDE, location.getAltitude(), cipher);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DesignForExtension")
|
||||
|
|
@ -113,4 +100,33 @@ public abstract class LocationUpdatePacket extends Packet {
|
|||
throw new ServerException(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private Cipher initCipher(Session session) {
|
||||
Cipher cipher = null;
|
||||
if (session.getDerivableE2EKey() != null) {
|
||||
try {
|
||||
cipher = Cipher.getInstance(Constants.E2E_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, session.getDerivableE2EKey().deriveSpec(), new SecureRandom());
|
||||
byte[] iv = cipher.getIV();
|
||||
setParameter(Constants.PACKET_PARAM_INIT_VECTOR, Base64.encodeToString(iv, Base64.DEFAULT));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | NoSuchPaddingException exception) {
|
||||
Log.e("Error was thrown while initializing E2E encryption", exception); //NON-NLS
|
||||
}
|
||||
}
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private <V> void encryptAndSetParameter(String key, V value, Cipher cipher) {
|
||||
if (cipher != null) {
|
||||
// We're using end-to-end encryption - generate an IV and encrypt all parameters.
|
||||
try {
|
||||
setParameter(key, Base64.encodeToString(cipher.doFinal(String.valueOf(value).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
} catch (BadPaddingException | IllegalBlockSizeException exception) {
|
||||
Log.e("Error was thrown while encrypting location data", exception); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
// If not using end-to-end encryption, send parameters in plain text.
|
||||
setParameter(key, String.valueOf(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,45 +21,37 @@ $sid = $_POST["sid"];
|
|||
$session = new Client($memcache, $sid);
|
||||
if (!$session->exists()) die($LANG['session_expired']."\n");
|
||||
|
||||
if (!$session->isEncrypted()) {
|
||||
// Perform input validation.
|
||||
$lat = floatval($_POST["lat"]);
|
||||
$lon = floatval($_POST["lon"]);
|
||||
$time = floatval($_POST["time"]);
|
||||
if ($lat < -90 || $lat > 90 || $lon < -180 || $lon > 180) die($LANG['location_invalid']."\n");
|
||||
$lat = $_POST["lat"];
|
||||
$lon = $_POST["lon"];
|
||||
$time = $_POST["time"];
|
||||
$speed = isset($_POST["spd"]) ? $_POST["spd"] : null;
|
||||
$altitude = isset($_POST["alt"]) ? $_POST["alt"] : null;
|
||||
$accuracy = isset($_POST["acc"]) ? $_POST["acc"] : null;
|
||||
$provider = isset($_POST["prv"]) ? $_POST["prv"] : null;
|
||||
|
||||
// Not all devices report speed and accuracy, but if available, report them
|
||||
// too.
|
||||
$speed = isset($_POST["spd"]) ? floatval($_POST["spd"]) : null;
|
||||
$accuracy = isset($_POST["acc"]) ? floatval($_POST["acc"]) : null;
|
||||
$provider = isset($_POST["prv"]) && $_POST["prv"] == "1" ? 1 : 0;
|
||||
|
||||
// The location data object contains the sharing interval (i), duration (d)
|
||||
// and a location list (l). Each entry in the location list contains a
|
||||
// latitude, longitude, timestamp, provider, accuracy and speed, in that
|
||||
// order, as an array.
|
||||
$session->addPoint([$lat, $lon, $time, $provider, $accuracy, $speed])->save();
|
||||
|
||||
} else {
|
||||
// Input validation cannot be performed for end-to-end encrypted data.
|
||||
$lat = $_POST["lat"];
|
||||
$lon = $_POST["lon"];
|
||||
$time = $_POST["time"];
|
||||
$speed = isset($_POST["spd"]) ? $_POST["spd"] : null;
|
||||
$accuracy = isset($_POST["acc"]) ? $_POST["acc"] : null;
|
||||
$provider = isset($_POST["prv"]) ? $_POST["prv"] : null;
|
||||
// The location data object contains the sharing interval (i), duration (d)
|
||||
// and a location list (l). Each entry in the location list contains a
|
||||
// latitude, longitude, timestamp, provider, accuracy and speed, in that
|
||||
// order, as an array.
|
||||
$point = [$lat, $lon, $time, $provider, $accuracy, $speed, $altitude];
|
||||
|
||||
if ($session->isEncrypted()) {
|
||||
// End-to-end encrypted connections also have an IV field used to decrypt
|
||||
// the data fields.
|
||||
requirePOST("iv");
|
||||
$iv = $_POST["iv"];
|
||||
|
||||
// The IV field is prepended to the array to send to the client.
|
||||
$session->addPoint([$iv, $lat, $lon, $time, $provider, $accuracy, $speed])->save();
|
||||
array_unshift($point , $iv);
|
||||
} else {
|
||||
// Perform input validation
|
||||
if (floatval($lat) < -90 || floatval($lat) > 90 || floatval($lon) < -180 || floatval($lon) > 180) die($LANG['location_invalid']."\n");
|
||||
}
|
||||
|
||||
$session->addPoint($point)->save();
|
||||
|
||||
if ($session->hasExpired()) {
|
||||
echo $LANG['session_expired']."\n";
|
||||
} else {
|
||||
echo "OK\n".getConfig("public_url")."?%s\n".implode(",", $session->getTargetIDs())."\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -15,5 +15,8 @@ var MAX_POINTS = <?php echo json_encode(getConfig("max_shown_pts")); ?>;
|
|||
var VELOCITY_DELTA_TIME = <?php echo json_encode(getConfig("v_data_points")); ?>;
|
||||
var TRAIL_COLOR = <?php echo json_encode(getConfig("trail_color")); ?>;
|
||||
var VELOCITY_UNIT = <?php echo json_encode(getConfig("velocity_unit")); ?>;
|
||||
var ALTITUDE_UNIT = <?php echo json_encode(getConfig("altitude_unit")); ?>;
|
||||
var SHOW_VELOCITY = <?php echo json_encode(getConfig("show_velocity")); ?>;
|
||||
var SHOW_ALTITUDE_AMSL = <?php echo json_encode(getConfig("show_altitude_amsl")); ?>;
|
||||
var OFFLINE_TIMEOUT = <?php echo json_encode(getConfig("offline_timeout")); ?>;
|
||||
var REQUEST_TIMEOUT = <?php echo json_encode(getConfig("request_timeout")); ?>;
|
||||
|
|
|
|||
|
|
@ -274,6 +274,16 @@
|
|||
// KILOMETERS_PER_HOUR, MILES_PER_HOUR, METERS_PER_SECOND
|
||||
"velocity_unit" => KILOMETERS_PER_HOUR,
|
||||
|
||||
// The unit of measurement of altitude. Valid are:
|
||||
// METERS, FEET
|
||||
"altitude_unit" => METERS,
|
||||
|
||||
// Display velocity below marker
|
||||
"show_velocity" => true,
|
||||
|
||||
// Display altitude AMSL below marker
|
||||
"show_altitude_amsl"=> true,
|
||||
|
||||
// The publicly accessible URL to reach Hauk, with trailing slash.
|
||||
"public_url" => 'https://example.com/'
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,16 @@ const METERS_PER_SECOND = array(
|
|||
"mpsMultiplier" => 1,
|
||||
"unit" => "m/s"
|
||||
);
|
||||
const METERS = array(
|
||||
// Absolute distance in meters
|
||||
"metersMultiplier" => 1,
|
||||
"unit" => "m"
|
||||
);
|
||||
const FEET = array(
|
||||
// Absolute distance in feet
|
||||
"metersMultiplier" => 3.280839895,
|
||||
"unit" => "ft"
|
||||
);
|
||||
|
||||
// Load fallback language.
|
||||
include(__DIR__."/lang/en/texts.php");
|
||||
|
|
@ -149,6 +159,9 @@ const DEFAULTS = array(
|
|||
"v_data_points" => 2,
|
||||
"trail_color" => '#d80037',
|
||||
"velocity_unit" => KILOMETERS_PER_HOUR,
|
||||
"altitude_unit" => METERS,
|
||||
"show_velocity" => true,
|
||||
"show_altitude_amsl" => true,
|
||||
"public_url" => 'https://example.com/'
|
||||
|
||||
);
|
||||
|
|
|
|||
|
|
@ -700,12 +700,13 @@ function processUpdate(data, init) {
|
|||
var lastPoint = shares[user].points.length > 0 ? shares[user].points[shares[user].points.length - 1] : null;
|
||||
|
||||
for (var i = 0; i < users[user].length; i++) {
|
||||
var lat = users[user][i][0];
|
||||
var lon = users[user][i][1];
|
||||
var time = users[user][i][2];
|
||||
var prov = users[user][i][3];
|
||||
var acc = users[user][i][4];
|
||||
var spd = users[user][i][5];
|
||||
var lat = getValueOrNull(users[user][i],0);
|
||||
var lon = getValueOrNull(users[user][i],1);
|
||||
var time = getValueOrNull(users[user][i],2);
|
||||
var prov = getValueOrNull(users[user][i],3);
|
||||
var acc = getValueOrNull(users[user][i],4);
|
||||
var spd = getValueOrNull(users[user][i],5);
|
||||
var alt = getValueOrNull(users[user][i],6);
|
||||
|
||||
// Default to "Fine" provider for older clients.
|
||||
if (prov === null) prov = LOC_PROVIDER_FINE;
|
||||
|
|
@ -726,14 +727,23 @@ function processUpdate(data, init) {
|
|||
'<div class="arrow still-' + shares[user].state + '" id="arrow-' + shares[user].id + '"></div>' +
|
||||
'<p class="' + shares[user].state + '" id="label-' + shares[user].id + '">' +
|
||||
'<span id="nickname-' + shares[user].id + '"></span>' +
|
||||
'<span class="velocity">' +
|
||||
'<span id="velocity-' + shares[user].id + '">0.0</span> ' +
|
||||
VELOCITY_UNIT.unit +
|
||||
'</span><span class="offline" id="last-seen-' + shares[user].id + '">' +
|
||||
'<span class="metric' + (SHOW_VELOCITY ? "" : " hidden") + '">' +
|
||||
'<span class="metric-label">vel:</span>' +
|
||||
'<span class="metric-value"><span id="velocity-' + shares[user].id + '">0.0</span> ' +
|
||||
VELOCITY_UNIT.unit + '</span>' +
|
||||
'<br>' +
|
||||
'</span>' +
|
||||
'<span id="altitude-container-' + shares[user].id + '" class="metric'+ (SHOW_ALTITUDE_AMSL ? "" : " hidden") + '">' +
|
||||
'<span class="metric-label">alt:</span>' +
|
||||
'<span class="metric-value"><span id="altitude-' + shares[user].id + '">0.0</span> ' +
|
||||
ALTITUDE_UNIT.unit + '</span>' +
|
||||
'</span>' +
|
||||
'<span class="offline" id="last-seen-' + shares[user].id + '">' +
|
||||
'</span>' +
|
||||
'</p>' +
|
||||
'</div>',
|
||||
iconAnchor: [33, 18]
|
||||
// FIXME: hard-coded and dependend on style.css .marker
|
||||
iconAnchor: [48, 18]
|
||||
});
|
||||
shares[user].marker = L.marker([lat, lon], {icon: shares[user].icon}).on("click", function() {
|
||||
follow(this.haukUser);
|
||||
|
|
@ -754,7 +764,7 @@ function processUpdate(data, init) {
|
|||
shares[user].circle.setLatLng([lat, lon]);
|
||||
if (acc !== null) shares[user].circle.setRadius(acc);
|
||||
}
|
||||
shares[user].points.push({lat: lat, lon: lon, line: line, time: time, spd: spd, acc: acc});
|
||||
shares[user].points.push({lat: lat, lon: lon, line: line, time: time, spd: spd, acc: acc, alt: alt});
|
||||
lastPoint = shares[user].points[shares[user].points.length - 1];
|
||||
}
|
||||
}
|
||||
|
|
@ -797,6 +807,20 @@ function processUpdate(data, init) {
|
|||
vel = velocity(dist, time);
|
||||
eVelocity.textContent = vel.toFixed(1);;
|
||||
}
|
||||
|
||||
// Altitude (If available)
|
||||
var eAltitude = document.getElementById("altitude-" + shares[user].id);
|
||||
var alt = 0;
|
||||
var eAltitudeContainer = document.getElementById("altitude-container-" + shares[user].id);
|
||||
if (lastPoint !== null && lastPoint.alt !== null && eAltitude !== null) {
|
||||
alt = lastPoint.alt * ALTITUDE_UNIT.metersMultiplier;
|
||||
eAltitude.textContent = alt.toFixed(1);
|
||||
if (SHOW_ALTITUDE_AMSL) {
|
||||
eAltitudeContainer.classList.remove("hidden");
|
||||
}
|
||||
} else {
|
||||
eAltitudeContainer.classList.add("hidden");
|
||||
}
|
||||
|
||||
// Flag that the first location has been received, for map centering.
|
||||
if (lastPoint !== null && !hasReceivedFirst) {
|
||||
|
|
@ -906,6 +930,14 @@ function processUpdate(data, init) {
|
|||
}
|
||||
}
|
||||
|
||||
function getValueOrNull(points,idx) {
|
||||
var value = null;
|
||||
if (idx < points.length) {
|
||||
value = points[idx];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calculates the distance between two points on a sphere using the Haversine
|
||||
// algorithm.
|
||||
function distance(from, to) {
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ a:last-child > .store-icon {
|
|||
|
||||
/* The outer marker div. */
|
||||
.marker {
|
||||
width: 66px;
|
||||
width: 96px;
|
||||
height: 62px;
|
||||
}
|
||||
|
||||
|
|
@ -312,8 +312,8 @@ a:last-child > .store-icon {
|
|||
width: 100%;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
padding: 2px 0;
|
||||
line-height: 100%;
|
||||
padding: 5px 0 5px;
|
||||
line-height: 125%;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
|
@ -334,7 +334,7 @@ a:last-child > .store-icon {
|
|||
background-color: rgba(165,0,42,0.5);
|
||||
}
|
||||
|
||||
.marker p.dead > span.velocity {
|
||||
.marker p.dead > span.metric {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
@ -343,3 +343,20 @@ a:last-child > .store-icon {
|
|||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
display:inline-block;
|
||||
width: 30%;
|
||||
text-align:right;
|
||||
vertical-align: bottom;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
display:inline-block;
|
||||
width: 70%;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue