PageRenderTime 46ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/vihu.php

https://bitbucket.org/monstroestudio/vihu-keyboard
PHP | 404 lines | 299 code | 85 blank | 20 comment | 73 complexity | 0e441769453b812af849b734be431eea MD5 | raw file
  1. <?php
  2. //RETIRA WARNINGS E NOTICE
  3. error_reporting(E_ERROR);
  4. //CONSTANTES
  5. const PATH = '/home/vihu/php/';
  6. //const PATH = './';
  7. const DOMAIN = 'api.vihu.ml';
  8. // const DOMAIN = 'api.vihu.com.br';
  9. const PWD = 'Vihu#Cond@dmin_2018';
  10. const SECRET_KEY = 'U2VjcmV0S2V5T2ZWaWh1QDc3Nw=='; //SecretKeyOfVihu@777
  11. const URL = 'https://'.DOMAIN;
  12. const TOKEN = PATH . 'vihu-token.key';
  13. const ACCESS = PATH . 'vihu-access.json';
  14. const LOG = PATH . 'vihu-log.txt';
  15. const LOG_ACCESS = PATH . 'vihu-log-access.txt';
  16. const DEBUG = true;
  17. const RESULTS = [
  18. "DELIVERY" => ["type"=>"DELIVERY", "audio"=>"/home/vihu/libera_portao.wav", "open"=>false, "call"=>true],
  19. "ACESSO_VISITANTE" => ["type"=>"ACESSO_VISITANTE", "audio"=>"/home/vihu/audio/Entrada_autorizada.wav", "open"=>true, "call"=>true],
  20. "ACESSO_LIBERADO" => ["type"=>"ACESSO_LIBERADO", "audio"=>"/home/vihu/audio/libera_portao.wav", "open"=>true, "call"=>false],
  21. "ACESSO_INVALIDO" => ["type"=>"ACESSO_INVALIDO", "audio"=>"/home/vihu/audio/Entrada_nao_autorizada.wav", "open"=>false, "call"=>false],
  22. "ACESSO_MORADOR_RFID" => ["type"=>"ACESSO_MORADOR_RFID", "audio"=>"/home/vihu/audio/libera_portao.wav", "open"=>true, "call"=>false],
  23. "ACESSO_GARAGEM_RFID" => ["type"=>"ACESSO_GARAGEM_RFID", "audio"=>"/home/vihu/audio/libera_portao.wav", "open"=>true, "call"=>false]
  24. ];
  25. function debug($value) {
  26. if (!DEBUG) return;
  27. if (!file_exists(LOG)) {
  28. $handle = fopen(LOG,'w+');
  29. fwrite($handle, '');
  30. fclose($handle);
  31. }
  32. file_put_contents(LOG, date("c") . " - " . $value . "\n", FILE_APPEND);
  33. }
  34. function logAccess($value) {
  35. $data = [];
  36. if (!file_exists(LOG_ACCESS)) {
  37. $handle = fopen(LOG_ACCESS,'w+');
  38. fwrite($handle, '');
  39. fclose($handle);
  40. } else {
  41. $data = json_decode(file_get_contents(LOG_ACCESS));
  42. if (count($data) == 0) {
  43. $data = [];
  44. }
  45. }
  46. array_push($data, $value);
  47. file_put_contents(LOG_ACCESS, json_encode($data));
  48. }
  49. function isConnected() {
  50. debug('CONNECTION: INIT CHECK');
  51. $connected = @fsockopen(DOMAIN, 80); //website, port (try 80 or 443)
  52. if ($connected){
  53. $is_conn = true; //action when connected
  54. fclose($connected);
  55. } else {
  56. $is_conn = false; //action in connection failure
  57. }
  58. debug('CONNECTION: ' . $is_conn);
  59. return boolval($is_conn);
  60. }
  61. function callAPI($method, $url, $data = false) {
  62. $curl = curl_init();
  63. switch ($method) {
  64. case "POST":
  65. curl_setopt($curl, CURLOPT_POST, 1);
  66. if ($data)
  67. curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  68. break;
  69. case "PUT":
  70. curl_setopt($curl, CURLOPT_PUT, 1);
  71. break;
  72. default:
  73. if ($data)
  74. $url = sprintf("%s?%s", $url, http_build_query($data));
  75. }
  76. if (file_exists(TOKEN)) {
  77. $authorization = "Authorization: Bearer ".file_get_contents(TOKEN);
  78. curl_setopt($curl, CURLOPT_HTTPHEADER, [$authorization]);
  79. }
  80. curl_setopt($curl, CURLOPT_URL, $url);
  81. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  82. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
  83. $result = curl_exec($curl);
  84. curl_close($curl);
  85. return $result;
  86. }
  87. function monstroEncrypt($pwd) {
  88. // Remove the base64 encoding from our key
  89. $encryption_key = base64_decode(SECRET_KEY);
  90. // Generate an initialization vector
  91. $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
  92. // Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
  93. $encrypted = openssl_encrypt($pwd, 'aes-256-cbc', $encryption_key, 0, $iv);
  94. // The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
  95. return base64_encode($encrypted . '::' . $iv);
  96. }
  97. function monstroDecrypt($pwd) {
  98. // Remove the base64 encoding from our key
  99. $encryption_key = base64_decode(SECRET_KEY);
  100. // To decrypt, split the encrypted data from our IV - our unique separator used was "::"
  101. list($encrypted_data, $iv) = explode('::', base64_decode($pwd), 2);
  102. return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
  103. }
  104. function syncDown() {
  105. $accesses = json_decode(callAPI('GET', URL . '/api/access/v2/sync-down'));
  106. $localData = json_decode(file_get_contents(ACCESS));
  107. if (!$localData->locked) {
  108. $localData = $accesses;
  109. updateLocalData(json_encode($localData));
  110. }
  111. }
  112. function syncUp() {
  113. $accessesToSync = [];
  114. $localData = json_decode(file_get_contents(ACCESS));
  115. foreach ( $localData->data->visitantes as $visitante){
  116. if ($visitante->sync) {
  117. array_push($accessesToSync, $visitante);
  118. }
  119. }
  120. if (file_exists(LOG_ACCESS)) {
  121. $logAccess = json_decode(file_get_contents(LOG_ACCESS));
  122. unlink(LOG_ACCESS);
  123. }
  124. $result = callAPI('POST', URL . '/api/access/v2/sync-up', json_encode(["accesses" => $accessesToSync, "logAccess" => $logAccess]));
  125. debug($result);
  126. }
  127. function updateLocalData($data) {
  128. $handle = fopen(ACCESS,'w+');
  129. fwrite($handle, $data);
  130. fclose($handle);
  131. }
  132. function validateResident($_code, $_keyboard) {
  133. debug('ACCESS (resident): ' . $_code . ' | ' . $_keyboard);
  134. $localData = json_decode(file_get_contents(ACCESS));
  135. $resultAccess = [];
  136. if (!$localData->data || $_code == '123456') {
  137. $resultAccess["type"] = "ACESSO_INVALIDO";
  138. return $resultAccess;
  139. }
  140. //VERIFICA SE É CHAVEIRO DE MORADOR
  141. if (strlen($_code) > 6) {
  142. foreach ($localData->data->moradores as $morador) {
  143. foreach ($morador->rfid_keys as $rfid_key) {
  144. if ($_code == $rfid_key->code) {
  145. logAccess([
  146. 'user_id' => $morador->id,
  147. 'condominiums_id' => $localData->data->condominio->id,
  148. 'keyboard' => $_keyboard,
  149. 'type' => 2,
  150. 'ip' => getHostByName(getHostName())
  151. ]);
  152. $resultAccess["type"] = "ACESSO_MORADOR_RFID";
  153. return $resultAccess;
  154. }
  155. }
  156. }
  157. }
  158. //VERIFICA SE O CODIGO EXISTE PARA ALGUM MORADOR
  159. $_codeEncrypted = sha1($_code);
  160. debug($_codeEncrypted);
  161. foreach ($localData->data->moradores as $morador) {
  162. if ($_codeEncrypted == $morador->access_code) {
  163. logAccess([
  164. 'user_id' => $morador->id,
  165. 'condominiums_id' => $localData->data->condominio->id,
  166. 'keyboard' => $_keyboard,
  167. 'type' => 1,
  168. 'ip' => getHostByName(getHostName())
  169. ]);
  170. $resultAccess["type"] = "ACESSO_LIBERADO";
  171. return $resultAccess;
  172. }
  173. }
  174. $resultAccess["type"] = "ACESSO_INVALIDO";
  175. return $resultAccess;
  176. }
  177. function validateAccessLocal($_code, $_keyboard) {
  178. debug('ACCESS (offline): ' . $_code . ' | ' . $_keyboard);
  179. $localData = json_decode(file_get_contents(ACCESS));
  180. $resultAccess = [];
  181. if (!$localData->data || $_code == '123456') {
  182. $resultAccess["type"] = "ACESSO_INVALIDO";
  183. return $resultAccess;
  184. }
  185. //VERIFICA SE O CODIGO EXISTE PARA ALGUM VISITANTE
  186. foreach ($localData->data->visitantes as $visitante) {
  187. if ($visitante->code == $_code && validateDeadline($visitante)) {
  188. if ($visitante->status != "ativo") {
  189. $resultAccess["type"] = "ACESSO_INVALIDO";
  190. return $resultAccess;
  191. }
  192. $__keyboard = ($visitante->gates == 'null') ? NULL : $visitante->gates;
  193. foreach ($__keyboard as $k) {
  194. if ($k->gate == $_keyboard) {
  195. $resultAccess["type"] = "ACESSO_INVALIDO";
  196. return $resultAccess;
  197. }
  198. }
  199. //PEGA DADOS DO APARTAMENTO DO MORADOR QUE ENVIOU O ACESSO
  200. foreach ($localData->data->moradores as $morador) {
  201. if ($morador->id == $visitante->users_id) {
  202. $resultAccess["ramal"] = $morador->units[0]->extension_number;
  203. $resultAccess["call"] = $localData->data->condominio->communication_type->intercom;
  204. }
  205. }
  206. $newKeyboard = [
  207. 'gate' => $_keyboard,
  208. 'datetime' => date("Y-m-d H:i:s")
  209. ];
  210. if ($visitante->type != 'delivery') {
  211. if ($__keyboard) {
  212. array_push($__keyboard, $newKeyboard);
  213. $visitante->gates = $__keyboard;
  214. } else {
  215. $visitante->gates = [$newKeyboard];
  216. }
  217. $visitante->validations = $visitante->validations + 1;
  218. $visitante->sync = true;
  219. if (($localData->data->condominio->number_of_gates == $visitante->validations) || ($_keyboard == $localData->data->condominio->number_of_gates) || ($visitante->exit)) {
  220. $visitante->status = 2;
  221. }
  222. //ATUALIZA ARQUIVO
  223. updateLocalData(json_encode($localData));
  224. //VEIRIFICA SE É A PRIMEIRA VALIDAÇÃO PARA AVISAR O MORADOR
  225. $resultAccess["type"] = ($visitante->validations == 1) ? "ACESSO_VISITANTE" : "ACESSO_LIBERADO";
  226. return $resultAccess;
  227. } else {
  228. //CASO FOR DELIVERY, VERIFICA SE CONDOMINIO TEM INTERFONE
  229. if (!$resultAccess["call"]) {
  230. $resultAccess["type"] = "ACESSO_INVALIDO";
  231. return $resultAccess;
  232. }
  233. $visitante->gates = [$newKeyboard];
  234. $visitante->validations = $visitante->validations + 1;
  235. $visitante->sync = true;
  236. $visitante->status = 2;
  237. //ATUALIZA ARQUIVO
  238. updateLocalData(json_encode($localData));
  239. $resultAccess["type"] = "DELIVERY";
  240. return $resultAccess;
  241. }
  242. }
  243. }
  244. $resultAccess["type"] = "ACESSO_INVALIDO";
  245. return $resultAccess;
  246. }
  247. function validateDeadline($access) {
  248. $before = new DateTime($access->date.' '. $access->time, new DateTimeZone('America/Sao_Paulo'));
  249. $before->modify('-30 minutes');
  250. $after = new DateTime($access->date.' '. $access->time, new DateTimeZone('America/Sao_Paulo'));
  251. $after->modify('+3 hours');
  252. $now = new DateTime();
  253. $now = $now->setTimezone(new DateTimeZone('America/Sao_Paulo'));
  254. if ($now >= $before && $now <= $after) {
  255. return true;
  256. }
  257. return false;
  258. }
  259. function getToken() {
  260. $pwd = monstroEncrypt(PWD);
  261. $result = json_decode(callAPI('POST', URL . '/api/vihubox/install', ['password'=>$pwd]));
  262. if ($result->status == 'SUCCESS') {
  263. $handle = fopen(TOKEN,'w+');
  264. fwrite($handle, $result->token);
  265. fclose($handle);
  266. return true;
  267. }
  268. return $result;
  269. }
  270. //INICIO
  271. $code = null;
  272. $keyboard = null;
  273. $result = null;
  274. debug('-------------');
  275. debug('INIT: ' . $argv[1]);
  276. if (!file_exists(ACCESS)) {
  277. updateLocalData(json_encode([]));
  278. }
  279. if ($argv[1] == 'INSTALL') {
  280. $result = getToken();
  281. if ($result === true) {
  282. syncDown();
  283. echo "SUCCESS";
  284. } else {
  285. echo $result;
  286. }
  287. } else if ($argv[1] == 'ACCESS') {
  288. $code = ($argv[2]) ? $argv[2] : null;
  289. $keyboard = ($argv[3]) ? $argv[3] : null;
  290. debug('ACCESS: ' . $code . ' | ' . $keyboard);
  291. $result = validateResident($code, $keyboard);
  292. if (isConnected() == true && strlen($code) == 6 && $result['type'] == 'ACESSO_INVALIDO') {
  293. debug('ACCESS: Call API');
  294. $result = callAPI('GET', URL . '/api/access/v2/'.$code.'/'.$keyboard);
  295. debug('ACCESS (result): ' . $result);
  296. $result = base64_decode($result);
  297. $result = json_decode($result, true);
  298. } else if ($result['type'] == 'ACESSO_INVALIDO') {
  299. debug('ACCESS: Offline');
  300. $result = validateAccessLocal($code, $keyboard);
  301. }
  302. //FORMATA RESULTADO PARA ENVIAR AO VIHUBOX
  303. try {
  304. debug('ACCESS (result): ' . json_encode($result));
  305. $print = new ArrayObject(RESULTS[$result["type"]]);
  306. } catch(Exception $e) {
  307. debug('ACCESS (catch): ' . $e->getMessage());
  308. $print = new ArrayObject(RESULTS["ACESSO_INVALIDO"]);
  309. }
  310. $print["ramal"] = $result["ramal"];
  311. $print["call"] = (!$result["call"]) ? false : $print["call"];
  312. print_r(json_encode($print));
  313. } else if ($argv[1] == 'SYNC') {
  314. if (isConnected()) {
  315. syncUp();
  316. syncDown();
  317. }
  318. }
  319. ?>