Subversion Repositories oidplus

Rev

Rev 867 | Rev 870 | 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 - 2022 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. require_once __DIR__ . '/../../../../../includes/oidplus.inc.php';
  21.  
  22. define('XML_URN', 'urn:ietf:id:viathinksoft-oidip-03');
  23. define('XML_URN_URL', OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE).'xml_schema.xsd');
  24. define('JSON_SCHEMA', OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE).'json_schema.json');
  25.  
  26. OIDplus::init(true);
  27. set_exception_handler(array('OIDplusGui', 'html_exception_handler'));
  28.  
  29. if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_OIDplusPagePublicWhois', false)) {
  30.         throw new OIDplusException(_L('This plugin was disabled by the system administrator!'));
  31. }
  32.  
  33. originHeaders();
  34.  
  35. // Step 0: Get request parameter
  36.  
  37. if (PHP_SAPI == 'cli') {
  38.         if ($_SERVER['argc'] != 2) {
  39.                 echo _L('Syntax').': '.$_SERVER['argv'][0].' <query>'."\n";
  40.                 exit(2);
  41.         }
  42.         $query = $_SERVER['argv'][1];
  43. } else {
  44.         if (!isset($_REQUEST['query'])) {
  45.                 http_response_code(400);
  46.                 die('<h1>'._L('Error').'</h1><p>'._L('Argument "%1" is missing','query').'<p>');
  47.         }
  48.         $query = $_REQUEST['query'];
  49. }
  50.  
  51. // Split input into query, authTokens and serverCommands
  52. $tokens = explode('$', $query);
  53. $query = array_shift($tokens);
  54. $authTokens[] = array();
  55. $serverCommands = array();
  56. foreach ($tokens as $token) {
  57.         if (strpos($token,'=') !== false) {
  58.                 $tmp = explode('=',$token,2);
  59.                 $serverCommands[strtolower($tmp[0])] = $tmp[1];
  60.         } else {
  61.                 $authTokens[] = $token;
  62.         }
  63. }
  64.  
  65. $query = str_replace('oid:.', 'oid:', $query); // allow leading dot
  66.  
  67. if (isset($_REQUEST['format'])) {
  68.         $format = $_REQUEST['format'];
  69. } else if (isset($serverCommands['format'])) {
  70.         $format = $serverCommands['format'];
  71. } else {
  72.         $format = 'text'; // default
  73. }
  74.  
  75. $unimplemented_format = ($format != 'text') && ($format != 'json') && ($format != 'xml');
  76. if ($unimplemented_format) {
  77.         $format = 'text';
  78. }
  79.  
  80. // Step 1: Collect data
  81.  
  82. $out = array();
  83.  
  84. $out[] = "query: $query";
  85.  
  86. $query = OIDplus::prefilterQuery($query, false);
  87.  
  88. if ($unimplemented_format) {
  89.         $out[] = 'result: Service error';
  90.         $out[] = 'message: Format is not implemented';
  91. } else {
  92.  
  93.         $distance = null;
  94.         $found = null;
  95.  
  96.         try {
  97.                 $obj = OIDplusObject::findFitting($query);
  98.                 if (!$obj) $obj = OIDplusObject::parse($query); // in case we didn't find anything fitting, we take it as it is and later use getParent() to find something else
  99.                 $query = $obj->nodeId();
  100.         } catch (Exception $e) {
  101.                 $obj = null;
  102.         }
  103.  
  104.         $only_wellknown_ids_found = false;
  105.         $continue = false;
  106.  
  107.         if (!$obj) {
  108.                 $out[] = "result: Not found"; // DO NOT TRANSLATE!
  109.                 $continue = false;
  110.                 $res = null;
  111.         } else {
  112.                 $obj = null;
  113.                 $distance = 0;
  114.  
  115.                 $init_query = $query;
  116.                 while (true) {
  117.                         $res = OIDplus::db()->query("select * from ###objects where id = ?", array($query));
  118.                         if ($res->any()) {
  119.                                 $obj = OIDplusObject::parse($query);
  120.                                 if ($distance > 0) {
  121.                                         $out[] = "result: Not found; superior object found"; // DO NOT TRANSLATE!
  122.                                         $out[] = "distance: $distance"; // DO NOT TRANSLATE
  123.                                 } else {
  124.                                         $out[] = "result: Found"; // DO NOT TRANSLATE!
  125.                                 }
  126.                                 $continue = true;
  127.                                 break;
  128.                         }
  129.  
  130.                         if (substr($query,0,4) === 'oid:') {
  131.                                 $query_prev = $query;
  132.                                 $query = 'oid:'.oid_up(explode(':',$query,2)[1]);
  133.                                 if ($query == $query_prev) break;
  134.                                 $distance++;
  135.                         } else {
  136.                                 // getParent() will find the parent which DOES exist in the DB.
  137.                                 // It does not need to be the direct parent (like ->one_up() does)
  138.                                 $obj = OIDplusObject::parse($query)->getParent(); // For objects, we assume that they are parents of each other
  139.                                 if ($obj) {
  140.                                         $res = OIDplus::db()->query("select * from ###objects where id = ?", array($obj->nodeId()));
  141.                                         $distance = $obj->distance($query);
  142.                                         assert($res->any());
  143.  
  144.                                         $query = $obj->nodeId();
  145.                                 }
  146.  
  147.                                 if ($distance > 0) {
  148.                                         $out[] = "result: Not found; superior object found"; // DO NOT TRANSLATE!
  149.                                         $out[] = "distance: $distance"; // DO NOT TRANSLATE
  150.                                 }
  151.                                 $continue = true;
  152.  
  153.                                 break;
  154.                         }
  155.                 }
  156.  
  157.                 if ((substr($query,0,4) === 'oid:') && (!$obj)) {
  158.                         $query = $init_query;
  159.                         $distance = 0;
  160.                         while (true) {
  161.                                 $res = OIDplus::db()->query("select * from ###asn1id where oid = ? union select * from ###iri where oid = ?", array($query, $query));
  162.                                 if ($res->any()) {
  163.                                         $obj = OIDplusObject::parse($query);
  164.                                         $res = null;
  165.                                         if ($distance > 0) {
  166.                                                 $out[] = "result: Not found; superior object found"; // DO NOT TRANSLATE!
  167.                                                 $out[] = "distance: $distance"; // DO NOT TRANSLATE
  168.                                         } else {
  169.                                                 $out[] = "result: Found"; // DO NOT TRANSLATE!
  170.                                         }
  171.                                         $only_wellknown_ids_found = true; // Information partially available
  172.                                         $continue = true;
  173.                                         break;
  174.                                 }
  175.                                 $query_prev = $query;
  176.                                 $query = 'oid:'.oid_up(explode(':',$query,2)[1]);
  177.                                 if ($query == $query_prev) break;
  178.                                 $distance++;
  179.                         }
  180.                 }
  181.  
  182.                 if (!$obj) {
  183.                         $out[] = "result: Not found"; // DO NOT TRANSLATE!
  184.                         $continue = false;
  185.                 }
  186.  
  187.                 $found = $distance == 0;
  188.         }
  189.  
  190.         if ($continue) {
  191.                 $out[] = "";
  192.                 $out[] = "object: $query"; // DO NOT TRANSLATE!
  193.                 if (!allowObjectView($obj, $authTokens)) {
  194.                         $out[] = "status: Information unavailable"; // DO NOT TRANSLATE!
  195.                         $out[] = "attribute: confidential"; // DO NOT TRANSLATE!
  196.                 } else {
  197.                         if ($only_wellknown_ids_found) {
  198.                                 $out[] = "status: Information partially available"; // DO NOT TRANSLATE!
  199.                         } else {
  200.                                 $out[] = "status: Information available"; // DO NOT TRANSLATE!
  201.                         }
  202.  
  203.                         $row = $res ? $res->fetch_object() : null;
  204.  
  205.                         if (!is_null($row)) {
  206.                                 $out[] = 'name: ' . $row->title; // DO NOT TRANSLATE!
  207.  
  208.                                 $cont = $row->description;
  209.                                 $cont = preg_replace('@<a[^>]+href\s*=\s*["\']([^\'"]+)["\'][^>]*>(.+)<\s*/\s*a\s*>@ismU', '\2 (\1)', $cont);
  210.                                 $cont = preg_replace('@<br.*>@', "\n", $cont);
  211.                                 $cont = preg_replace('@\\n+@', "\n", $cont);
  212.                                 $out[] = 'description: ' . trim(html_entity_decode(strip_tags($cont))); // DO NOT TRANSLATE!
  213.                         }
  214.  
  215.                         if (substr($query,0,4) === 'oid:') {
  216.                                 $out[] = 'asn1-notation: ' . $obj->getAsn1Notation(false); // DO NOT TRANSLATE!
  217.                                 $out[] = 'iri-notation: ' . $obj->getIriNotation(false); // DO NOT TRANSLATE!
  218.  
  219.                                 $res2 = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($obj->nodeId()));
  220.                                 while ($row2 = $res2->fetch_object()) {
  221.                                         $out[] = 'identifier: ' . $row2->name; // DO NOT TRANSLATE!
  222.                                 }
  223.  
  224.                                 $res2 = OIDplus::db()->query("select * from ###asn1id where standardized = ? and oid = ?", array(true, $obj->nodeId()));
  225.                                 while ($row2 = $res2->fetch_object()) {
  226.                                         $out[] = 'standardized-id: ' . $row2->name; // DO NOT TRANSLATE!
  227.                                 }
  228.  
  229.                                 $res2 = OIDplus::db()->query("select * from ###iri where oid = ?", array($obj->nodeId()));
  230.                                 while ($row2 = $res2->fetch_object()) {
  231.                                         $out[] = 'unicode-label: ' . $row2->name; // DO NOT TRANSLATE!
  232.                                 }
  233.  
  234.                                 $res2 = OIDplus::db()->query("select * from ###iri where longarc = ? and oid = ?", array(true, $obj->nodeId()));
  235.                                 while ($row2 = $res2->fetch_object()) {
  236.                                         $out[] = 'long-arc: ' . $row2->name; // DO NOT TRANSLATE!
  237.                                 }
  238.                         }
  239.  
  240.                         if ($obj->isConfidential()) { // yes, we use isConfidential() instead of allowObjectView()!
  241.                                 $out[] = 'attribute: confidential'; // DO NOT TRANSLATE!
  242.                         }
  243.  
  244.                         if ($obj->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
  245.                                 // Also ask $obj for extra attributes:
  246.                                 // This way we could add various additional information, e.g. IPv4/6 range analysis, interpretation of GUID, etc. (TODO)
  247.                                 $obj->whoisObjectAttributes($obj->nodeId(), $out);
  248.                         }
  249.  
  250.                         foreach (OIDplus::getPagePlugins() as $plugin) {
  251.                                 if ($plugin->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
  252.                                         $plugin->whoisObjectAttributes($obj->nodeId(), $out);
  253.                                 }
  254.                         }
  255.  
  256.                         if (substr($query,0,4) === 'oid:') {
  257.                                 $sParent = 'oid:'.oid_up(explode(':',$query,2)[1]);
  258.  
  259.                                 $objTest = OIDplusObject::parse($sParent);
  260.                                 if (allowObjectView($objTest, $authTokens)) {
  261.                                         $out[] = 'parent: ' . $sParent . show_asn1_appendix($sParent); // DO NOT TRANSLATE!
  262.                                 } else {
  263.                                         $out[] = 'parent: ' . $sParent; // DO NOT TRANSLATE!
  264.                                 }
  265.                         } else if (!is_null($row) && !empty($row->parent) && (!is_root($row->parent))) {
  266.                                 $sParent = $row->parent;
  267.                                 $out[] = 'parent: ' . $row->parent; // DO NOT TRANSLATE!
  268.                         }
  269.  
  270.                         $res2 = OIDplus::db()->query("select * from ###objects where parent = ? order by ".OIDplus::db()->natOrder('id'), array($obj->nodeId()));
  271.                         while ($row2 = $res2->fetch_object()) {
  272.                                 $objTest = OIDplusObject::parse($row2->id);
  273.                                 if (allowObjectView($objTest, $authTokens)) {
  274.                                         $out[] = 'subordinate: ' . $row2->id . show_asn1_appendix($row2->id); // DO NOT TRANSLATE!
  275.                                 } else {
  276.                                         $out[] = 'subordinate: ' . $row2->id; // DO NOT TRANSLATE!
  277.                                 }
  278.                         }
  279.  
  280.                         if (!is_null($row)) {
  281.                                 if ($row->created) $out[] = 'created: ' . date('Y-m-d H:i:s', strtotime($row->created)); // DO NOT TRANSLATE!
  282.                                 if ($row->updated) $out[] = 'updated: ' . date('Y-m-d H:i:s', strtotime($row->updated)); // DO NOT TRANSLATE!
  283.                         }
  284.  
  285.                         $out[] = '';
  286.  
  287.                         $res2 = OIDplus::db()->query("select * from ###ra where email = ?", array(is_null($row) ? '' : $row->ra_email));
  288.                         if ($row2 = $res2->fetch_object()) {
  289.                                 $out[] = 'ra: '.(!empty($row2->ra_name) ? $row2->ra_name : (!empty($row2->email) ? $row2->email : _L('Unknown'))); // DO NOT TRANSLATE!
  290.                                 $out[] = 'ra-status: Information available'; // DO NOT TRANSLATE!
  291.  
  292.                                 $tmp = array();
  293.                                 if (!empty($row2->office)) $tmp[] = $row2->office;
  294.                                 if (!empty($row2->organization)) $tmp[] = $row2->organization;
  295.                                 $tmp = implode(', ', $tmp);
  296.  
  297.                                 $out[] = 'ra-contact-name: ' . $row2->personal_name.(!empty($tmp) ? " ($tmp)" : ''); // DO NOT TRANSLATE!
  298.                                 if (!allowRAView($row2, $authTokens)) {
  299.                                         if (!empty($row2->street) || !empty($row2->zip_town) || !empty($row2->country)) {
  300.                                                 $out[] = 'ra-address: '._L('(redacted)'); // DO NOT TRANSLATE!
  301.                                         }
  302.                                         $out[] = 'ra-phone: ' . (!empty($row2->phone) ? _L('(redacted)') : ''); // DO NOT TRANSLATE!
  303.                                         $out[] = 'ra-mobile: ' . (!empty($row2->mobile) ? _L('(redacted)') : ''); // DO NOT TRANSLATE!
  304.                                         $out[] = 'ra-fax: ' . (!empty($row2->fax) ? _L('(redacted)') : ''); // DO NOT TRANSLATE!
  305.                                 } else {
  306.                                         if (!empty($row2->street))   $out[] = 'ra-address: ' . $row2->street; // DO NOT TRANSLATE!
  307.                                         if (!empty($row2->zip_town)) $out[] = 'ra-address: ' . $row2->zip_town; // DO NOT TRANSLATE!
  308.                                         if (!empty($row2->country))  $out[] = 'ra-address: ' . $row2->country; // DO NOT TRANSLATE!
  309.                                         $out[] = 'ra-phone: ' . $row2->phone; // DO NOT TRANSLATE!
  310.                                         $out[] = 'ra-mobile: ' . $row2->mobile; // DO NOT TRANSLATE!
  311.                                         $out[] = 'ra-fax: ' . $row2->fax; // DO NOT TRANSLATE!
  312.                                 }
  313.                                 $out[] = 'ra-email: ' . $row->ra_email; // DO NOT TRANSLATE!
  314.  
  315.                                 $ra = new OIDplusRA($row->ra_email);
  316.                                 if ($ra->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
  317.                                         $ra->whoisRaAttributes($row->ra_email, $out); /** @phpstan-ignore-line */
  318.                                 }
  319.  
  320.                                 foreach (OIDplus::getPagePlugins() as $plugin) {
  321.                                         if ($plugin->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
  322.                                                 $plugin->whoisRaAttributes($row->ra_email, $out);
  323.                                         }
  324.                                 }
  325.  
  326.                                 if ($row2->privacy) { // yes, we use row2->privacy() instead of allowRAView()!
  327.                                         $out[] = 'ra-attribute: confidential'; // DO NOT TRANSLATE!
  328.                                 }
  329.  
  330.                                 if ($row2->registered) $out[] = 'ra-created: ' . date('Y-m-d H:i:s', strtotime($row2->registered)); // DO NOT TRANSLATE!
  331.                                 if ($row2->updated)    $out[] = 'ra-updated: ' . date('Y-m-d H:i:s', strtotime($row2->updated)); // DO NOT TRANSLATE!
  332.                         } else {
  333.                                 $out[] = 'ra: '.(!is_null($row) && !empty($row->ra_email) ? $row->ra_email : _L('Unknown')); // DO NOT TRANSLATE!
  334.                                 if (!is_null($row)) {
  335.                                         foreach (OIDplus::getPagePlugins() as $plugin) {
  336.                                                 if ($plugin->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
  337.                                                         $plugin->whoisRaAttributes($row->ra_email, $out);
  338.                                                 }
  339.                                         }
  340.                                 }
  341.                                 $out[] = "ra-status: Information unavailable"; // DO NOT TRANSLATE!
  342.                         }
  343.                 }
  344.         }
  345. }
  346.  
  347. // Step 2: Format output
  348.  
  349. if ($format == 'text') {
  350.         header('Content-Type:text/plain; charset=UTF-8');
  351.  
  352.         $longest_key = 0;
  353.         foreach ($out as $line) {
  354.                 $longest_key = max($longest_key, strlen(trim(explode(':',$line,2)[0])));
  355.         }
  356.  
  357.         ob_start();
  358.  
  359.         //echo '% ' . str_repeat('*', OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80)-2)."\r\n";
  360.  
  361.         foreach ($out as $line) {
  362.                 if (trim($line) == '') {
  363.                         echo "\r\n";
  364.                         continue;
  365.                 }
  366.  
  367.                 $ary = explode(':', $line, 2);
  368.  
  369.                 $key = trim($ary[0]);
  370.  
  371.                 $value = isset($ary[1]) ? trim($ary[1]) : '';
  372.  
  373.                 // Normalize line-breaks to \r\n, otherwise mb_wordwrap won't work correctly
  374.                 $value = str_replace("\r\n", "\n", $value);
  375.                 $value = str_replace("\r", "\n", $value);
  376.                 $value = str_replace("\n", "\r\n", $value);
  377.  
  378.                 $value = mb_wordwrap($value, OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80) - $longest_key - strlen(':') - OIDplus::config()->getValue('webwhois_output_format_spacer', 2), "\r\n");
  379.                 $value = str_replace("\r\n", "\r\n$key:".str_repeat(' ', $longest_key-strlen($key)) . str_repeat(' ', OIDplus::config()->getValue('webwhois_output_format_spacer', 2)), $value);
  380.  
  381.                 if (!empty($value)) {
  382.                         echo $key.':' . str_repeat(' ', $longest_key-strlen($key)) . str_repeat(' ', OIDplus::config()->getValue('webwhois_output_format_spacer', 2)) . $value . "\r\n";
  383.                 }
  384.         }
  385.  
  386.         //echo '% ' . str_repeat('*', OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80)-2)."\r\n";
  387.  
  388.         $cont = ob_get_contents();
  389.         ob_end_clean();
  390.  
  391.         echo $cont;
  392.  
  393.         if (OIDplus::getPkiStatus()) {
  394.                 $signature = '';
  395.                 if (@openssl_sign($cont, $signature, OIDplus::getSystemPrivateKey())) {
  396.                         $signature = base64_encode($signature);
  397.                         $signature = mb_wordwrap($signature, OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80) - strlen('% '), "\r\n", true);
  398.                         $signature = "% -----BEGIN RSA SIGNATURE-----\r\n".
  399.                                      preg_replace('/^/m', '% ', $signature)."\r\n".
  400.                                      "% -----END RSA SIGNATURE-----\r\n";
  401.                         echo $signature;
  402.                 }
  403.         }
  404. }
  405.  
  406. if ($format == 'json') {
  407.         $ary = array();
  408.  
  409.         $current_section = array();
  410.         $ary[] = &$current_section;
  411.  
  412.         foreach ($out as $line) {
  413.                 if ($line == '') {
  414.                         unset($current_section);
  415.                         $current_section = array();
  416.                         $ary[] = &$current_section;
  417.                 } else {
  418.                         list($key,$val) = explode(':', $line, 2);
  419.                         $val = trim($val);
  420.                         if (!empty($val)) {
  421.                                 if (!isset($current_section[$key])) {
  422.                                         $current_section[$key] = $val;
  423.                                 } elseif (is_array($current_section[$key])) {
  424.                                         $current_section[$key][] = $val;
  425.                                 } else {
  426.                                         $current_section[$key] = array($current_section[$key], $val);
  427.                                 }
  428.                         }
  429.                 }
  430.         }
  431.         $ary = array(
  432.                 // https://code.visualstudio.com/docs/languages/json#_mapping-in-the-json
  433.                 // Note that this syntax is VS Code-specific and not part of the JSON Schema specification.
  434.                 //'$schema' => 'https://oidplus.viathinksoft.com/oidplus/plugins/publicPages/100_whois/whois/json_schema.json',
  435.                 '$schema' => JSON_SCHEMA,
  436.  
  437.                 // we need this NAMED root, otherwise PHP will name the sections "0", "1", "2" if the array is not sequencial (e.g. because "signature" is added)
  438.                 'oidip' => $ary
  439.         );
  440.  
  441.         $json = json_encode($ary);
  442.  
  443.         if (OIDplus::getPkiStatus()) {
  444.                 require_once __DIR__.'/json/security.inc.php';
  445.                 $json = oidplus_json_sign($json, OIDplus::getSystemPrivateKey(), OIDplus::getSystemPublicKey());
  446.         }
  447.  
  448.         // Good JSON schema validator here: https://www.jsonschemavalidator.net
  449.         header('Content-Type:application/json; charset=UTF-8');
  450.         echo $json;
  451. }
  452.  
  453. if ($format == 'xml') {
  454.         $xml = '<oidip><section>';
  455.         foreach ($out as $line) {
  456.                 if ($line == '') {
  457.                         $xml .= '</section><section>';
  458.                 } else {
  459.                         list($key,$val) = explode(':', $line, 2);
  460.                         $val = trim($val);
  461.                         if (!empty($val)) {
  462.                                 $xml .= "<$key>".htmlspecialchars($val)."</$key>";
  463.                         }
  464.                 }
  465.         }
  466.         $xml .= '</section></oidip>';
  467.  
  468.         $xml = preg_replace('@<section><(.+)>(.+)</section>@ismU', '<\\1Section><\\1>\\2</\\1Section>', $xml);
  469.  
  470.         // Good XSD validator here: https://www.liquid-technologies.com/online-xsd-validator
  471.         $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'.
  472.                '<root xmlns="'.XML_URN.'"'.
  473.                '      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'.
  474.                '      xsi:schemaLocation="'.XML_URN.' '.XML_URN_URL.'">'.
  475.                $xml.
  476.                '</root>';
  477.  
  478.         if (OIDplus::getPkiStatus()) {
  479.                 require_once __DIR__.'/xml/security.inc.php';
  480.                 $xml = oidplus_xml_sign($xml, OIDplus::getSystemPrivateKey(), OIDplus::getSystemPublicKey());
  481.         }
  482.  
  483.         header('Content-Type:application/xml; charset=UTF-8');
  484.         echo $xml;
  485. }
  486.  
  487. # ---
  488.  
  489. function show_asn1_appendix($id) {
  490.         if (substr($id,0,4) === 'oid:') {
  491.                 $appendix_asn1ids = array();
  492.                 $res3 = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($id));
  493.                 while ($row3 = $res3->fetch_object()) {
  494.                         $appendix_asn1ids[] = $row3->name;
  495.                 }
  496.  
  497.                 $appendix = implode(', ', $appendix_asn1ids);
  498.                 if (!empty($appendix)) $appendix = " ($appendix)";
  499.         } else {
  500.                 $appendix = '';
  501.         }
  502.         return $appendix;
  503. }
  504.  
  505. function is_root($id) {
  506.         return empty(explode(':',$id,2)[1]);
  507. }
  508.  
  509. function authTokenAccepted($content, $authTokens) {
  510.         foreach ($authTokens as $token) {
  511.                 if (OIDplusPagePublicWhois::genWhoisAuthToken($content) == $token) return true;
  512.         }
  513.         return false;
  514. }
  515.  
  516. function allowObjectView($obj, $authTokens) {
  517.         // Master auth token
  518.         $authToken = trim(OIDplus::config()->getValue('whois_auth_token'));
  519.         if (empty($authToken)) $authToken = false;
  520.         if ($authToken && in_array($authToken, $authTokens)) return true;
  521.  
  522.         // Per-OID auth tokens
  523.         $curid = $obj->nodeId();
  524.         while (($res = OIDplus::db()->query("select parent, confidential from ###objects where id = ?", array($curid)))->any()) {
  525.                 $row = $res->fetch_array();
  526.                 // Example: You have an auth Token for 2.999.1.2.3
  527.                 // This allows you to view 2.999.1.2.3 and all of its children,
  528.                 // as long as they are not confidential (then you need their auth token).
  529.                 // 2, 2.999, 2.999.1 and 2.999.1.2 are visible,
  530.                 // (because their existence is now obvious).
  531.                 if ($row['confidential'] && !authTokenAccepted($curid, $authTokens)) return false;
  532.                 $curid = $row['parent'];
  533.         }
  534.  
  535.         // Allow
  536.         return true;
  537. }
  538.  
  539. function allowRAView($row, $authTokens) {
  540.         // Master auth token
  541.         $authToken = trim(OIDplus::config()->getValue('whois_auth_token'));
  542.         if (empty($authToken)) $authToken = false;
  543.         if ($authToken && in_array($authToken, $authTokens)) return true;
  544.  
  545.         // Per-RA auth tokens
  546.         if ($row->privacy && !authTokenAccepted('ra:'.$row->ra_name, $authTokens)) return false;
  547.  
  548.         // Allow
  549.         return true;
  550. }
  551.