Subversion Repositories oidplus

Rev

Rev 1267 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. namespace ViaThinkSoft\OIDplus;
  21.  
  22. // phpcs:disable PSR1.Files.SideEffects
  23. \defined('INSIDE_OIDPLUS') or die;
  24. // phpcs:enable PSR1.Files.SideEffects
  25.  
  26. class OIDplusPageAdminRegistration extends OIDplusPagePluginAdmin
  27.         implements INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_1, /* oobeRequested, oobeEntry */
  28.                    INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8  /* getNotifications */
  29. {
  30.  
  31.         /**
  32.          *
  33.          */
  34.         /*private*/ const QUERY_REGISTER_V1 =         '1.3.6.1.4.1.37476.2.5.2.1.1.1';
  35.  
  36.         /**
  37.          *
  38.          */
  39.         /*private*/ const QUERY_UNREGISTER_V1 =       '1.3.6.1.4.1.37476.2.5.2.1.2.1';
  40.  
  41.         /**
  42.          *
  43.          */
  44.         /*private*/ const QUERY_LISTALLSYSTEMIDS_V1 = '1.3.6.1.4.1.37476.2.5.2.1.3.1';
  45.  
  46.         /**
  47.          *
  48.          */
  49.         /*private*/ const QUERY_LIVESTATUS_V1 =       '1.3.6.1.4.1.37476.2.5.2.1.4.1';
  50.  
  51.         /**
  52.          * @param string $actionID
  53.          * @return bool
  54.          */
  55.         public function csrfUnlock(string $actionID): bool {
  56.                 if ($actionID == 'verify_pubkey') return true;
  57.                 return parent::csrfUnlock($actionID);
  58.         }
  59.  
  60.         /**
  61.          * This action is called by the ViaThinkSoft server in order to verify that the system is in the ownership of the correct private key
  62.          * @param array $params
  63.          * @return array
  64.          * @throws OIDplusException
  65.          */
  66.         private function action_VerifyPubKey(array $params): array {
  67.                 _CheckParamExists($params, 'challenge');
  68.  
  69.                 $payload = 'oidplus-verify-pubkey:'.sha3_512($params['challenge']);
  70.  
  71.                 $signature = '';
  72.                 if (!OIDplus::getPkiStatus() || !@openssl_sign($payload, $signature, OIDplus::getSystemPrivateKey())) {
  73.                         throw new OIDplusException(_L('Signature failed'));
  74.                 }
  75.  
  76.                 return array(
  77.                         "status" => 0,
  78.                         "response" => base64_encode($signature)
  79.                 );
  80.         }
  81.  
  82.         /**
  83.          * @param string $actionID
  84.          * @param array $params
  85.          * @return array
  86.          * @throws OIDplusException
  87.          */
  88.         public function action(string $actionID, array $params): array {
  89.                 if ($actionID == 'verify_pubkey') {
  90.                         return $this->action_VerifyPubKey($params);
  91.                 } else {
  92.                         return parent::action($actionID, $params);
  93.                 }
  94.         }
  95.  
  96.         /**
  97.          * @param string $id
  98.          * @param array $out
  99.          * @param bool $handled
  100.          * @return void
  101.          * @throws OIDplusConfigInitializationException
  102.          * @throws OIDplusException
  103.          */
  104.         public function gui(string $id, array &$out, bool &$handled) {
  105.                 if ($id === 'oidplus:srv_registration') {
  106.                         $handled = true;
  107.                         $out['title'] = _L('System registration settings');
  108.                         $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
  109.  
  110.                         if (!OIDplus::authUtils()->isAdminLoggedIn()) {
  111.                                 throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')), $out['title'], 401);
  112.                         }
  113.  
  114.                         if (file_exists(__DIR__ . '/info$'.OIDplus::getCurrentLang().'.html')) {
  115.                                 $info = file_get_contents(__DIR__ . '/info$'.OIDplus::getCurrentLang().'.html');
  116.                         } else {
  117.                                 $info = file_get_contents(__DIR__ . '/info.html');
  118.                         }
  119.  
  120.                         list($html, $js, $css) = extractHtmlContents($info);
  121.                         $info = '';
  122.                         if (!empty($js))  $info .= "<script>\n$js\n</script>";
  123.                         if (!empty($css)) $info .= "<style>\n$css\n</style>";
  124.                         $info .= stripHtmlComments($html);
  125.  
  126.                         $out['text'] = $info;
  127.  
  128.                         if (!OIDplus::getPkiStatus()) {
  129.                                 $out['text'] .= '<p><font color="red">'._L('Error: Your system could not generate a private/public key pair. (OpenSSL is probably missing on your system). Therefore, you cannot register/unregister your OIDplus instance.').'</font></p>';
  130.                         } else if (!url_post_contents_available(true, $reason)) {
  131.                                 $out['text'] .= '<p><font color="red">';
  132.                                 $out['text'] .= _L('OIDplus cannot connect to the Internet (%1). Therefore, you <b>cannot</b> register your OIDplus instance now.', $reason);
  133.                                 $out['text'] .= '</font></p>';
  134.                         } else {
  135.                                 $out['text'] .= '<p><input type="button" onclick="openOidInPanel(\'oidplus:srvreg_status\');" value="'._L('Check status of the registration and collected data').'"></p>';
  136.  
  137.                                 if (OIDplus::baseConfig()->getValue('REGISTRATION_HIDE_SYSTEM', false)) {
  138.                                         $out['text'] .= '<p><font color="red"><b>'._L('Attention!').'</b> '._L('<code>REGISTRATION_HIDE_SYSTEM</code> is set in the local configuration file! Therefore, this system will not register itself, despite of the settings below.').'</font></p>';
  139.                                 }
  140.  
  141.                                 $out['text'] .= '<p>'._L('You can adjust your privacy level here').':</p><p><select name="reg_privacy" id="reg_privacy">';
  142.  
  143.                                 # ---
  144.  
  145.                                 $out['text'] .= '<option value="0"';
  146.                                 if (OIDplus::config()->getValue('reg_privacy') == 0) {
  147.                                         $out['text'] .= ' selected';
  148.                                 } else {
  149.                                         $out['text'] .= '';
  150.                                 }
  151.                                 $out['text'] .= '>'._L('0 = Register to directory service and automatically publish RA/OID data at oid-info.com').'</option>';
  152.  
  153.                                 # ---
  154.  
  155.                                 $out['text'] .= '<option value="1"';
  156.                                 if (OIDplus::config()->getValue('reg_privacy') == 1) {
  157.                                         $out['text'] .= ' selected';
  158.                                 } else {
  159.                                         $out['text'] .= '';
  160.                                 }
  161.                                 $out['text'] .= '>'._L('1 = Only register to directory service').'</option>';
  162.  
  163.                                 # ---
  164.  
  165.                                 $out['text'] .= '<option value="2"';
  166.                                 if (OIDplus::config()->getValue('reg_privacy') == 2) {
  167.                                         $out['text'] .= ' selected';
  168.                                 } else {
  169.                                         $out['text'] .= '';
  170.                                 }
  171.                                 $out['text'] .= '>'._L('2 = Hide system').'</option>';
  172.  
  173.                                 # ---
  174.  
  175.                                 $out['text'] .= '</select> <input type="button" value="'._L('Change').'" onclick="OIDplusPageAdminRegistration.crudActionRegPrivacyUpdate()"></p>';
  176.  
  177.                                 $out['text'] .= '<p>'._L('After clicking "change", your OIDplus system will contact the ViaThinkSoft server to adjust (add or remove information) your privacy setting. This may take a few minutes.').'</p>';
  178.  
  179.                                 $out['text'] .= '<p>'._L('<i>Privacy information:</i> Please note that removing your system from the directory does not automatically delete information about OIDs which are already published at oid-info.com. To remove already submitted OIDs at oid-info.com, please contact the <a href="mailto:admin@oid-info.com">OID Repository Webmaster</a>.').'</p>';
  180.                         }
  181.                 }
  182.                 if ($id === 'oidplus:srvreg_status') {
  183.                         $handled = true;
  184.                         $out['title'] = _L('Registration live status');
  185.                         $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
  186.  
  187.                         if (!OIDplus::authUtils()->isAdminLoggedIn()) {
  188.                                 throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')), $out['title'], 401);
  189.                         }
  190.  
  191.                         $query = self::QUERY_LIVESTATUS_V1;
  192.  
  193.                         $payload = array(
  194.                                 "query" => $query, // we must include $query to the playload, because we want to sign it
  195.                                 "lang" => OIDplus::getCurrentLang(),
  196.                                 "system_id" => OIDplus::getSystemId(false)
  197.                         );
  198.  
  199.                         $signature = '';
  200.                         if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
  201.                                 throw new OIDplusException(_L('Signature failed'));
  202.                         }
  203.  
  204.                         $data = array(
  205.                                 "payload" => $payload,
  206.                                 "signature" => base64_encode($signature)
  207.                         );
  208.  
  209.                         if (function_exists('gzdeflate')) {
  210.                                 $compressed = "1";
  211.                                 $data2 = gzdeflate(json_encode($data));
  212.                         } else {
  213.                                 $compressed = "0";
  214.                                 $data2 = json_encode($data);
  215.                         }
  216.  
  217.                         $res = url_post_contents(
  218.                                 'https://oidplus.viathinksoft.com/reg2/query.php',
  219.                                 array(
  220.                                         "query"      => $query,
  221.                                         "compressed" => $compressed,
  222.                                         "data"       => base64_encode($data2)
  223.                                 )
  224.                         );
  225.  
  226.                         if ($res === false) {
  227.                                 throw new OIDplusException(_L('Communication with %1 server failed', 'ViaThinkSoft'));
  228.                         }
  229.  
  230.                         $json = @json_decode($res, true);
  231.  
  232.                         if (!$json) {
  233.                                 throw new OIDplusException(_L('JSON reply from ViaThinkSoft decoding error: %1',$res), $out['title']);
  234.                         }
  235.  
  236.                         if (isset($json['error']) || ($json['status'] < 0)) {
  237.                                 if (isset($json['error'])) {
  238.                                         throw new OIDplusException(_L('Received error status code: %1',$json['error']), $out['title']);
  239.                                 } else {
  240.                                         throw new OIDplusException(_L('Received error status code: %1',$json['status']), $out['title']);
  241.                                 }
  242.                         }
  243.  
  244.                         $out['text']  = '<p><a '.OIDplus::gui()->link('oidplus:srv_registration').'><img src="img/arrow_back.png" width="16" alt="'._L('Go back').'"> '._L('Go back to registration settings').'</a></p>' .
  245.                                         $json['content'];
  246.                 }
  247.         }
  248.  
  249.         /**
  250.          * @return bool
  251.          * @throws OIDplusException
  252.          */
  253.         protected function areWeRegistered(): bool {
  254.                 // To check if we are registered. Check it "anonymously" (i.e. without revealing our system ID)
  255.                 $res = url_get_contents('https://oidplus.viathinksoft.com/reg2/query.php?query='.self::QUERY_LISTALLSYSTEMIDS_V1);
  256.                 if ($res === false) return false;
  257.  
  258.                 $json = @json_decode($res, true);
  259.  
  260.                 if (!$json) {
  261.                         return false; // throw new OIDplusException(_L('JSON reply from ViaThinkSoft decoding error: %1',$res));
  262.                 }
  263.  
  264.                 if (isset($json['error']) || ($json['status'] < 0)) {
  265.                         if (isset($json['error'])) {
  266.                                 return false; // throw new OIDplusException(_L('Received error status code: %1',$json['error']));
  267.                         } else {
  268.                                 return false; // throw new OIDplusException(_L('Received error status code: %1',$json['status']));
  269.                         }
  270.                 }
  271.  
  272.                 $list = $json['list'];
  273.  
  274.                 return in_array(OIDplus::getSystemId(false), $list);
  275.         }
  276.  
  277.         /**
  278.          * @param int|null $privacy_level
  279.          * @return false|void
  280.          * @throws OIDplusException|\OIDInfoException
  281.          */
  282.         public function sendRegistrationQuery(int $privacy_level=null) {
  283.  
  284.                 if (is_null($privacy_level)) {
  285.                         $privacy_level = OIDplus::config()->getValue('reg_privacy');
  286.                 }
  287.  
  288.                 $system_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL);
  289.  
  290.                 // It is very important that we set the ping time NOW, because ViaThinkSoft might contact us during the ping,
  291.                 // and this would cause an endless loop!
  292.                 OIDplus::config()->setValue('reg_last_ping', time());
  293.  
  294.                 if (!OIDplus::getPkiStatus()) return false;
  295.  
  296.                 if (!url_post_contents_available(true)) return false;
  297.  
  298.                 if ($privacy_level == 2) {
  299.                         // The user wants to unregister,  but we only unregister if we are registered
  300.                         if ($this->areWeRegistered()) {
  301.                                 $query = self::QUERY_UNREGISTER_V1;
  302.  
  303.                                 $payload = array(
  304.                                         "query" => $query, // we must include $query to the payload, because we want to sign it
  305.                                         "system_id" => OIDplus::getSystemId(false)
  306.                                 );
  307.  
  308.                                 $signature = '';
  309.                                 if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
  310.                                         return false; // throw new OIDplusException(_L('Signature failed'));
  311.                                 }
  312.  
  313.                                 $data = array(
  314.                                         "payload" => $payload,
  315.                                         "signature" => base64_encode($signature)
  316.                                 );
  317.  
  318.                                 if (function_exists('gzdeflate')) {
  319.                                         $compressed = "1";
  320.                                         $data2 = gzdeflate(json_encode($data));
  321.                                 } else {
  322.                                         $compressed = "0";
  323.                                         $data2 = json_encode($data);
  324.                                 }
  325.  
  326.                                 $res = url_post_contents(
  327.                                         'https://oidplus.viathinksoft.com/reg2/query.php',
  328.                                         array(
  329.                                                 "query" => $query,
  330.                                                 "compressed" => $compressed,
  331.                                                 "data" => base64_encode($data2)
  332.                                         )
  333.                                 );
  334.  
  335.                                 if ($res === false) return false; // throw new OIDplusException(_L('Communication with %1 server failed', 'ViaThinkSoft'));
  336.  
  337.                                 $json = @json_decode($res, true);
  338.  
  339.                                 if (!$json) {
  340.                                         return false; // throw new OIDplusException(_L('JSON reply from ViaThinkSoft decoding error: %1',$res));
  341.                                 }
  342.  
  343.                                 if (isset($json['error']) || ($json['status'] < 0)) {
  344.                                         return false; // throw new OIDplusException(_L('Received error status code: %1',isset($json['error']) ? $json['error'] : $json['status']));
  345.                                 }
  346.                         }
  347.                 } else {
  348.                         if ($privacy_level == 0) {
  349.                                 $adminExportPlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.3.400'); // OIDplusPageAdminOIDInfoExport
  350.                                 if (!is_null($adminExportPlugin)) {
  351.                                         list($oidinfo_xml, $dummy_content_type) = OIDplusPageAdminOIDInfoExport::outputXML(false); // no online check, because the query should be short (since the query is done while a visitor waits for the response)
  352.                                 } else {
  353.                                         $oidinfo_xml = false;
  354.                                 }
  355.                         } else {
  356.                                 $oidinfo_xml = false;
  357.                         }
  358.  
  359.                         $query = self::QUERY_REGISTER_V1;
  360.  
  361.                         $root_oids = array();
  362.                         foreach (OIDplus::getEnabledObjectTypes() as $ot) {
  363.                                 if ($ot::ns() == 'oid') {
  364.                                         $res = OIDplus::db()->query("select id from ###objects where " .
  365.                                                                     "(parent = 'oid:' or " .
  366.                                                                     // The following two cases are special cases e.g. if there are multiple PEN or UUID-OIDs, and the system owner decides to use the IANA PEN as root OID, but actually, it is not "his" root then! The OIDs inside the IANA PEN root are his root then!
  367.                                                                     "parent in (select oid from ###asn1id where well_known = ?) or " .
  368.                                                                     "parent in (select oid from ###iri where well_known = ?)) and " .
  369.                                                                     // We assume hereby that RAs of well-known OIDs (e.g. IANA) will not use OIDplus for allocating OIDs:
  370.                                                                     "id not in (select oid from ###asn1id where well_known = ?) and " .
  371.                                                                     "id not in (select oid from ###iri where well_known = ?)", array(true, true, true, true));
  372.                                         $res->naturalSortByField('id');
  373.                                         while ($row = $res->fetch_array()) {
  374.                                                 $root_oids[] = substr($row['id'],strlen('oid:'));
  375.                                         }
  376.                                 }
  377.                         }
  378.                         $payload = array(
  379.                                 "query" => $query, // we must include $query to the payload, because we want to sign it
  380.                                 "privacy_level" => $privacy_level,
  381.                                 "system_id" => OIDplus::getSystemId(false),
  382.                                 "public_key" => OIDplus::getSystemPublicKey(),
  383.                                 "system_url" => $system_url,
  384.                                 "hide_system_url" => 0,
  385.                                 "hide_public_key" => 0,
  386.                                 "admin_email" => OIDplus::config()->getValue('admin_email'),
  387.                                 "system_title" => OIDplus::config()->getValue('system_title'),
  388.                                 "oidinfo_xml" => @base64_encode($oidinfo_xml),
  389.                                 "root_oids" => $root_oids,
  390.                                 "system_version" => OIDplus::getVersion(),
  391.                                 "system_install_type" => OIDplus::getInstallType()
  392.                         );
  393.  
  394.                         $signature = '';
  395.                         if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
  396.                                 return false; // throw new OIDplusException(_L('Signature failed'));
  397.                         }
  398.  
  399.                         $data = array(
  400.                                 "payload" => $payload,
  401.                                 "signature" => base64_encode($signature)
  402.                         );
  403.  
  404.                         if (function_exists('gzdeflate')) {
  405.                                 $compressed = "1";
  406.                                 $data2 = gzdeflate(json_encode($data));
  407.                         } else {
  408.                                 $compressed = "0";
  409.                                 $data2 = json_encode($data);
  410.                         }
  411.  
  412.                         $res = url_post_contents(
  413.                                 'https://oidplus.viathinksoft.com/reg2/query.php',
  414.                                 array(
  415.                                         "query"      => $query,
  416.                                         "compressed" => $compressed,
  417.                                         "data"       => base64_encode($data2)
  418.                                 )
  419.                         );
  420.  
  421.                         if ($res === false) return false; // throw new OIDplusException(_L('Communication with %1 server failed', 'ViaThinkSoft'));
  422.  
  423.                         $json = @json_decode($res, true);
  424.  
  425.                         if (!$json) {
  426.                                 return false; // throw new OIDplusException(_L('JSON reply from ViaThinkSoft decoding error: %1',$res));
  427.                         }
  428.  
  429.                         if (isset($json['error']) || ($json['status'] < 0)) {
  430.                                 if (isset($json['error'])) {
  431.                                         return false; // throw new OIDplusException(_L('Received error status code: %1',$json['error']));
  432.                                 } else {
  433.                                         return false; // throw new OIDplusException(_L('Received error status code: %1',$json['status']));
  434.                                 }
  435.                         } else if ($json['status'] == 99/*Hash conflict*/) {
  436.                                 OIDplus::logger()->log("V2:[WARN]A", "Removing SystemID and key pair because there is a hash conflict with another OIDplus system!");
  437.  
  438.                                 // Delete the system ID since we have a conflict with the 31-bit hash!
  439.                                 OIDplus::config()->setValue('oidplus_private_key', '');
  440.                                 OIDplus::config()->setValue('oidplus_public_key', '');
  441.  
  442.                                 // Try to generate a new system ID
  443.                                 OIDplus::getPkiStatus(true);
  444.  
  445.                                 // Enforce a new registration attempt at the next page visit
  446.                                 // We will not try again here, because that might lead to an endless loop if the VTS server would always return 'HASH_CONFLCIT'
  447.                                 OIDplus::config()->setValue('reg_last_ping', 0);
  448.                         } else if ($json['status'] == 0/*OK*/) {
  449.                                 // Note: whois.viathinksoft.de:43 uses VGWhoIs, which uses these patterns: https://github.com/danielmarschall/vgwhois/blob/master/main/pattern/oid
  450.                                 // If your system gets acknowledged by ViaThinkSoft, then vts_whois will be filled with that server name whois.viathinksoft.de:43
  451.                                 if (isset($json['vts_whois'])) OIDplus::config()->setValue('vts_whois', $json['vts_whois']);
  452.  
  453.                                 // ViaThinkSoft certifies the system public key and other system attributes and root objects (requires human verification)
  454.                                 if (isset($json['vts_cert'])) OIDplus::config()->setValue('vts_cert', $json['vts_cert']);
  455.                                 if (isset($json['vts_ca'])) OIDplus::config()->setValue('vts_ca', $json['vts_ca']);
  456.                         }
  457.                 }
  458.         }
  459.  
  460.         /**
  461.          * @param bool $html
  462.          * @return void
  463.          * @throws OIDplusException
  464.          */
  465.         public function init(bool $html=true) {
  466.                 if (OIDplus::getEditionInfo()['vendor'] != 'ViaThinkSoft') {
  467.                         throw new OIDplusException(_L('This plugin is only available in the ViaThinkSoft edition of OIDplus'));
  468.                 }
  469.  
  470.                 // Note: It is important that the default value is '2', otherwise, systems which don't have CURL will fail
  471.                 OIDplus::config()->prepareConfigKey('reg_privacy', '2=Hide your system, 1=Register your system to the ViaThinkSoft directory and oid-info.com, 0=Publish your system to ViaThinkSoft directory and all public contents (RA/OID) to oid-info.com', '2', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
  472.                         if (($value != '0') && ($value != '1') && ($value != '2')) {
  473.                                 throw new OIDplusException(_L('Please enter either 0, 1 or 2.'));
  474.                         }
  475.                         // Now do a recheck and notify the ViaThinkSoft server
  476.                         if (($value == 2) || !OIDplus::baseConfig()->getValue('REGISTRATION_HIDE_SYSTEM', false)) {
  477.                                 OIDplus::config()->setValue('reg_last_ping', 0);
  478.                                 if (!url_post_contents_available(true, $reason)) throw new OIDplusException(_L('The system cannot contact the ViaThinkSoft server to change the registration settings.').' '.$reason);
  479.                                 $this->sendRegistrationQuery($value);
  480.                         }
  481.                 });
  482.                 OIDplus::config()->prepareConfigKey('reg_ping_interval', 'Registration ping interval (in seconds)', '3600', OIDplusConfig::PROTECTION_HIDDEN, function($value) {
  483.  
  484.                 });
  485.                 OIDplus::config()->prepareConfigKey('reg_last_ping', 'Last ping to ViaThinkSoft directory services', '0', OIDplusConfig::PROTECTION_HIDDEN, function($value) {
  486.  
  487.                 });
  488.                 OIDplus::config()->prepareConfigKey('vts_whois', 'ViaThinkSoft Whois Server (if this system is recognized)', '', OIDplusConfig::PROTECTION_READONLY, function($value) {
  489.  
  490.                 });
  491.                 OIDplus::config()->prepareConfigKey('vts_cert', 'ViaThinkSoft certificate (requires registration)', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) {
  492.  
  493.                 });
  494.                 OIDplus::config()->prepareConfigKey('vts_ca', 'ViaThinkSoft certificate root (requires registration)', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) {
  495.  
  496.                 });
  497.                 OIDplus::config()->prepareConfigKey('oobe_registration_done', '"Out Of Box Experience" wizard for OIDplusPageAdminRegistration done once?', '0', OIDplusConfig::PROTECTION_HIDDEN, function($value) {});
  498.  
  499.                 // Is it time to register / renew the directory entry?
  500.                 // Note: REGISTRATION_HIDE_SYSTEM is an undocumented constant that can be put in the userdata/baseconfig/config.inc.php files of a test system accessing the same database as the productive system that is registered.
  501.                 // This avoids that the URL of a productive system is overridden with the URL of a cloned test system (since they use the same database, they also have the same system ID)
  502.  
  503.                 if (OIDplus::config()->getValue('oobe_registration_done') == '1') {
  504.                         if (!OIDplus::baseConfig()->getValue('REGISTRATION_HIDE_SYSTEM', false)) {
  505.                                 $privacy_level = OIDplus::config()->getValue('reg_privacy');
  506.  
  507.                                 if (PHP_SAPI !== 'cli') { // don't register when called from CLI, otherwise the oidinfo XML can't convert relative links into absolute links
  508.                                         $last_ping = OIDplus::config()->getValue('reg_last_ping');
  509.                                         if (!is_numeric($last_ping)) $last_ping = 0;
  510.                                         $last_ping_interval = OIDplus::config()->getValue('reg_ping_interval');
  511.                                         if (!is_numeric($last_ping_interval)) $last_ping_interval = 3600;
  512.  
  513.                                         // Cronjobs get half ping interval, to make sure that a web visitor won't get any delay
  514.                                         if (OIDplus::isCronjob()) $last_ping_interval /= 2;
  515.  
  516.                                         if ((time()-$last_ping >= $last_ping_interval)) {
  517.                                                 try {
  518.                                                         $this->sendRegistrationQuery();
  519.                                                 } catch (\Exception $e) {
  520.                                                         // Don't do anything, because we don't want that a failed registration query blocks the system
  521.                                                         OIDplus::logger()->log('V2:[WARN]A', 'System registration query crashed: %1', $e->getMessage());
  522.                                                 }
  523.                                         }
  524.                                 }
  525.                         }
  526.                 }
  527.         }
  528.  
  529.         /**
  530.          * @param array $json
  531.          * @param string|null $ra_email
  532.          * @param bool $nonjs
  533.          * @param string $req_goto
  534.          * @return bool
  535.          * @throws OIDplusException
  536.          */
  537.         public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
  538.                 if (!OIDplus::authUtils()->isAdminLoggedIn()) return false;
  539.  
  540.                 if (file_exists(__DIR__.'/img/main_icon16.png')) {
  541.                         $tree_icon = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png';
  542.                 } else {
  543.                         $tree_icon = null; // default icon (folder)
  544.                 }
  545.  
  546.                 $json[] = array(
  547.                         'id' => 'oidplus:srv_registration',
  548.                         'icon' => $tree_icon,
  549.                         'text' => _L('System registration')
  550.                 );
  551.  
  552.                 return true;
  553.         }
  554.  
  555.         /**
  556.          * @param string $request
  557.          * @return array|false
  558.          */
  559.         public function tree_search(string $request) {
  560.                 return false;
  561.         }
  562.  
  563.         /**
  564.          * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_1
  565.          * @return bool
  566.          * @throws OIDplusException
  567.          */
  568.         public function oobeRequested(): bool {
  569.                 return OIDplus::config()->getValue('oobe_registration_done') == '0';
  570.         }
  571.  
  572.         /**
  573.          * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_1
  574.          * @param int $step
  575.          * @param bool $do_edits
  576.          * @param bool $errors_happened
  577.          * @return void
  578.          * @throws OIDplusConfigInitializationException
  579.          * @throws OIDplusException
  580.          */
  581.         public function oobeEntry(int $step, bool $do_edits, bool &$errors_happened)/*: void*/ {
  582.                 echo '<h2>'._L('Step %1: System registration and automatic publishing (optional)',$step).'</h2>';
  583.  
  584.                 if (file_exists(__DIR__ . '/info$'.OIDplus::getCurrentLang().'.html')) {
  585.                         $info = file_get_contents(__DIR__ . '/info$'.OIDplus::getCurrentLang().'.html');
  586.                 } else {
  587.                         $info = file_get_contents(__DIR__ . '/info.html');
  588.                 }
  589.  
  590.                 // make sure the program works even if the user provided HTML is not UTF-8
  591.                 $info = convert_to_utf8_no_bom($info);
  592.  
  593.                 echo $info;
  594.  
  595.                 if (!url_post_contents_available(true, $reason)) {
  596.                         echo '<p><font color="red">';
  597.                         echo _L('OIDplus cannot connect to the Internet (%1). Therefore, you <b>cannot</b> register your OIDplus instance now.', $reason);
  598.                         echo '</font></p>';
  599.                         if ($do_edits) {
  600.                                 OIDplus::config()->setValue('oobe_registration_done', '1');
  601.                         }
  602.                         return;
  603.                 }
  604.  
  605.                 $pki_status = OIDplus::getPkiStatus();
  606.  
  607.                 if (!$pki_status) {
  608.                         echo '<p><font color="red">';
  609.                         echo _L('Your system could not generate a private/public key pair. (OpenSSL is probably missing on your system).').' ';
  610.                         echo _L('Therefore, you <b>cannot</b> register your OIDplus instance now.');
  611.                         echo '</font></p>';
  612.                         if ($do_edits) {
  613.                                 OIDplus::config()->setValue('oobe_registration_done', '1');
  614.                         }
  615.                         return;
  616.                 }
  617.  
  618.                 echo '<p>'._L('Privacy level').':</p><select name="reg_privacy" id="reg_privacy">';
  619.  
  620.                 # ---
  621.  
  622.                 echo '<option value="0"';
  623.                 if (isset($_POST['sent'])) {
  624.                         if (isset($_POST['reg_privacy']) && ($_POST['reg_privacy'] == 0)) echo ' selected';
  625.                 } else {
  626.                         if ((OIDplus::config()->getValue('reg_privacy') == 0) || !OIDplus::config()->getValue('oobe_registration_done')) {
  627.                                 echo ' selected';
  628.                         } else {
  629.                                 echo '';
  630.                         }
  631.                 }
  632.                 echo '>'._L('0 = Register to directory service and automatically publish RA/OID data at oid-info.com').'</option>';
  633.  
  634.                 # ---
  635.  
  636.                 echo '<option value="1"';
  637.                 if (isset($_POST['sent'])) {
  638.                         if (isset($_POST['reg_privacy']) && ($_POST['reg_privacy'] == 1)) echo ' selected';
  639.                 } else {
  640.                         if ((OIDplus::config()->getValue('reg_privacy') == 1)) {
  641.                                 echo ' selected';
  642.                         } else {
  643.                                 echo '';
  644.                         }
  645.                 }
  646.                 echo '>'._L('1 = Only register to directory service').'</option>';
  647.  
  648.                 # ---
  649.  
  650.                 echo '<option value="2"';
  651.                 if (isset($_POST['sent'])) {
  652.                         if (isset($_POST['reg_privacy']) && ($_POST['reg_privacy'] == 2)) echo ' selected';
  653.                 } else {
  654.                         if ((OIDplus::config()->getValue('reg_privacy') == 2)) {
  655.                                 echo ' selected';
  656.                         } else {
  657.                                 echo '';
  658.                         }
  659.                 }
  660.                 echo '>'._L('2 = Hide system').'</option>';
  661.  
  662.                 # ---
  663.  
  664.                 echo '</select>';
  665.  
  666.                 $htmlmsg = '';
  667.                 if ($do_edits) {
  668.                         try {
  669.                                 OIDplus::config()->setValue('reg_privacy', $_POST['reg_privacy'] ?? 1);
  670.                                 OIDplus::config()->setValue('oobe_registration_done', '1');
  671.                         } catch (\Exception $e) {
  672.                                 $htmlmsg = $e instanceof OIDplusException ? $e->getHtmlMessage() : htmlentities($e->getMessage());
  673.                                 $errors_happened = true;
  674.                         }
  675.                 }
  676.                 if (!empty($htmlmsg)) echo ' <font color="red"><b>'.$htmlmsg.'</b></font>';
  677.  
  678.                 echo '<p>'._L('<i>Privacy information:</i> This setting can always be changed in the administrator login / control panel.').'<br>';
  679.                 echo _L('<a %1>Click here</a> for more information about privacy related topics.','href="../../../../res/OIDplus/privacy_documentation.html" target="_blank"');
  680.                 echo '</p>';
  681.         }
  682.  
  683.         /**
  684.          * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
  685.          * @param string|null $user
  686.          * @return array
  687.          * @throws OIDplusException
  688.          */
  689.         public function getNotifications(string $user=null): array {
  690.                 $notifications = array();
  691.                 if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
  692.                         if (!url_post_contents_available(true, $reason)) {
  693.                                 $title = _L('System registration');
  694.                                 $notifications[] = new OIDplusNotification('ERR', _L('OIDplus plugin "%1" is enabled, but OIDplus cannot connect to the Internet.', '<a '.OIDplus::gui()->link('oidplus:srv_registration').'>'.htmlentities($title).'</a>').' '.$reason);
  695.                         }
  696.                 }
  697.                 return $notifications;
  698.         }
  699.  
  700. }
  701.