Subversion Repositories oidplus

Rev

Rev 731 | Rev 760 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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