2019-07-15 20:29:19 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* BlueWeather
|
|
|
|
*
|
|
|
|
* PHP version 7.2
|
|
|
|
*
|
|
|
|
* @category Tools
|
|
|
|
* @package BlueWeather
|
|
|
|
* @author Dorian Zedler <dorian@itsblue.de>
|
|
|
|
* @license GPLV3 gpl.com
|
|
|
|
* @link itsblue.de
|
|
|
|
*/
|
|
|
|
|
|
|
|
class BlueWeather
|
|
|
|
{
|
|
|
|
private $_config;
|
|
|
|
private $_con;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param mixed $_config config array containing some stuff
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function __construct($_config)
|
|
|
|
{
|
|
|
|
$this->_config = $_config;
|
|
|
|
$this->_con = mysqli_connect(
|
|
|
|
$_config['dbhost'],
|
|
|
|
$_config['dbuser'],
|
|
|
|
$_config['dbpassword'],
|
|
|
|
$_config['dbname']
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!$this->_con) {
|
|
|
|
echo "Error connecting to database: " . mysqli_connect_error();
|
|
|
|
http_response_code(500);
|
|
|
|
die();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-07-16 17:42:33 +02:00
|
|
|
// -------------------
|
|
|
|
// - user management -
|
|
|
|
// -------------------
|
2019-07-15 20:29:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to login users
|
|
|
|
*
|
|
|
|
* @param string $username username
|
|
|
|
* @param string $password passowrd
|
|
|
|
*
|
|
|
|
* @return string '' or session token
|
|
|
|
*/
|
|
|
|
public function loginUser($username, $password)
|
|
|
|
{
|
|
|
|
$sql = "SELECT * FROM `users`
|
|
|
|
WHERE`username`=\"".$this->_con->real_escape_string($username)."\"";
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
// only one row will be returned
|
|
|
|
$data = $result->fetch_assoc();
|
|
|
|
|
|
|
|
if (!password_verify($password, $data['password'])) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
//generate random token
|
|
|
|
$length = 10;
|
|
|
|
$str = "";
|
|
|
|
$characters = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9'));
|
|
|
|
$max = count($characters) - 1;
|
|
|
|
for ($i = 0; $i < $length; $i++) {
|
|
|
|
$rand = mt_rand(0, $max);
|
|
|
|
$str .= $characters[$rand];
|
|
|
|
}
|
|
|
|
$token_hash = password_hash($str, PASSWORD_DEFAULT);
|
|
|
|
|
|
|
|
$sql = 'INSERT INTO `sessions` (userId, session)
|
|
|
|
VALUES ("'. $data['id'] .'", "'. $token_hash .'")';
|
|
|
|
|
|
|
|
if (!$this->_con->query($sql)) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $token_hash;
|
|
|
|
}
|
|
|
|
|
2019-07-16 17:42:33 +02:00
|
|
|
/**
|
|
|
|
* Function to check if a session token exists and get the corresponding user
|
|
|
|
*
|
|
|
|
* @param string $session session token
|
|
|
|
*
|
|
|
|
* @return int (-1: does not exist; x>0: userId)
|
|
|
|
*/
|
|
|
|
public function checkSession($session)
|
|
|
|
{
|
|
|
|
$sql = "SELECT * FROM `sessions`
|
|
|
|
WHERE`session`=\"".$this->_con->real_escape_string($session)."\"";
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
if (!$result->num_rows > 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only one row will be returned
|
|
|
|
$data = $result->fetch_assoc();
|
|
|
|
|
|
|
|
return($data['userId']);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-15 20:29:19 +02:00
|
|
|
// --------------------
|
|
|
|
// - getter functions -
|
|
|
|
// --------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to get all locations from the database
|
|
|
|
*
|
|
|
|
* @return mixed (array with all locations)
|
|
|
|
*/
|
|
|
|
public function getAllLocations()
|
|
|
|
{
|
|
|
|
// get all locations
|
|
|
|
$sql = "SELECT * FROM `locations`";
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
//loop through the returned data
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
|
|
$locations[] = $row;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $locations;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to get the data of a specific location
|
|
|
|
*
|
|
|
|
* @param int $locId id of the location to return data of
|
|
|
|
* @param mixed $range contains 'from' and 'to' as unix timestamps
|
2019-07-30 13:07:49 +02:00
|
|
|
* @param mixed $maxVals array containing:
|
|
|
|
* 'count': maximum measvals to be transmitted
|
|
|
|
* 'mode':
|
|
|
|
* - 'newest': will return the newest <maxVals> values
|
|
|
|
* - 'oldest': will return the oldest <maxVals> values
|
|
|
|
* - 'avg' : <maxVals> sub avarages will be calculated
|
2019-07-15 20:29:19 +02:00
|
|
|
*
|
|
|
|
* @return mixed object with all information about the location (see docs)
|
|
|
|
*/
|
|
|
|
function getLocationData($locId, $range, $maxVals)
|
|
|
|
{
|
2019-07-15 20:42:06 +02:00
|
|
|
|
|
|
|
$locId = $this->_con->real_escape_string($locId);
|
|
|
|
$range["from"] = $this->_con->real_escape_string($range["from"]);
|
|
|
|
$range["to"] = $this->_con->real_escape_string($range["to"]);
|
|
|
|
|
2019-07-15 20:29:19 +02:00
|
|
|
$sql = "SELECT * FROM `locations`
|
|
|
|
WHERE`id`=$locId";
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
// only one row will be returned
|
|
|
|
$data = $result->fetch_assoc();
|
|
|
|
|
|
|
|
if (!isset($range['from']) || $range['from'] === "") {
|
|
|
|
$range['from'] = time() - 24 * 60 * 60;
|
|
|
|
}
|
2019-07-30 13:07:49 +02:00
|
|
|
|
|
|
|
if ($range['from'] < 0) {
|
|
|
|
$range['from'] = time() + $range['from'];
|
|
|
|
}
|
2019-07-15 20:29:19 +02:00
|
|
|
|
|
|
|
if (!isset($range['to']) || $range['to'] === "") {
|
|
|
|
$range['to'] = time();
|
|
|
|
}
|
|
|
|
|
|
|
|
// get all measvalues of given location
|
|
|
|
$sql = "SELECT M.measvalue,M.sensorid,M.timestamp FROM measvalues M
|
|
|
|
JOIN sensors S ON M.sensorid = S.id
|
|
|
|
JOIN locations L ON S.locationid=L.id
|
|
|
|
WHERE L.id=$locId AND M.timestamp > " . $range['from'] .
|
2019-07-15 21:00:30 +02:00
|
|
|
" AND M.timestamp < " . $range['to'] .
|
|
|
|
" ORDER BY timestamp ASC";
|
2019-07-15 20:29:19 +02:00
|
|
|
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
|
|
$measvalues[] = $row;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get all sensors of given location
|
|
|
|
$sql = "SELECT * FROM `sensors`
|
|
|
|
WHERE `locationid` = $locId";
|
|
|
|
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
//loop through the returned data
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
|
|
unset($row['locationid']); // remove locId as it is redundant
|
|
|
|
$sensors[] = $row;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get all value types
|
|
|
|
$sql = "SELECT * FROM `valuetypes`";
|
|
|
|
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
2019-07-30 13:07:49 +02:00
|
|
|
// get all necessaray valuetypes
|
2019-07-15 20:29:19 +02:00
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
|
|
foreach ($sensors as $sensor) {
|
|
|
|
if ($sensor['valuetypeid'] == $row['id']) {
|
|
|
|
$valuetypes[] = $row;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 13:07:49 +02:00
|
|
|
if (isset($maxVals) && isset($maxVals['count']) && isset($maxVals['mode'])) {
|
2019-07-15 20:29:19 +02:00
|
|
|
// build the new measvalues array with respect to maxVals for each sensor
|
|
|
|
|
|
|
|
$finalMeasvals = array();
|
|
|
|
|
|
|
|
foreach ($sensors as $sensor) {
|
|
|
|
|
|
|
|
$rawMeasvalsOfThisSensor = array();
|
|
|
|
$finalMeasvalsOfThisSensor = array();
|
|
|
|
|
2019-07-30 13:07:49 +02:00
|
|
|
// get all measvalues of the current sensor
|
2019-07-15 20:29:19 +02:00
|
|
|
foreach ($measvalues as $measvalue) {
|
|
|
|
if ($measvalue["sensorid"] === $sensor["id"]) {
|
|
|
|
array_push($rawMeasvalsOfThisSensor, $measvalue);
|
|
|
|
}
|
|
|
|
}
|
2019-07-16 17:42:33 +02:00
|
|
|
|
2019-07-30 13:07:49 +02:00
|
|
|
if (count($rawMeasvalsOfThisSensor) <= $maxVals['count']) {
|
|
|
|
// measvls don't exceed maxVals -> nothng needs to be done
|
2019-07-16 17:44:04 +02:00
|
|
|
$finalMeasvals = array_merge(
|
|
|
|
$finalMeasvals, $rawMeasvalsOfThisSensor
|
|
|
|
);
|
2019-07-16 17:42:33 +02:00
|
|
|
continue;
|
|
|
|
}
|
2019-07-15 20:29:19 +02:00
|
|
|
|
2019-07-30 13:07:49 +02:00
|
|
|
switch($maxVals['mode']) {
|
|
|
|
case "newest":
|
|
|
|
$finalMeasvalsOfThisSensor = array_slice(
|
|
|
|
$rawMeasvalsOfThisSensor,
|
|
|
|
count($rawMeasvalsOfThisSensor) - $maxVals['count'],
|
|
|
|
count($rawMeasvalsOfThisSensor)
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "oldest":
|
|
|
|
$finalMeasvalsOfThisSensor = array_slice(
|
|
|
|
$rawMeasvalsOfThisSensor, 0, $maxVals['count']
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "avg":
|
|
|
|
// always sum up the same amount of values to get a new array
|
|
|
|
// which doesn't have more than $maxVals values
|
|
|
|
$countOfValuesForAvarage = intval(
|
|
|
|
round(count($rawMeasvalsOfThisSensor) / $maxVals['count'], 0)
|
|
|
|
);
|
|
|
|
|
|
|
|
$takenValuesForNextSum = 0;
|
|
|
|
$tmpMeasvalueSum = 0;
|
|
|
|
$tmpTimestampSum = 0;
|
|
|
|
|
|
|
|
for ($i = 0; $i < count($rawMeasvalsOfThisSensor); $i++) {
|
|
|
|
// loop through all measvals of the sensor
|
|
|
|
$measvalue = $rawMeasvalsOfThisSensor[$i];
|
|
|
|
|
|
|
|
if ($measvalue["sensorid"] === $sensor["id"]) {
|
|
|
|
$tmpMeasvalueSum += $measvalue["measvalue"];
|
|
|
|
$tmpTimestampSum += $measvalue["timestamp"];
|
|
|
|
$takenValuesForNextSum += 1;
|
|
|
|
}
|
|
|
|
if ($takenValuesForNextSum === $countOfValuesForAvarage
|
|
|
|
|| $i === count($rawMeasvalsOfThisSensor) - 1
|
|
|
|
) {
|
|
|
|
array_push(
|
|
|
|
$finalMeasvalsOfThisSensor,
|
|
|
|
array(
|
|
|
|
"measvalue" => round(
|
|
|
|
$tmpMeasvalueSum / $takenValuesForNextSum, 2
|
|
|
|
),
|
|
|
|
"timestamp" => round(
|
|
|
|
$tmpTimestampSum / $takenValuesForNextSum, 0
|
|
|
|
),
|
|
|
|
"sensorid" => $sensor["id"]
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
$takenValuesForNextSum = 0;
|
|
|
|
$tmpMeasvalueSum = 0;
|
|
|
|
$tmpTimestampSum = 0;
|
|
|
|
}
|
2019-07-15 20:29:19 +02:00
|
|
|
}
|
2019-07-30 13:07:49 +02:00
|
|
|
break;
|
2019-07-15 20:29:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// insert the new vals of this sensor into the global new vals
|
|
|
|
$finalMeasvals = array_merge(
|
|
|
|
$finalMeasvals, $finalMeasvalsOfThisSensor
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$measvalues = $finalMeasvals;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find actual range
|
|
|
|
$min = null;
|
|
|
|
$max = null;
|
|
|
|
|
|
|
|
foreach ($measvalues as $value) {
|
|
|
|
if ($value['timestamp'] < $min || !isset($min)) {
|
|
|
|
$min = $value['timestamp'];
|
|
|
|
} else if ($value['timestamp'] > $max || !isset($max)) {
|
|
|
|
$max = $value['timestamp'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add sensors and value types to data object
|
|
|
|
$data['measvalues'] = $measvalues;
|
|
|
|
$data['sensors'] = $sensors;
|
|
|
|
$data['valuetypes'] = $valuetypes;
|
|
|
|
$data['range'] = array('from' => $min, 'to' => $max);
|
|
|
|
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
2019-07-29 00:24:01 +02:00
|
|
|
// --------------------
|
|
|
|
// - setter functions -
|
|
|
|
// --------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to generate an api key for specific user
|
|
|
|
*
|
|
|
|
* @param int $userId userId
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
function createApiKey($userId)
|
|
|
|
{
|
|
|
|
|
|
|
|
//generate random token
|
|
|
|
$length = 10;
|
|
|
|
$str = "";
|
|
|
|
$characters = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9'));
|
|
|
|
$max = count($characters) - 1;
|
|
|
|
for ($i = 0; $i < $length; $i++) {
|
|
|
|
$rand = mt_rand(0, $max);
|
|
|
|
$str .= $characters[$rand];
|
|
|
|
}
|
|
|
|
|
|
|
|
$apiKey = password_hash($str, PASSWORD_DEFAULT);
|
|
|
|
|
|
|
|
$sql = "INSERT INTO `apikeys` (userid, apikey)
|
|
|
|
VALUES (\"$userId\", \"$apiKey\")";
|
|
|
|
|
|
|
|
return $this->_con->query($sql);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to validate and insert submited sensor data
|
|
|
|
*
|
|
|
|
* @param mixed $sensorData object containing
|
|
|
|
* - identity (int)
|
|
|
|
* - signature (string)
|
|
|
|
* - data (string: JSON array[array[sensorId,measvalue,timestamp]])
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
* 'status': 200: was inserted
|
|
|
|
* 400: format of request was wrong
|
|
|
|
* 401: signture verify failed
|
|
|
|
* 404: key identity was not found
|
|
|
|
* 500: internal error
|
|
|
|
* 900: format of data JSON string was invalid
|
|
|
|
* 901: signature was fine but one or more sensors have been ignored due to some error (see 'data') all other sensors were processes successfully
|
|
|
|
* 'data': Array[Array[sensorId, errorCode]] sensors which were ignored due to some error (401: user doesn't own sensor; 404: senso wasn't found)
|
|
|
|
* only set if status is 901
|
|
|
|
*/
|
|
|
|
function processSensorData($sensorData)
|
|
|
|
{
|
|
|
|
|
|
|
|
// check if request structure is valid
|
|
|
|
if (!isset($sensorData['identity']) || !isset($sensorData['signature']) || !isset($sensorData['data'])) {
|
|
|
|
return array("status"=>400);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get private key from database
|
|
|
|
$thisIdentity = $this->_con->real_escape_string($sensorData['identity']);
|
|
|
|
$thisData = $sensorData['data'];
|
|
|
|
|
|
|
|
// get public key of user
|
|
|
|
$sql = "SELECT * FROM `apikeys` WHERE id=$thisIdentity";
|
|
|
|
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
if ($result->num_rows < 1) {
|
|
|
|
// public key was not found
|
|
|
|
return array("status"=>404);
|
|
|
|
}
|
|
|
|
|
|
|
|
// only one row will be returned
|
|
|
|
$data = $result->fetch_assoc();
|
|
|
|
$key = $data['apikey'];
|
|
|
|
$thisUserId = $data["userid"];
|
|
|
|
|
|
|
|
// check signature
|
|
|
|
$signedHash = hash_hmac("sha256", $thisData, $key);
|
|
|
|
|
|
|
|
if ($signedHash !== strtolower($sensorData['signature'])) {
|
|
|
|
return array("status"=>401);
|
|
|
|
}
|
|
|
|
|
|
|
|
// signature is valid
|
|
|
|
|
|
|
|
$measvalues = json_decode($thisData, true);
|
|
|
|
|
|
|
|
if (!$measvalues) {
|
|
|
|
// the format of the data JSON is invalid
|
|
|
|
return array("status"=>901, "data"=>"malformed data JSON");
|
|
|
|
}
|
|
|
|
|
|
|
|
$allSensors = array();
|
|
|
|
$validSensors = array();
|
|
|
|
$ignoredSensors = array();
|
|
|
|
|
|
|
|
// find all sensors
|
|
|
|
foreach ($measvalues as $measvalue) {
|
|
|
|
$thisSensorId = $this->_con->real_escape_string($measvalue[0]);
|
|
|
|
if (!in_array($thisSensorId, $allSensors)) {
|
|
|
|
array_push($allSensors, $thisSensorId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if all sensors are valid and belong to the user
|
|
|
|
foreach ($allSensors as $sensor) {
|
|
|
|
// check if sensor belongs to user
|
|
|
|
$sql = "SELECT userid FROM `sensors` WHERE id=\"$sensor\"";
|
|
|
|
|
|
|
|
$result = $this->_con->query($sql);
|
|
|
|
|
|
|
|
if ($result->num_rows < 1) {
|
|
|
|
// sensor was not found
|
|
|
|
array_push($ignoredSensors, array($sensor, 404));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only one row will be returned
|
|
|
|
$data = $result->fetch_assoc();
|
|
|
|
|
|
|
|
if ($data['userid'] !== $thisUserId) {
|
|
|
|
// sensor does not belong to user
|
|
|
|
array_push($ignoredSensors, array($sensor, 401));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
array_push($validSensors, $sensor);
|
|
|
|
}
|
|
|
|
|
|
|
|
// iterate through all measvalues
|
|
|
|
// and insert them into the database if the sensor is valid
|
|
|
|
foreach ($measvalues as $measvalue) {
|
|
|
|
if (!in_array($measvalue[0], $validSensors)) {
|
|
|
|
// if the sensor is not valid -> continue
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$thisSensorId = $this->_con->real_escape_string($measvalue[0]);
|
|
|
|
$thisMeasvalue = $this->_con->real_escape_string($measvalue[1]);
|
|
|
|
$thisTimestamp = $this->_con->real_escape_string($measvalue[2]);
|
|
|
|
|
|
|
|
$sql = "INSERT INTO `measvalues` (sensorid,measvalue,timestamp)
|
|
|
|
VALUES (\"$thisSensorId\", \"$thisMeasvalue\", \"$thisTimestamp\")";
|
|
|
|
|
|
|
|
$this->_con->query($sql);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($ignoredSensors) > 0) {
|
|
|
|
return array("status"=>901, "data"=>$ignoredSensors);
|
|
|
|
}
|
|
|
|
return array("status"=>200);
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------
|
|
|
|
// - Helper functions -
|
|
|
|
// --------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to convert a string to hex
|
|
|
|
*
|
|
|
|
* @param string $string the string to be converted
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
function strToHex($string)
|
|
|
|
{
|
|
|
|
$hex = '';
|
|
|
|
for ($i=0; $i<strlen($string); $i++) {
|
|
|
|
$ord = ord($string[$i]);
|
|
|
|
$hexCode = dechex($ord);
|
|
|
|
$hex .= substr('0'.$hexCode, -2);
|
|
|
|
}
|
|
|
|
return strToUpper($hex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to convert hex to string
|
|
|
|
*
|
|
|
|
* @param string $hex hex to be converted
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
function hexToStr($hex)
|
|
|
|
{
|
|
|
|
$string='';
|
|
|
|
for ($i=0; $i < strlen($hex)-1; $i+=2) {
|
|
|
|
$string .= chr(hexdec($hex[$i].$hex[$i+1]));
|
|
|
|
}
|
|
|
|
return $string;
|
|
|
|
}
|
2019-07-15 20:29:19 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
?>
|