d_validator.php (18128B)
1 <?php 2 3 class ControllerextensionDShopunityDValidator extends Controller 4 { 5 protected $pack; 6 protected $error; 7 protected $expire; 8 protected $module; 9 protected $validator; 10 protected $d_validator; 11 protected $module_name; 12 protected $validate_error; 13 protected $module_codename; 14 protected $d_opencart_patch; 15 protected $codename = 'd_validator'; 16 protected $route = 'extension/d_shopunity/d_validator'; 17 18 public function __construct($registry) 19 { 20 parent::__construct($registry); 21 22 $this->load->language($this->route); 23 $this->d_validator = (file_exists(DIR_SYSTEM . 'library/d_shopunity/extension/d_validator.json')); 24 $this->d_opencart_patch = (file_exists(DIR_SYSTEM . 'library/d_shopunity/extension/d_opencart_patch.json')); 25 } 26 27 public function index() 28 { 29 $this->installCompatibility(); 30 } 31 32 public function installCompatibility() 33 { 34 $this->load->model('extension/d_shopunity/d_validator'); 35 $this->model_extension_d_shopunity_d_validator->installModule(); 36 } 37 38 public function form() 39 { 40 $json = array(); 41 42 if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { 43 $this->load->language($this->route); 44 45 $market = $this->request->post['market']; 46 $email = $this->request->post['email']; 47 $order_id = $this->request->post['order_id']; 48 $codename = trim($this->request->post['codename']); 49 $result = json_decode($this->request($market, $email, $order_id, $codename)); 50 51 if (isset($result->success) && isset($result->licence)) { 52 $key = $this->strToHex($codename); 53 54 $encrypted = base64_decode($result->licence); 55 $decrypted = SaferCrypto::decrypt($encrypted, $key); 56 57 if ($decrypted) { 58 $this->saveLicenceToFile($codename, $result->licence); 59 60 if($this->validateLicense($decrypted, $codename)){ 61 $json['success'] = $this->language->get('license_is_valid'); 62 }else{ 63 $json['warning'] = $this->validate_error; 64 } 65 66 }else{ 67 $json['warning'] = $this->language->get('license_is_expired'); 68 } 69 } else { 70 if (isset($result->error)) { 71 $json['warning'] = $result->error; 72 } 73 } 74 }else{ 75 $json['error'] = $this->error; 76 } 77 78 $this->response->addHeader('Content-Type: application/json'); 79 $this->response->setOutput(json_encode($json)); 80 } 81 82 public 83 function validate() 84 { 85 $fields = [ 86 'market', 87 'email', 88 'order_id', 89 'codename' 90 ]; 91 92 foreach ($fields as $field) { 93 if (!$this->request->post[$field]) { 94 $this->error[$field] = $this->language->get('field_require'); 95 } 96 } 97 98 return !$this->error; 99 } 100 101 function strToHex($string) 102 { 103 $hex = ''; 104 for ($i = 0; $i < strlen($string); $i++) { 105 $hex .= dechex(ord($string[$i])); 106 } 107 return $hex; 108 } 109 110 public 111 function view(&$route, &$args, &$output) 112 { 113 $this->load->language($this->route); 114 $this->load->model('extension/d_shopunity/d_validator'); 115 $this->model_extension_d_shopunity_d_validator->createTable(); 116 117 $this->expire = false; 118 119 $url_parts = explode("/", $route); 120 121 foreach ($url_parts as $url_part) { 122 if (strpos($url_part, 'd_') !== false) { 123 $this->module = trim($url_part); 124 } 125 } 126 127 if ($this->module && $this->validationFileJsonCommercialByRoute($route)) { 128 $licence = $this->getLicenceByFile(); 129 130 if ($licence) { 131 $key = $this->strToHex($this->module_codename); 132 133 $encrypted = base64_decode($licence); 134 $decrypted = SaferCrypto::decrypt($encrypted, $key); 135 136 if ($decrypted) { 137 138 if(!$this->validateLicense($decrypted, $this->module_codename)){ 139 $this->decideShowOrNot(); 140 } 141 142 }else{ 143 $this->decideShowOrNot(); 144 } 145 146 } else { 147 $this->decideShowOrNot(); 148 } 149 } 150 151 if ($this->expire) { 152 153 $html = $this->expire; 154 155 $html_dom = new d_simple_html_dom(); 156 157 $html_dom->load((string)$output, $lowercase = true, $stripRN = false, $defaultBRText = DEFAULT_BR_TEXT); 158 159 $findSelector = $html_dom->find('#content>.page-header', 0); 160 161 if ($findSelector) { 162 $html_dom->find('#content>.page-header', 0)->outertext .= $html; 163 } 164 165 $output = (string)$html_dom; 166 } 167 } 168 169 public function validationFileJsonCommercialByRoute($route) 170 { 171 $this->load->config($this->codename); 172 $modules = $this->config->get($this->codename . '_modules'); 173 174 foreach ($modules as $module) { 175 if (isset($module['route']) && $module['route'] == $route) { 176 177 if (is_array($module['json'])) { 178 foreach ($module['json'] as $key => $json) { 179 $filePath = DIR_SYSTEM . "library/d_shopunity/extension/" . $json; 180 181 if (false !== ($contents = @file_get_contents($filePath, true))) { 182 $file = json_decode($contents); 183 } 184 185 if (isset($file) && isset($file->license->type) && $file->license->type == 'commercial') { 186 $this->module_codename = $module['codename'][$key]; 187 $this->module_name = $file->name; 188 189 return true; 190 } 191 } 192 } 193 194 $filePath = DIR_SYSTEM . "library/d_shopunity/extension/" . $module['json']; 195 196 if (false !== ($contents = @file_get_contents($filePath, true))) { 197 $file = json_decode($contents); 198 } 199 200 if (isset($file) && isset($file->license->type) && $file->license->type == 'commercial') { 201 $this->module_codename = $module['codename']; 202 $this->module_name = $file->name; 203 204 return true; 205 } 206 } 207 } 208 209 return false; 210 } 211 212 public function getLicenceByFile() 213 { 214 $filePath = DIR_SYSTEM . "library/d_shopunity/license/" . $this->module_codename . ".txt"; 215 $fileExists = (file_exists($filePath)); 216 217 if ($fileExists) { 218 $content = file_get_contents($filePath); 219 220 if ($content) { 221 return $content; 222 } 223 } 224 225 return false; 226 } 227 228 public function validateLicense($decoded_licence, $codename) 229 { 230 $this->load->language($this->route); 231 232 $data = json_decode($decoded_licence); 233 234 $url = $this->getSiteURl(); 235 236 if ($data && isset($data->url) && isset($data->codename) && isset($data->date_expired)) { 237 if ($data->url != $url){ 238 $this->validate_error = $this->language->get('license_error_url'); 239 } 240 241 if ($data->codename != $codename) { 242 $this->validate_error = $this->language->get('license_error_codename'); 243 } 244 245 if ($this->isExpire($data->date_expired)){ 246 $this->validate_error = $this->language->get('license_is_expired'); 247 } 248 249 }else{ 250 $this->validate_error = $this->language->get('license_error'); 251 } 252 253 return !$this->validate_error; 254 } 255 256 public function saveLicenceToFile($codename, $license) 257 { 258 $fonderPath = DIR_SYSTEM . "library/d_shopunity/license"; 259 $fileExists = (file_exists($fonderPath)); 260 261 if (!$fileExists) { 262 $tags = explode('/', $fonderPath); 263 $mkDir = ""; 264 265 foreach ($tags as $folder) { 266 $mkDir = $mkDir . $folder . "/"; 267 if (!is_dir($mkDir)) { 268 mkdir($mkDir, 0777); 269 } 270 } 271 } 272 273 $filePath = DIR_SYSTEM . "library/d_shopunity/license/" . $codename . ".txt"; 274 return file_put_contents($filePath, $license, LOCK_EX); 275 } 276 277 public function decideShowOrNot() 278 { 279 $this->load->model('extension/d_shopunity/d_validator'); 280 $result = $this->model_extension_d_shopunity_d_validator->getTimeByCodename($this->module_codename); 281 282 if ($result) { 283 if ($this->isShow($result['date_show'])) { 284 $view = $this->generateExpireView(); 285 286 $this->expire = $view; 287 } 288 } else { 289 $view = $this->generateExpireView(); 290 291 $this->expire = $view; 292 } 293 } 294 295 public 296 function generateExpireView() 297 { 298 $data = array(); 299 300 $data['module_name'] = $this->module_name; 301 $data['codename'] = $this->module_codename; 302 303 $data['text'] = sprintf($this->language->get('license_expired'), $data['module_name']); 304 $data['help'] = $this->language->get('license_visit_text'); 305 306 $this->load->model('extension/d_opencart_patch/url'); 307 $data['action'] = $this->model_extension_d_opencart_patch_url->ajax($this->route . '/form'); 308 $data['action_remind'] = $this->model_extension_d_opencart_patch_url->ajax($this->route . '/remind'); 309 310 $this->load->model('extension/d_opencart_patch/load'); 311 $view = $this->model_extension_d_opencart_patch_load->view('extension/d_shopunity/d_validator', $data); 312 313 $this->expire = $view; 314 315 return $view; 316 } 317 318 public function remind() 319 { 320 $json = array(); 321 322 if (($this->request->server['REQUEST_METHOD'] == 'POST')) { 323 $module = $this->request->post['module']; 324 325 $data = array(); 326 $data['codename'] = $this->request->post['module']; 327 $data['date_created'] = date("Y-m-d H:i:s"); 328 $data['date_show'] = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s") . " + 7 day")); 329 330 $this->load->model('extension/d_shopunity/d_validator'); 331 $result = $this->model_extension_d_shopunity_d_validator->getTimeByCodename($module); 332 333 if ($result) { 334 $result = $this->model_extension_d_shopunity_d_validator->updateTimeByCodename($module, $data); 335 } else { 336 $result = $this->model_extension_d_shopunity_d_validator->insertTime($data); 337 } 338 339 $json['success'] = true; 340 } 341 342 $this->response->addHeader('Content-Type: application/json'); 343 $this->response->setOutput(json_encode($json)); 344 } 345 346 public function request($market, $email, $order_id, $codename) 347 { 348 $url = $this->getSiteURl(); 349 350 $data = array( 351 'codename' => $codename, 352 'market' => $market, 353 'order_id' => $order_id, 354 'email' => $email, 355 'url' => $url 356 ); 357 358 $payload = json_encode($data); 359 360 $this->load->config($this->codename); 361 $setting = $this->config->get($this->codename . '_setting'); 362 363 if (isset($this->request->post['validateShopunity'])){ 364 $url = $setting['api_url_shopunity']; 365 }else{ 366 $url = $setting['api_url']; 367 } 368 369 $ch = curl_init($url); 370 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 371 curl_setopt($ch, CURLINFO_HEADER_OUT, true); 372 curl_setopt($ch, CURLOPT_POST, true); 373 curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); 374 375 curl_setopt($ch, CURLOPT_HTTPHEADER, array( 376 'Content-Type: application/json', 377 'Content-Length: ' . strlen($payload)) 378 ); 379 380 $result = curl_exec($ch); 381 curl_close($ch); 382 383 return $result; 384 } 385 386 public 387 function isExpire($date_expire) 388 { 389 $now = new DateTime(); 390 $expire = new DateTime($date_expire); 391 392 if ($expire > $now) { 393 return 0; 394 } else { 395 return 1; 396 } 397 } 398 399 public 400 function isShow($date_show) 401 { 402 $now = new DateTime(); 403 $expire = new DateTime($date_show); 404 405 if ($now > $expire) { 406 return 1; 407 } else { 408 return 0; 409 } 410 } 411 412 public function getSiteURl() 413 { 414 $url = null; 415 416 if (defined('HTTPS_CATALOG')) { 417 $url = HTTPS_CATALOG; 418 } elseif (defined('HTTP_CATALOG')) { 419 $url = HTTP_CATALOG; 420 } else { 421 $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 422 "https" : "http") . "://" . $_SERVER['HTTP_HOST']; 423 } 424 425 return $url; 426 } 427 } 428 429 430 class UnsafeCrypto 431 { 432 const METHOD = 'aes-256-ctr'; 433 434 /** 435 * Encrypts (but does not authenticate) a message 436 * 437 * @param string $message - plaintext message 438 * @param string $key - encryption key (raw binary expected) 439 * @param boolean $encode - set to TRUE to return a base64-encoded 440 * @return string (raw binary) 441 */ 442 public static function encrypt($message, $key, $encode = false) 443 { 444 $nonceSize = openssl_cipher_iv_length(self::METHOD); 445 $nonce = openssl_random_pseudo_bytes($nonceSize); 446 447 $ciphertext = openssl_encrypt( 448 $message, 449 self::METHOD, 450 $key, 451 OPENSSL_RAW_DATA, 452 $nonce 453 ); 454 455 // Now let pack the IV and the ciphertext together 456 // Naively, we can just concatenate 457 if ($encode) { 458 return base64_encode($nonce . $ciphertext); 459 } 460 return $nonce . $ciphertext; 461 } 462 463 /** 464 * Decrypts (but does not verify) a message 465 * 466 * @param string $message - ciphertext message 467 * @param string $key - encryption key (raw binary expected) 468 * @param boolean $encoded - are we expecting an encoded string? 469 * @return string 470 */ 471 public static function decrypt($message, $key, $encoded = false) 472 { 473 if ($encoded) { 474 $message = base64_decode($message, true); 475 if ($message === false) { 476 throw new Exception('Encryption failure'); 477 } 478 } 479 480 $nonceSize = openssl_cipher_iv_length(self::METHOD); 481 $nonce = mb_substr($message, 0, $nonceSize, '8bit'); 482 $ciphertext = mb_substr($message, $nonceSize, null, '8bit'); 483 484 $plaintext = openssl_decrypt( 485 $ciphertext, 486 self::METHOD, 487 $key, 488 OPENSSL_RAW_DATA, 489 $nonce 490 ); 491 492 return $plaintext; 493 } 494 } 495 496 class SaferCrypto extends UnsafeCrypto 497 { 498 const HASH_ALGO = 'sha256'; 499 500 /** 501 * Encrypts then MACs a message 502 * 503 * @param string $message - plaintext message 504 * @param string $key - encryption key (raw binary expected) 505 * @param boolean $encode - set to TRUE to return a base64-encoded string 506 * @return string (raw binary) 507 */ 508 public static function encrypt($message, $key, $encode = false) 509 { 510 list($encKey, $authKey) = self::splitKeys($key); 511 512 // Pass to UnsafeCrypto::encrypt 513 $ciphertext = parent::encrypt($message, $encKey); 514 515 // Calculate a MAC of the IV and ciphertext 516 $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true); 517 518 if ($encode) { 519 return base64_encode($mac . $ciphertext); 520 } 521 // Prepend MAC to the ciphertext and return to caller 522 return $mac . $ciphertext; 523 } 524 525 /** 526 * Decrypts a message (after verifying integrity) 527 * 528 * @param string $message - ciphertext message 529 * @param string $key - encryption key (raw binary expected) 530 * @param boolean $encoded - are we expecting an encoded string? 531 * @return string (raw binary) 532 */ 533 public static function decrypt($message, $key, $encoded = false) 534 { 535 list($encKey, $authKey) = self::splitKeys($key); 536 if ($encoded) { 537 $message = base64_decode($message, true); 538 if ($message === false) { 539 throw new Exception('Encryption failure'); 540 } 541 } 542 543 // Hash Size -- in case HASH_ALGO is changed 544 $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit'); 545 $mac = mb_substr($message, 0, $hs, '8bit'); 546 547 $ciphertext = mb_substr($message, $hs, null, '8bit'); 548 549 $calculated = hash_hmac( 550 self::HASH_ALGO, 551 $ciphertext, 552 $authKey, 553 true 554 ); 555 556 if (!self::hashEquals($mac, $calculated)) { 557 throw new Exception('Encryption failure'); 558 } 559 560 // Pass to UnsafeCrypto::decrypt 561 $plaintext = parent::decrypt($ciphertext, $encKey); 562 563 return $plaintext; 564 } 565 566 /** 567 * Splits a key into two separate keys; one for encryption 568 * and the other for authenticaiton 569 * 570 * @param string $masterKey (raw binary) 571 * @return array (two raw binary strings) 572 */ 573 protected static function splitKeys($masterKey) 574 { 575 // You really want to implement HKDF here instead! 576 return [ 577 hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true), 578 hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true) 579 ]; 580 } 581 582 /** 583 * Compare two strings without leaking timing information 584 * 585 * @param string $a 586 * @param string $b 587 * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW 588 * @return boolean 589 */ 590 protected static function hashEquals($a, $b) 591 { 592 if (function_exists('hash_equals')) { 593 return hash_equals($a, $b); 594 } 595 $nonce = openssl_random_pseudo_bytes(32); 596 return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce); 597 } 598 }