daniilak

ir_egrn.lib.php

Oct 2nd, 2021
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 34.07 KB | None | 0 0
  1. <?php
  2. if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
  3.     echo "Этот скрипт не должен выполняться напрямую" . PHP_EOL;
  4.     exit;
  5. }
  6.  
  7. class IR_EGRN{
  8.     protected $rosreestrKey;
  9.     protected $rosreestrInterval;
  10.     protected $anticaptchaKey;
  11.     protected $region;
  12.     protected $anticaptcha;
  13.     protected $captchaMethod;
  14.     protected $python;
  15.     protected $captchaSolver;
  16.     protected $cookieFile;
  17.     protected $counter = 0;
  18.  
  19.     protected function solveCaptcha($content){
  20.         switch($this->captchaMethod){
  21.             case 'captcha_solver':
  22.                 $result = shell_exec($x = strtr(':python :script imgData=:img', [
  23.                     ':python' => escapeshellarg($this->python),
  24.                     ':script' => escapeshellarg($this->captchaSolver),
  25.                     ':img' => escapeshellarg(strtr(base64_encode($content), [
  26.                         '+' => '.',
  27.                         '/' => '_',
  28.                         '=' => '-',
  29.                     ])),
  30.                 ]));
  31.                 $result = preg_replace('#[^0-9]#', '', $result);
  32.                 return $result;
  33.                 break;
  34.             case 'anticaptcha':
  35.                 if(!$this->anticaptcha) {
  36.                     $this->anticaptcha = new ImageToText();
  37.                     $this->anticaptcha->setVerboseMode(true);
  38.                     $this->anticaptcha->setKey($this->anticaptchaKey);
  39.                     $this->anticaptcha->setNumericFlag(true);
  40.                 }
  41.  
  42.                 $this->anticaptcha->setBody($content);
  43.                 if (!$this->anticaptcha->createTask()) {
  44.                     return false;
  45.                 }
  46.  
  47.                 if (!$this->anticaptcha->waitForResult()) {
  48.                     return false;
  49.                 }
  50.  
  51.                 return $this->anticaptcha->getTaskSolution();
  52.                 break;
  53.             default:
  54.                 throw new RuntimeException('Unknown captcha solver method');
  55.                 break;
  56.         }
  57.     }
  58.  
  59.     public function __construct($config){
  60.         // счетчик ошибок
  61.         $this->counter = 1;
  62.         //  ключ к росреестру
  63.         $this->rosreestrKey = $config['rosreestr_key'];
  64.         // антикапча
  65.         $this->anticaptchaKey = $config['anticaptcha_key'];
  66.         // интервал вызова
  67.         $this->rosreestrInterval = $config['rosreestr_interval'];
  68.         // метод капчи
  69.         $this->captchaMethod = $config['captcha_method'];
  70.         // название куки файла
  71.         $this->cookieFile = 'ir_egrn_'.$this->rosreestrKey.'.cookie';
  72.     }
  73.  
  74.     protected $baseURL = 'https://rosreestr.gov.ru';
  75.     protected $portletFullURL;
  76.     protected $appLogin;
  77.     protected $location;
  78.     protected $result;
  79.     protected $ch;
  80.     protected $lastQueryTime = 0;
  81.    
  82.  
  83.     // public $cookieFile = 'ir_egrn.cookie';
  84.  
  85.     public function login(){
  86.         if($this->ch){
  87.             curl_close($this->ch);
  88.         }
  89.         $this->ch = curl_init();
  90.         curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
  91.         curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
  92.         curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
  93.         // $proxy = '127.0.0.1:8888';
  94.         //$proxyauth = 'user:password';
  95.         // curl_setopt($ch, CURLOPT_PROXY, $proxy);
  96.         // curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyauth);   // Use if proxy have username and password
  97.         curl_setopt($this->ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/79.0.3945.79 Chrome/79.0.3945.79 Safari/537.36');
  98.         curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate, gzip');
  99.         curl_setopt($this->ch, CURLOPT_COOKIEFILE, $file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .  'runtime' . DIRECTORY_SEPARATOR . $this->cookieFile);
  100.         curl_setopt($this->ch, CURLOPT_COOKIEJAR, __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .  'runtime' . DIRECTORY_SEPARATOR . $this->cookieFile);
  101.  
  102.         curl_setopt($this->ch, CURLOPT_POST, 0);
  103.         curl_setopt($this->ch, CURLOPT_HEADER, 1);
  104.         curl_setopt($this->ch, CURLOPT_URL, $this->baseURL . '/wps/portal/p/cc_present/ir_egrn');
  105.  
  106.         do {
  107.             $result = curl_exec($this->ch);
  108.             $responseCode = (int)curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
  109.             if ($responseCode >= 500) {
  110.                 var_dump(curl_getinfo($this->ch));
  111.                 var_dump(curl_error($this->ch));
  112.                 // @todo limit retry
  113.                 print "Request failed, error {$responseCode}. Retry 110\n";
  114.             }
  115.             $isError = false;
  116.  
  117.  
  118.             preg_match('#Content-Location: ([^\n\r]+)#u', $result, $location);
  119.             if(isset($location[1])) {
  120.                 $this->location = $location[1];
  121.             }
  122.             else{
  123.                 $isError = true;
  124.             }
  125.  
  126.             /** @noinspection RegExpRedundantEscape */
  127.             preg_match('#vaadin\.vaadinConfigurations\["(.+?)"\]\s*=\s*(.+?\});#', $result, $vaadin);
  128.             if(!isset($vaadin[1])){
  129.                 $isError = true;
  130.             }
  131.  
  132.         } while ($responseCode >= 500 || $isError);
  133.  
  134.         print "Initial request ok 131\n";
  135.  
  136.  
  137.         $appConfig = $vaadin[2];
  138.         $appConfig = str_replace('\'', '"', $appConfig);
  139.         $appConfig = preg_replace('#([a-z]+):#ui', '"\\1":', $appConfig);
  140.         $appConfig = json_decode($appConfig, 1);
  141.  
  142.         try{
  143.             $random = random_int(100, 999);
  144.         }
  145.         catch(Throwable $exception){
  146.             print "Ошибка! Так быть точно не должно..." . PHP_EOL;
  147.             exit;
  148.         }
  149.  
  150.         $repaintOptions = http_build_query([
  151.             'repaintAll' => 1,
  152.             'sh' => 1080, // screen height
  153.             'sw' => 1920, // screen width
  154.             'cw' => 1903, // window width,
  155.             'ch' => 463, // window height
  156.             'vw' => 753, // parent width
  157.             'vh' => 1, // parent height
  158.             'fr' => '',
  159.             'tzo' => -180, // timezone offset,
  160.             'rtzo' => -180, // relative timezone offset
  161.             'dstd' => 0, // daylight
  162.             'dston' => 'false', // daylight
  163.             'curDate' => time() . $random,
  164.             'wsver' => $appConfig['versionInfo']['vaadinVersion'],
  165.         ]);
  166.  
  167.         $locationA = explode('!/',$this->location);
  168.         $this->location = $locationA[0].'!/'.$appConfig['portletUidlURLBase'];
  169.         $this->portletFullURL = $this->baseURL . $this->location;
  170.         $this->appLogin = $this->vaadinQuery($repaintOptions, "init\x1D")[0];
  171.         $this->result = $this->appLogin;
  172.         print "AppInit request ok\n";
  173.  
  174.         /** @noinspection PhpUnusedLocalVariableInspection */
  175.         $isAuthFormAppeared = false;
  176.         try {
  177.             $field = $this->findFocusedField($this->appLogin);
  178.             $isAuthFormAppeared = true;
  179.         } catch (Throwable $exception) {
  180.             // it's ok
  181.             print "Already logged in\n";
  182.             return;
  183.         }
  184.  
  185.         if ($isAuthFormAppeared) {
  186.             $apiKeyLength = strlen($this->rosreestrKey);
  187.             $this->vaadinQuery('windowName=1',
  188.                 "{$this->appLogin['Vaadin-Security-Key']}\x1D{$apiKeyLength}\x1F{$field}\x1Fc\x1Fi\x1E{$this->rosreestrKey}\x1F{$field}\x1FcurText\x1Fs"
  189.             );
  190.  
  191.             print "API key entered\n";
  192.  
  193.             // click the auth button
  194.             $enterButton = $this->findButtonByCaption($this->appLogin, 'Войти');
  195.             $this->result = $this->vaadinClickButton($enterButton);
  196.             if (isset($this->result[0]['changes'][0][2][4][2][1]['style']) && $this->result[0]['changes'][0][2][4][2][1]['style'] === 'error') {
  197.                 throw new RuntimeException('Auth failed. Wrong API key?');
  198.             }
  199.             print "API key ok\n";
  200.         }
  201.     }
  202.  
  203.     protected function findFieldByText(array $data, $text)
  204.     {
  205.         foreach ($data as $k => $v) {
  206.             if ($k === 'v' && isset($v['text']) && $v['text'] == $text) {
  207.                 return $data['id'];
  208.             } elseif (is_array($v)) {
  209.                 try {
  210.                     return $this->findFieldByText($v, $text);
  211.                 } catch (Throwable $exception) {
  212.                     // it's ok, do nothing
  213.                 }
  214.             }
  215.         }
  216.         throw new RuntimeException("Text {$text} not found!");
  217.     }
  218.  
  219.     protected function findFieldBySelectMode(array $data, $mode)
  220.     {
  221.         foreach ($data as $k => $v) {
  222.             if (is_array($v)) {
  223.                 try {
  224.                     return $this->findFieldBySelectMode($v, $mode);
  225.                 } catch (Throwable $exception) {
  226.                     // it's ok, do nothing
  227.                 }
  228.             } elseif ($k === 'selectmode' && $v === $mode) {
  229.                 return $data['id'];
  230.             }
  231.         }
  232.         throw new RuntimeException("Mode {$mode} not found!");
  233.     }
  234.  
  235.     protected function findSrcByIcon(array $data, $icon)
  236.     {
  237.         foreach ($data as $k => $v) {
  238.             if (is_array($v)) {
  239.                 try {
  240.                     return $this->findSrcByIcon($v, $icon);
  241.                 } catch (Throwable $exception) {
  242.                     // it's ok, do nothing
  243.                 }
  244.             } elseif ($k === 'icon' && $v === $icon) {
  245.                 return $data['src'];
  246.             }
  247.         }
  248.         throw new RuntimeException("Src {$icon} not found!");
  249.     }
  250.  
  251.     public function getResult($queryId){
  252.         $this->login();
  253.  
  254.         $searchFormData = $this->vaadinClickButton($this->findButtonByCaption($this->result, 'Запрос по правообладателю'));
  255.  
  256.         try {
  257.             $searchFormData = $this->vaadinClickButton($this->findButtonByCaption($searchFormData, 'Мои заявки'));
  258.         }
  259.         catch(Throwable $exception){
  260.             $searchFormData = $this->vaadinClickButton($this->findButtonByCaption($this->result, 'Мои заявки'));
  261.         }
  262.  
  263.         $fieldQueryInput = $this->findFieldByText($searchFormData, '');
  264.         $buttonRefresh = $this->findButtonByCaption($searchFormData, 'Обновить');
  265.         $areaField = $this->findFieldBySelectMode($searchFormData, 'single');
  266.  
  267.         $queryIdLength = strlen($queryId);
  268.  
  269.         $searchFormData = $this->vaadinQuery('windowName=1',
  270.         "{$this->appLogin['Vaadin-Security-Key']}\x1D1261\x1FPID0\x1Fheight\x1Fi\x1E755\x1FPID0\x1Fwidth\x1Fi\x1E1903\x1FPID0\x1FbrowserWidth\x1Fi\x1E463\x1FPID0\x1FbrowserHeight\x1Fi\x1Etrue\x1F{$areaField}\x1FclearSelections\x1Fb\x1E\x1F{$areaField}\x1Fselected\x1Fc\x1E{$queryId}\x1F{$fieldQueryInput}\x1Ftext\x1Fs\x1E{$queryIdLength}\x1F{$fieldQueryInput}\x1Fc\x1Fi\x1Etrue\x1F{$buttonRefresh}\x1Fstate\x1Fb\x1E1,1176,525,false,false,false,false,1,23,6\x1F{$buttonRefresh}\x1Fmousedetails\x1Fs"
  271.     );
  272.  
  273.         try {
  274.             $locationA = explode('!/',$this->location);
  275.             $this->location = $locationA[0].'!/'.$this->findSrcByIcon($searchFormData, 'theme://img/download.png');
  276.             $zipSrc = $this->baseURL . $this->location;
  277.  
  278.             curl_setopt($this->ch, CURLOPT_URL, $zipSrc);
  279.             curl_setopt($this->ch, CURLOPT_POST, 0);
  280.             curl_setopt($this->ch, CURLOPT_HTTPHEADER, [
  281.                 'Accept: image/webp,image/apng,image/*,*/*;q=0.8',
  282.             ]);
  283.             do {
  284.                 $zipFile = curl_exec($this->ch);
  285.                 $responseCode = (int)curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
  286.                 if ($responseCode >= 500 || empty($zipFile)) {
  287.                     // @todo limit retry
  288.                     print "Request failed, error {$responseCode}. Retry 282\n";
  289.                 }
  290.             } while ($responseCode >= 500 || empty($zipFile));
  291.  
  292.             file_put_contents($fileName = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . $queryId . '.zip', $zipFile);
  293.             return $fileName;
  294.         }
  295.         catch(Throwable $exception){
  296.             return null;
  297.         }
  298.     }
  299.  
  300.     public function parseZipArchive($fileName){
  301.         $zip = new ZipArchive();
  302.         if(!is_readable($fileName)){
  303.             throw new RuntimeException(strtr('File :filename is not readable', [
  304.                 ':filename' => $fileName,
  305.             ]));
  306.         }
  307.         if(!$zip->open($fileName)){
  308.             throw new RuntimeException('Failed to open archive!');
  309.         }
  310.         $isZipFound = false;
  311.         $isDataFound = false;
  312.         $data = '';
  313.         $i = 0;
  314.         while(($name = $zip->getNameIndex($i++)) !== false){
  315.             $ext = pathinfo($name, PATHINFO_EXTENSION);
  316.             if($ext == 'zip'){
  317.                 $isZipFound = true;
  318.                 $data = $zip->getFromIndex($i-1);
  319.                 file_put_contents($fileName . '.tmp', $data);
  320.                 $zip->close();
  321.                 $zip->open($fileName . '.tmp');
  322.                 $j = 0;
  323.                 while(($name = $zip->getNameIndex($j++)) !== false){
  324.                     $ext = pathinfo($name, PATHINFO_EXTENSION);
  325.                     if($ext == 'xml'){
  326.                         $isDataFound = true;
  327.                         $data = $zip->getFromIndex($j-1);
  328.                         $zip->close();
  329.                         break 2;
  330.                     }
  331.                 }
  332.             }
  333.         }
  334.         @unlink($fileName);
  335.         @unlink($fileName . '.tmp');
  336.         if(!$isZipFound || !$isDataFound || !$data){
  337.             throw new RuntimeException('Broken archive!');
  338.         }
  339.  
  340.         file_put_contents($fileName . '.xml', $data);
  341.         return $fileName . '.xml';
  342.     }
  343.  
  344.     /** @noinspection PhpUndefinedFieldInspection */
  345.     public function parseXMLFile($fileName){
  346.         if(!is_readable($fileName)){
  347.             throw new RuntimeException('File ' . $fileName . ' is not readable');
  348.         }
  349.  
  350.         $data = file_get_contents($fileName);
  351.  
  352.         $result = new Ownership();
  353.         $result->xml = $data;
  354.  
  355.         $xml = simplexml_load_string($data);
  356.         $result->cadastralNo = (string)$xml->Realty->Flat['CadastralNumber'];
  357.         $area = (string)$xml->Realty->Flat->Area;
  358.         if($area){
  359.             $area = str_replace(',', '.', $area);
  360.         }
  361.         $result->area = $area;
  362.  
  363.         foreach($xml->ReestrExtract->ExtractObjectRight->ExtractObject->ObjectRight->Right as $right){
  364.             $result->ownership[] = (string)$right->Registration->Name;
  365.             $ownerNames = [];
  366.             foreach($right->Owner as $owner){
  367.                 $name = (string)$owner->Person->Content;
  368.                 if($owner->Organization){
  369.                     $name = (string)$owner->Organization->Content;
  370.                 }
  371.  
  372.                 $ownerNames[] = $name;
  373.             }
  374.             $result->names[] = implode("\n", $ownerNames);
  375.         }
  376.  
  377.         @unlink($fileName);
  378.  
  379.         return $result;
  380.     }
  381.  
  382.     public function createRequest($cadastralNo, $reg_ion, $keyoptiongroup){
  383.         while(1) {
  384.             try {
  385.                 $presleep = $this->rosreestrInterval - 30;
  386.                 //if(time() - $this->lastQueryTime < $presleep){
  387.                 //    $toSleep = $presleep - (time() - $this->lastQueryTime);
  388.                 //    print "Предварительная пауза $toSleep секунд между запросами в Росреестр\n";
  389.                 //    //sleep($toSleep);
  390.                 //}
  391.                 // вообще конечно так быть не должно, но это быстрое решение проблемы обрыва соединения
  392.                 $this->login();
  393.  
  394.                 $searchFormData = $this->vaadinClickButton($this->findButtonByCaption($this->result, 'Запрос по правообладателю'));
  395.  
  396.                 try {
  397.                     $searchFormData = $this->vaadinClickButton($this->findButtonByCaption($searchFormData, 'Поиск объектов недвижимости'));
  398.                 } catch (Throwable $exception) {
  399.                     $searchFormData = $this->vaadinClickButton($this->findButtonByCaption($this->result, 'Поиск объектов недвижимости'));
  400.                 }
  401.  
  402.  
  403.                 // fill search fields
  404.                 $this->vaadinSetFieldText($this->findButtonByPrompt($searchFormData, 'Кадастровый номер'), $cadastralNo);
  405.                 print "Cadastral no ok\n";
  406.  
  407.                 $regionField = $this->findButtonByPrompt($searchFormData, 'Регион');
  408.                 $cityList = $this->vaadinQuery('windowName=1',
  409.                     "{$this->appLogin['Vaadin-Security-Key']}\x1D{$reg_ion}\x1F{$regionField}\x1Ffilter\x1Fs\x1E0\x1F{$regionField}\x1Fpage\x1Fi"
  410.                 );
  411.                 $dropDownKey = $this->findKeyByCaption($cityList, $reg_ion);
  412.                 $this->vaadinQuery('windowName=1',
  413.                     "{$this->appLogin['Vaadin-Security-Key']}\x1D{$dropDownKey}\x1C\x1F{$regionField}\x1Fselected\x1Fc"
  414.                 );
  415.  
  416.                 print "Region ok\n";
  417.  
  418.                 $searchingData = $this->vaadinClickButton($this->findButtonByCaption($searchFormData, 'Найти'));
  419.                 print "Form filled and sent\n";
  420.  
  421.                 $refreshField = $this->findButtonByCaption($searchingData, 'Поиск объектов недвижимости');
  422.                 $targetElement = null;
  423.                
  424.                 do {
  425.                     // @todo limit retry
  426.                     usleep(500000);
  427.                     $result = $this->vaadinQuery('windowName=1',
  428.                         "{$this->appLogin['Vaadin-Security-Key']}\x1D832\x1F{$refreshField}\x1Fpositionx\x1Fi\x1E404\x1F{$refreshField}\x1Fpositiony\x1Fi"
  429.                     );
  430.                     try {
  431.                         $targetElement = $this->findFieldWithEvent($result, 'itemClick');
  432.                         break;
  433.                     } catch (Throwable $exception) {
  434.                         if  ($exception->getMessage() == 'Event itemClick not found!') {
  435.                             $this->counter = 105;
  436.                         } else {
  437.                             var_dump($exception->getMessage() . "433 строчка");
  438.                             $this->counter += 1;
  439.                         }
  440.                         // it's ok: continue polling
  441.                     }
  442.                 } while (1);
  443.  
  444.                 if (!$targetElement) {
  445.                     throw new RuntimeException('Data was not found!');
  446.                 }
  447.                 $result = $this->vaadinQuery('windowName=1',
  448.                     "{$this->appLogin['Vaadin-Security-Key']}\x1D1\x1F{$targetElement}\x1FclickedKey\x1Fs\x1E1\x1F{$targetElement}\x1FclickedColKey\x1Fs\x1E1,572,680,false,false,false,false,8,-1,-1\x1F{$targetElement}\x1FclickEvent\x1Fs\x1Etrue\x1F{$targetElement}\x1FclearSelections\x1Fb\x1E1\x1C\x1F{$targetElement}\x1Fselected\x1Fc"
  449.                 );
  450.                
  451.                 try {
  452.                     $locationA = explode('!/',$this->location);
  453.                     $this->location = $locationA[0].'!/'.$this->findSrcByContentType($result, 'application/octet-stream');
  454.                     $captchaSrc = $this->baseURL . $this->location;
  455.                 } catch (Throwable $exception) {
  456.                     $this->counter = 100;
  457.                 }
  458.                 $keyoptiongroup = intval($keyoptiongroup);
  459.                 $r = $this->vaadinSetKeyText($this->findButton2ByCaption($result, 'optiongroup'), $keyoptiongroup);
  460.                 do {
  461.                     curl_setopt($this->ch, CURLOPT_URL, $captchaSrc);
  462.                     curl_setopt($this->ch, CURLOPT_POST, 0);
  463.                     curl_setopt($this->ch, CURLOPT_HTTPHEADER, [
  464.                         ' image/webp,image/apng,image/*,*/*;q=0.8',
  465.                     ]);
  466.                     do {
  467.                         $captcha = curl_exec($this->ch);
  468.                         $responseCode = (int)curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
  469.                         if ($responseCode >= 500 || empty($captcha)) {
  470.                             // @todo limit retry
  471.                             print "Request failed, error {$responseCode}. Retry 469\n";
  472.                             usleep(500000);
  473.                         }
  474.                     } while ($responseCode >= 500 || empty($captcha));
  475.  
  476.  
  477.                     $solvedCaptcha = $this->solveCaptcha($captcha);
  478.                     if (!$solvedCaptcha) {
  479.                         continue;
  480.                     }
  481.                     $this->vaadinSetFieldText($this->findFieldByStyle($result, 'srv-field'), $solvedCaptcha);
  482.  
  483.                     break;
  484.                 } while (1);
  485.  
  486.                 // print "Финальная пауза $this->rosreestrInterval секунд между запросами в Росреестр\n";
  487.                 // if(time() - $this->lastQueryTime < $this->rosreestrInterval){
  488.                 //     $toSleep = $this->rosreestrInterval - (time() - $this->lastQueryTime);
  489.                 //     print "Финальная пауза $toSleep секунд между запросами в Росреестр\n";
  490.                 //     sleep($toSleep);
  491.                 //     // sleep($toSleep);
  492.                 // }
  493.                 $result = $this->vaadinClickButton($this->findButtonByCaption($result, 'Отправить запрос'));
  494.                 try {
  495.                     $this->findFieldByStyle($result, 'error');
  496.                     throw new RuntimeException('Server error while adding query');
  497.                 } catch (Throwable $exception) {
  498.  
  499.                 }
  500.                 $this->lastQueryTime = time();
  501.                 try {
  502.                     return $this->findRegexp($result, '#<b>(\d+\-\d+)</b>#ui')[1];
  503.                 } catch (Throwable $exception) {
  504.                     $ree = $this->findTextRegexp($result, 'Превышен интервал между запросами');
  505.                     if ($ree != '') {
  506.                     } else {
  507.                         // var_dump("Другая фигня");
  508.                     }
  509.                     return -102;
  510.                 }
  511.             } catch (Throwable $exception) {
  512.                 if ($this->counter == 2) {
  513.                     return -100;
  514.                 }
  515.                 if ($this->counter == 100) {
  516.                     return -101;
  517.                 }
  518.                 if ($this->counter == 105) {
  519.                     return -105;
  520.                 }
  521.                 $this->login();
  522.                 continue;
  523.             }
  524.         }
  525.     }
  526.  
  527.     protected function findRegexp(array $data, $regexp)
  528.     {
  529.         foreach ($data as $k => $v) {
  530.             if (is_array($v)) {
  531.                 try {
  532.                     return $this->findRegexp($v, $regexp);
  533.                 } catch (Throwable $exception) {
  534.                     // it's ok, do nothing
  535.                 }
  536.             } elseif (preg_match($regexp, $v, $matches)) {
  537.                 return $matches;
  538.             }
  539.         }
  540.         throw new RuntimeException("Pattern {$regexp} not found!");
  541.     }
  542.     protected function findTextRegexp(array $data, $text)
  543.     {
  544.         $a =  json_encode($data, JSON_UNESCAPED_UNICODE);
  545.         if (preg_match('/(Превышен интервал между запросами)/', $a , $matches)) {
  546.             return $matches[0];
  547.         } else {
  548.             return "";
  549.         }
  550.     }
  551.  
  552.     protected function findFieldByStyle(array $data, $style)
  553.     {
  554.         foreach ($data as $k => $v) {
  555.             if (is_array($v)) {
  556.                 try {
  557.                     return $this->findFieldByStyle($v, $style);
  558.                 } catch (Throwable $exception) {
  559.                     // it's ok, do nothing
  560.                 }
  561.             } elseif ($k === 'style' && $v === $style) {
  562.                 return $data['id'];
  563.             }
  564.         }
  565.         throw new RuntimeException("Button {$style} not found!");
  566.     }
  567.  
  568.     protected function findSrcByContentType(array $data, $contentType)
  569.     {
  570.         foreach ($data as $k => $v) {
  571.             if (is_array($v)) {
  572.                 try {
  573.                     return $this->findSrcByContentType($v, $contentType);
  574.                 } catch (Throwable $exception) {
  575.                     // it's ok, do nothing
  576.                 }
  577.             } elseif ($k === 'mimetype' && $v === $contentType) {
  578.                 return $data['src'];
  579.             }
  580.         }
  581.         throw new RuntimeException("Src {$contentType} not found!");
  582.     }
  583.  
  584.     protected function findKeyByCaption(array $data, $caption)
  585.     {
  586.         foreach ($data as $k => $v) {
  587.             if (is_array($v)) {
  588.                 try {
  589.                     return $this->findKeyByCaption($v, $caption);
  590.                 } catch (Throwable $exception) {
  591.                     // it's ok, do nothing
  592.                 }
  593.             } elseif ($k === 'caption' && $v === $caption) {
  594.                 return $data['key'];
  595.             }
  596.         }
  597.         throw new RuntimeException("Button {$caption} not found!");
  598.     }
  599.  
  600.     protected function findFieldWithEvent(array $data, $event)
  601.     {
  602.         foreach ($data as $k => $v) {
  603.             if ($k === 'eventListeners' && is_array($v) && in_array($event, $v)) {
  604.                 return $data['id'];
  605.             } elseif (is_array($v)) {
  606.                 try {
  607.                     return $this->findFieldWithEvent($v, $event);
  608.                 } catch (Throwable $exception) {
  609.                     // it's ok, do nothing
  610.                 }
  611.             }
  612.         }
  613.         throw new RuntimeException("Event {$event} not found!");
  614.     }
  615.  
  616.     protected function vaadinSetFieldText($field, $text)
  617.     {
  618.         $textLength = strlen($text);
  619.         $this->vaadinQuery('windowName=1',
  620.             "{$this->appLogin['Vaadin-Security-Key']}\x1D{$text}\x1F{$field}\x1Ftext\x1Fs\x1E{$textLength}\x1F{$field}\x1Fc\x1Fi"
  621.         );
  622.     }
  623.  
  624.     protected function findButtonByPrompt(array $data, $caption)
  625.     {
  626.         foreach ($data as $k => $v) {
  627.             if (is_array($v)) {
  628.                 try {
  629.                     return $this->findButtonByPrompt($v, $caption);
  630.                 } catch (Throwable $exception) {
  631.                     // it's ok, do nothing
  632.                 }
  633.             } elseif ($k === 'prompt' && $v === $caption) {
  634.                 return $data['id'];
  635.             }
  636.         }
  637.         throw new RuntimeException("Button {$caption} not found!");
  638.     }
  639.  
  640.     protected function findFocusedField(array $data)
  641.     {
  642.         foreach ($data as $k => $v) {
  643.             if (is_array($v)) {
  644.                 try {
  645.                     return $this->findFocusedField($v);
  646.                 } catch (Throwable $exception) {
  647.                     // it's ok, do nothing
  648.                 }
  649.             } elseif ($k === 'focused') {
  650.                 return $v;
  651.             }
  652.         }
  653.         throw new RuntimeException('Focused field not found!');
  654.     }
  655.  
  656.  
  657.     protected function vaadinQuery($uri, $data){
  658.         curl_setopt($this->ch, CURLOPT_URL, $url = $this->portletFullURL . '?' . $uri);
  659.         curl_setopt($this->ch, CURLOPT_HEADER, 0);
  660.         curl_setopt($this->ch, CURLOPT_HTTPHEADER, [
  661.             'Content-Type: text/plain;charset=UTF-8',
  662.         ]);
  663.         curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data); // GS, group separator??
  664.         do {
  665.             $result = curl_exec($this->ch);
  666.             $responseCode = (int)curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
  667.             if ($responseCode >= 500) {
  668.                 // @todo limit retry
  669.                 print "Request failed, error {$responseCode}. Retry 677\n";
  670.             }
  671.         } while ($responseCode >= 500);
  672.         $result = str_replace('for(;;);', '', $result);
  673.         $result = json_decode($result, 1);
  674.         if (!is_array($result)) {
  675.             throw new RuntimeException('Failed to execute Vaadin query');
  676.         }
  677.         if (isset($result[0]['meta']['appError'])) {
  678.             throw new RuntimeException($result[0]['meta']['appError']['caption']);
  679.         }
  680.         return $result;
  681.     }
  682.  
  683.     protected function findButtonByCaption(array $data, $caption)
  684.     {
  685.         foreach ($data as $k => $v) {
  686.             if (is_array($v)) {
  687.                 try {
  688.                     return $this->findButtonByCaption($v, $caption);
  689.                 } catch (Throwable $exception) {
  690.                     // it's ok, do nothing
  691.                 }
  692.             } elseif ($k === 'caption' && $v === $caption) {
  693.                 return $data['id'];
  694.             }
  695.         }
  696.         throw new RuntimeException("Button {$caption} not found!");
  697.     }
  698.     protected function findButton2ByCaption(array $data, $caption)
  699.     {
  700.         foreach ($data as $k => $v) {
  701.             if (is_array($v)) {
  702.                 try {
  703.                     return $this->findButton2ByCaption($v, $caption);
  704.                 } catch (Throwable $exception) {
  705.                     // it's ok, do nothing
  706.                 }
  707.             } elseif ($k === 'type' && $v === $caption) {
  708.                 return $data['id'];
  709.             }
  710.         }
  711.         throw new RuntimeException("Button {$caption} not found!");
  712.     }
  713.    
  714.     protected function vaadinSetKeyText($id, $key)
  715.     {
  716.         $this->vaadinQuery('windowName=1',
  717.         "{$this->appLogin['Vaadin-Security-Key']}\x1D{$key}\x1C\x1F{$id}\x1Fselected\x1Fc"
  718.         );
  719.     }
  720.    
  721.  
  722.     protected function vaadinClickButton($field)
  723.     {
  724.         return $this->vaadinQuery('windowName=1',
  725.             "{$this->appLogin['Vaadin-Security-Key']}\x1Dtrue\x1F{$field}\x1Fstate\x1Fb\x1E1,0,0,false,false,false,false,1,30,17\x1F{$field}\x1Fmousedetails\x1Fs"
  726.         );
  727.  
  728.     }
  729.  
  730.     public function getOwnershipAndAreaFree($cadastralNo){
  731.         do {
  732.             $ch = curl_init();
  733.             curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  734.             curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  735.             curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  736.             curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/79.0.3945.79 Chrome/79.0.3945.79 Safari/537.36');
  737.             curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory');
  738.             curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory');
  739.  
  740.             curl_setopt($ch, CURLOPT_POST, 0);
  741.             curl_setopt($ch, CURLOPT_HEADER, 1);
  742.             curl_setopt($ch, CURLOPT_URL, $this->baseURL . '/wps/portal/p/cc_ib_portal_services/online_request');
  743.             $result = curl_exec($ch);
  744.             print "Initial request ok\n";
  745.  
  746.             preg_match('#<img src="([^"]+)" id=\"captchaImage2\">#u', $result, $captcha);
  747.             $captcha = $captcha[1];
  748.             preg_match('#Content-Location: ([^\n\r]+)#u', $result, $location);
  749.             $location = $location[1];
  750.  
  751.             preg_match('#<form action="([^"]+)"#u', $result, $form);
  752.             $form = $form[1];
  753.  
  754.             $captchaURL = $this->baseURL . $location . $captcha . '?refresh=true&time=' . time() . random_int(100, 999);
  755.             $formURL = $this->baseURL . $location . $form;
  756.  
  757.             curl_setopt($ch, CURLOPT_HEADER, 0);
  758.             curl_setopt($ch, CURLOPT_URL, $captchaURL);
  759.  
  760.             $result = curl_exec($ch);
  761.             if(strlen($result) < 100){
  762.                 print 'Wrong captcha (length ' . strlen($result) . ")\n";
  763.                 // wrong captcha
  764.                 curl_close($ch);
  765.                 continue;
  766.             }
  767.             print "Captcha ok\n";
  768.  
  769.             $solvedCaptcha = $this->solveCaptcha($result);
  770.             if(!$solvedCaptcha){
  771.                 curl_close($ch);
  772.                 continue;
  773.             }
  774.  
  775.             curl_setopt($ch, CURLOPT_URL, $formURL);
  776.             curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
  777.                 'search_action' => 'true',
  778.                 'subject' => '',
  779.                 'region' => '',
  780.                 'settlement' => '',
  781.                 'search_type' => 'CAD_NUMBER',
  782.                 'cad_num' => $cadastralNo,
  783.                 'start_position' => '',
  784.                 'obj_num' => '',
  785.                 'old_number' => '',
  786.                 'street_type' => 'str0',
  787.                 'street' => '',
  788.                 'house' => '',
  789.                 'building' => '',
  790.                 'structure' => '',
  791.                 'apartment' => '',
  792.                 'right_reg' => '',
  793.                 'encumbrance_reg' => '',
  794.                 'captchaText' => $solvedCaptcha,
  795.             ]));
  796.             curl_setopt($ch, CURLOPT_HEADER, 1);
  797.             $result = curl_exec($ch);
  798.  
  799.             if(strpos($result, 'Текст с картинки введен неверно') !== false){
  800.                 curl_close($ch);
  801.                 continue;
  802.             }
  803.  
  804.             preg_match('#<a href="([^"]+dbName=firLite&region_key=\d+)">#u', $result, $propertyDataUrl);
  805.             $propertyDataUrl = $propertyDataUrl[1];
  806.             preg_match('#Content-Location: ([^\n\r]+)#u', $result, $location);
  807.             $location = $location[1];
  808.  
  809.             $finalURL = $this->baseURL . $location . $propertyDataUrl;
  810.             curl_setopt($ch, CURLOPT_URL, $finalURL);
  811.             curl_setopt($ch, CURLOPT_POST, 0);
  812.             $result = curl_exec($ch);
  813.  
  814.             curl_close($ch);
  815.  
  816.             preg_match_all("#>([^<]+обственность\)[^<]+)<#mu", $result, $ownershipIds);
  817.             $ownershipIds = $ownershipIds[1];
  818.  
  819.             foreach($ownershipIds as &$ownershipId){
  820.                 $ownershipId = html_entity_decode($ownershipId, null, 'UTF-8');
  821.                 $ownershipId = str_replace(["\n","\r"],' ', $ownershipId);
  822.                 $ownershipId = preg_replace('#\s+#u', ' ', $ownershipId);
  823.                 $ownershipId = trim($ownershipId);
  824.             }
  825.             unset($ownershipId);
  826.  
  827.             preg_match('#Площадь ОКС\'a:.+?<b>([^<]+)</b>#usm', $result, $area);
  828.             if(!count($area)){
  829.                 preg_match('#Площадь:.+?<b>([^<]+)</b>#usm', $result, $area);
  830.             }
  831.             $area = $area[1];
  832.  
  833.             return [
  834.                 'ownership' => $ownershipIds,
  835.                 'area' => $area,
  836.             ];
  837.         }
  838.         while(1);
  839.     }
  840. }
  841.  
Add Comment
Please, Sign In to add comment