Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
941 daniel-mar 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
if (!defined('INSIDE_OIDPLUS')) die();
21
 
22
class OIDplusOIDIP {
23
 
24
        protected $XML_SCHEMA_URN;
25
        protected $XML_SCHEMA_URL;
26
        protected $JSON_SCHEMA_URN;
27
        protected $JSON_SCHEMA_URL;
28
 
29
        public function __construct() {
30
                // NOTES:
31
                // - XML_SCHEMA_URN must be equal to the string in the .xsd file!
32
                // - the schema file names and draft version are also written in OIDplusPagePublicWhois.class.php
33
                $this->XML_SCHEMA_URN  = 'urn:ietf:id:draft-viathinksoft-oidip-04';
34
                $this->XML_SCHEMA_URL  = OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE).'whois/draft-viathinksoft-oidip-04.xsd';
35
                $this->JSON_SCHEMA_URN = 'urn:ietf:id:draft-viathinksoft-oidip-04';
36
                $this->JSON_SCHEMA_URL = OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE).'whois/draft-viathinksoft-oidip-04.json';
37
        }
38
 
39
        public function oidipQuery($query) {
40
 
41
                $out_type = null;
42
                $out_content = '';
43
 
44
                // Split input into query and arguments
45
                $chunks = explode('$', $query);
46
                $query = array_shift($chunks);
47
                $original_query = $query;
48
                $arguments = array();
49
                foreach ($chunks as $chunk) {
50
                        if (strtolower(substr($chunk,0,5)) !== 'auth=') $original_query .= '$'.$chunk;
51
 
52
                        if (strpos($chunk,'=') === false) continue;
53
                        $tmp = explode('=',$chunk,2);
54
 
55
                        //if (!preg_match('@^[a-z0-9]+$@', $tmp[0], $m)) continue; // be strict with the names. They must be lowercase (TODO: show "Service error" message?)
56
                        $tmp[0] = strtolower($tmp[0]); // be tolerant instead
57
 
58
                        $arguments[$tmp[0]] = $tmp[1];
59
                }
60
 
61
                $query = str_replace('oid:.', 'oid:', $query); // allow leading dot
62
 
63
                if (isset($arguments['format'])) {
64
                        $format = $arguments['format'];
65
                } else {
66
                        $format = 'text'; // default
67
                }
68
 
69
                if (isset($arguments['auth'])) {
70
                        $authTokens = explode(',', $arguments['auth']);
71
                } else {
72
                        $authTokens = array();
73
                }
74
 
75
                // $lang= is not implemented, since we don't know in which language the OID descriptions are written by the site operator
76
                /*
77
                if (isset($arguments['lang'])) {
78
                        $languages = explode(',', $arguments['lang']);
79
                } else {
80
                        $languages = array();
81
                }
82
                */
83
 
84
                $unimplemented_format = ($format != 'text') && ($format != 'json') && ($format != 'xml');
85
                if ($unimplemented_format) {
86
                        $format = 'text';
87
                }
88
 
89
                // Step 1: Collect data
90
 
91
                $out = array();
92
 
93
                // ATTENTION: THE ORDER IS IMPORTANT FOR THE XML VALIDATION!
94
                // The order of the RFC is the same as in the XSD
95
                $out[] = $this->_oidip_attr('query', $original_query);
96
 
97
                $query = OIDplus::prefilterQuery($query, false);
98
 
99
                if ($unimplemented_format) {
100
                        $out[] = $this->_oidip_attr('result', 'Service error');
101
                        $out[] = $this->_oidip_attr('message', 'Format is not implemented');
102
                        $out[] = $this->_oidip_attr('lang', 'en-US');
103
                } else {
104
 
105
                        $distance = null;
106
                        $found = null;
107
 
971 daniel-mar 108
                        $obj = OIDplusObject::parse($query);
941 daniel-mar 109
 
110
                        $only_wellknown_ids_found = false;
111
                        $continue = false;
112
 
113
                        if (!$obj) {
971 daniel-mar 114
                                // Object type not known or invalid syntax of $query
941 daniel-mar 115
                                $out[] = $this->_oidip_attr('result', 'Not found'); // DO NOT TRANSLATE!
116
                                $continue = false;
117
                        } else {
971 daniel-mar 118
 
119
                                $query = $obj->nodeId(); // normalize
120
 
941 daniel-mar 121
                                $obj = null;
122
                                $distance = 0;
123
 
124
                                $init_query = $query;
125
                                while (true) {
971 daniel-mar 126
 
127
                                        $obj_test = OIDplusObject::findFitting($query);
128
                                        if ($obj_test) {
129
                                                $obj = $obj_test;
130
                                        } else {
131
                                                $alts = OIDplusPagePublicObjects::getAlternativesForQuery($query);
132
                                                foreach ($alts as $alt) {
133
                                                        $obj_test = OIDplusObject::findFitting($alt);
134
                                                        if ($obj_test) {
135
                                                                $query = $alt;
136
                                                                $obj = $obj_test;
137
                                                        }
138
                                                }
139
                                        }
140
                                        if ($obj) {
941 daniel-mar 141
                                                if ($distance > 0) {
142
                                                        $out[] = $this->_oidip_attr('result', 'Not found; superior object found'); // DO NOT TRANSLATE!
143
                                                        $out[] = $this->_oidip_attr('distance', $distance); // DO NOT TRANSLATE
144
                                                } else {
145
                                                        $out[] = $this->_oidip_attr('result', 'Found'); // DO NOT TRANSLATE!
146
                                                }
147
                                                $continue = true;
148
                                                break;
149
                                        }
150
 
151
                                        if (substr($query,0,4) === 'oid:') {
152
                                                $query_prev = $query;
153
                                                $query = 'oid:'.oid_up(explode(':',$query,2)[1]);
154
                                                if ($query == $query_prev) break;
155
                                                $distance++;
156
                                        } else {
157
                                                // getParent() will find the parent which DOES exist in the DB.
158
                                                // It does not need to be the direct parent (like ->one_up() does)
159
                                                $obj = OIDplusObject::parse($query)->getParent(); // For objects, we assume that they are parents of each other
160
                                                if ($obj) {
161
                                                        $distance = $obj->distance($query);
975 daniel-mar 162
                                                        assert(OIDplusObject::findFitting($query));
941 daniel-mar 163
 
164
                                                        $query = $obj->nodeId();
165
                                                }
166
 
167
                                                if ($distance > 0) {
168
                                                        $out[] = $this->_oidip_attr('result', 'Not found; superior object found'); // DO NOT TRANSLATE!
169
                                                        $out[] = $this->_oidip_attr('distance', $distance); // DO NOT TRANSLATE
170
                                                }
171
                                                $continue = true;
172
 
173
                                                break;
174
                                        }
175
                                }
176
 
177
                                if ((substr($query,0,4) === 'oid:') && (!$obj)) {
178
                                        $query = $init_query;
179
                                        $distance = 0;
180
                                        while (true) {
975 daniel-mar 181
                                                // Checks if there is any identifier (so it is a well-known OID)
182
                                                $res_test = OIDplus::db()->query("select * from ###asn1id where oid = ? union select * from ###iri where oid = ?", array($query, $query));
183
                                                if ($res_test->any()) {
941 daniel-mar 184
                                                        $obj = OIDplusObject::parse($query);
185
                                                        if ($distance > 0) {
186
                                                                $out[] = $this->_oidip_attr('result', 'Not found; superior object found'); // DO NOT TRANSLATE!
187
                                                                $out[] = $this->_oidip_attr('distance', $distance); // DO NOT TRANSLATE
188
                                                        } else {
189
                                                                $out[] = $this->_oidip_attr('result', 'Found'); // DO NOT TRANSLATE!
190
                                                        }
191
                                                        $only_wellknown_ids_found = true; // Information partially available
192
                                                        $continue = true;
193
                                                        break;
194
                                                }
195
                                                $query_prev = $query;
196
                                                $query = 'oid:'.oid_up(explode(':',$query,2)[1]);
197
                                                if ($query == $query_prev) break;
198
                                                $distance++;
199
                                        }
200
                                }
201
 
202
                                if (!$obj) {
203
                                        $out[] = $this->_oidip_attr('result', 'Not found'); // DO NOT TRANSLATE!
204
                                        $continue = false;
205
                                }
206
 
207
                                $found = $distance == 0;
208
                        }
209
 
210
                        if ($continue) {
211
                                $out[] = '';
212
 
213
                                // ATTENTION: THE ORDER IS IMPORTANT FOR THE XML VALIDATION!
214
                                // The order of the RFC is the same as in the XSD
215
                                $out[] = $this->_oidip_attr('object', $query); // DO NOT TRANSLATE!
216
                                if (!$this->allowObjectView($obj, $authTokens)) {
217
                                        $out[] = $this->_oidip_attr('status', 'Information unavailable'); // DO NOT TRANSLATE!
218
                                        $out[] = $this->_oidip_attr('attribute', 'confidential'); // DO NOT TRANSLATE!
219
                                } else {
220
                                        if ($only_wellknown_ids_found) {
221
                                                $out[] = $this->_oidip_attr('status', 'Information partially available'); // DO NOT TRANSLATE!
222
                                        } else {
223
                                                $out[] = $this->_oidip_attr('status', 'Information available'); // DO NOT TRANSLATE!
224
                                        }
225
 
226
                                        // $this->_oidip_attr('lang', ...); // not implemented (since we don't know the language of the texts written by the page operator)
227
 
975 daniel-mar 228
                                        if ($obj) {
229
                                                $out[] = $this->_oidip_attr('name', $obj->getTitle()); // DO NOT TRANSLATE!
941 daniel-mar 230
 
975 daniel-mar 231
                                                $cont = $obj->getDescription();
941 daniel-mar 232
                                                $cont = preg_replace('@<a[^>]+href\s*=\s*["\']([^\'"]+)["\'][^>]*>(.+)<\s*/\s*a\s*>@ismU', '\2 (\1)', $cont);
233
                                                $cont = preg_replace('@<br.*>@', "\n", $cont);
234
                                                $cont = preg_replace('@\\n+@', "\n", $cont);
235
                                                $out[] = $this->_oidip_attr('description', trim(html_entity_decode(strip_tags($cont)))); // DO NOT TRANSLATE!
236
                                        }
237
 
238
                                        // $this->_oidip_attr('information', ...); Not used. Contains additional information, e.g. Management Information Base (MIB) definitions.
239
 
240
                                        if ($only_wellknown_ids_found) {
241
                                                if (substr($query,0,4) === 'oid:') {
242
                                                        // Since it is well-known, oid-info.com will most likely have it described
243
                                                        $out[] = $this->_oidip_attr('url', 'http://www.oid-info.com/get/'.$obj->nodeId(false));
244
                                                }
245
                                        } else {
246
                                                $out[] = $this->_oidip_attr('url', OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE).'?goto='.urlencode($obj->nodeId(true)));
247
                                        }
248
 
249
                                        if (substr($query,0,4) === 'oid:') {
250
                                                $out[] = $this->_oidip_attr('asn1-notation', $obj->getAsn1Notation(false)); // DO NOT TRANSLATE!
251
                                                $out[] = $this->_oidip_attr('iri-notation', $obj->getIriNotation(false)); // DO NOT TRANSLATE!
252
 
975 daniel-mar 253
                                                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($obj->nodeId()));
254
                                                while ($row_asn = $res_asn->fetch_object()) {
255
                                                        $out[] = $this->_oidip_attr('identifier', $row_asn->name); // DO NOT TRANSLATE!
941 daniel-mar 256
                                                }
257
 
975 daniel-mar 258
                                                $res_asn = OIDplus::db()->query("select * from ###asn1id where standardized = ? and oid = ?", array(true, $obj->nodeId()));
259
                                                while ($row_asn = $res_asn->fetch_object()) {
260
                                                        $out[] = $this->_oidip_attr('standardized-id', $row_asn->name); // DO NOT TRANSLATE!
941 daniel-mar 261
                                                }
262
 
975 daniel-mar 263
                                                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ?", array($obj->nodeId()));
264
                                                while ($row_iri = $res_iri->fetch_object()) {
265
                                                        $out[] = $this->_oidip_attr('unicode-label', $row_iri->name); // DO NOT TRANSLATE!
941 daniel-mar 266
                                                }
267
 
975 daniel-mar 268
                                                $res_iri = OIDplus::db()->query("select * from ###iri where longarc = ? and oid = ?", array(true, $obj->nodeId()));
269
                                                while ($row_iri = $res_iri->fetch_object()) {
270
                                                        $out[] = $this->_oidip_attr('long-arc', $row_iri->name); // DO NOT TRANSLATE!
941 daniel-mar 271
                                                }
272
                                        }
273
 
274
                                        // $this->_oidip_attr('oidip-service', ...); Not used.
275
 
276
                                        if ($obj->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
277
                                                // Also ask $obj for extra attributes:
278
                                                // This way we could add various additional information, e.g. IPv4/6 range analysis, interpretation of GUID, etc.
279
                                                $obj->whoisObjectAttributes($obj->nodeId(), $out);
280
                                        }
281
 
282
                                        foreach (OIDplus::getPagePlugins() as $plugin) {
283
                                                if ($plugin->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
284
                                                        $plugin->whoisObjectAttributes($obj->nodeId(), $out);
285
                                                }
286
                                        }
287
 
288
                                        if ($obj->isConfidential()) { // yes, we use isConfidential() instead of $this->allowObjectView()!
289
                                                $out[] = $this->_oidip_attr('attribute', 'confidential'); // DO NOT TRANSLATE!
290
                                        }
291
 
292
                                        if (substr($query,0,4) === 'oid:') {
293
                                                $sParent = 'oid:'.oid_up(explode(':',$query,2)[1]);
294
 
295
                                                $objTest = OIDplusObject::parse($sParent);
296
                                                if ($this->allowObjectView($objTest, $authTokens)) {
297
                                                        $out[] = $this->_oidip_attr('parent', $sParent.$this->show_asn1_appendix($sParent)); // DO NOT TRANSLATE!
298
                                                } else {
299
                                                        $out[] = $this->_oidip_attr('parent', $sParent); // DO NOT TRANSLATE!
300
                                                }
975 daniel-mar 301
                                        } else {
302
                                                $sParent = $obj->getParent();
303
                                                if ($obj && !empty($sParent) && (!$this->is_root($sParent))) {
304
                                                        $out[] = $this->_oidip_attr('parent', $sParent); // DO NOT TRANSLATE!
305
                                                }
941 daniel-mar 306
                                        }
307
 
975 daniel-mar 308
                                        $res_children = OIDplus::db()->query("select * from ###objects where parent = ? order by ".OIDplus::db()->natOrder('id'), array($obj->nodeId()));
309
                                        while ($row_children = $res_children->fetch_object()) {
310
                                                $objTest = OIDplusObject::parse($row_children->id);
941 daniel-mar 311
                                                if ($this->allowObjectView($objTest, $authTokens)) {
975 daniel-mar 312
                                                        $out[] = $this->_oidip_attr('subordinate', $row_children->id.$this->show_asn1_appendix($row_children->id)); // DO NOT TRANSLATE!
941 daniel-mar 313
                                                } else {
975 daniel-mar 314
                                                        $out[] = $this->_oidip_attr('subordinate', $row_children->id); // DO NOT TRANSLATE!
941 daniel-mar 315
                                                }
316
                                        }
317
 
975 daniel-mar 318
                                        if ($obj) {
319
                                                if ($tim = $obj->getCreatedTime()) $out[] = $this->_oidip_attr('created', date('Y-m-d H:i:s', strtotime($tim))); // DO NOT TRANSLATE!
320
                                                if ($tim = $obj->getUpdatedTime()) $out[] = $this->_oidip_attr('updated', date('Y-m-d H:i:s', strtotime($tim))); // DO NOT TRANSLATE!
941 daniel-mar 321
                                        }
322
 
323
                                        $out[] = '';
324
 
325
                                        // ATTENTION: THE ORDER IS IMPORTANT FOR THE XML VALIDATION!
326
                                        // The order of the RFC is the same as in the XSD
975 daniel-mar 327
                                        $res_ra = OIDplus::db()->query("select * from ###ra where email = ?", array($obj ? $obj->getRaMail() : ''));
328
                                        if ($row_ra = $res_ra->fetch_object()) {
329
                                                $out[] = $this->_oidip_attr('ra', (!empty($row_ra->ra_name) ? $row_ra->ra_name : (!empty($row_ra->email) ? $row_ra->email : /*_L*/('Unknown')))); // DO NOT TRANSLATE!
941 daniel-mar 330
 
975 daniel-mar 331
                                                if (!$this->allowRAView($row_ra, $authTokens)) {
941 daniel-mar 332
                                                        $out[] = $this->_oidip_attr('ra-status', 'Information partially available'); // DO NOT TRANSLATE!
333
                                                } else {
334
                                                        $out[] = $this->_oidip_attr('ra-status', 'Information available'); // DO NOT TRANSLATE!
335
                                                }
336
 
337
                                                // $this->_oidip_attr('ra-lang', ...); // not implemented (since we don't know the language of the texts written by the page operator)
338
 
339
                                                $tmp = array();
975 daniel-mar 340
                                                if (!empty($row_ra->office)) $tmp[] = $row_ra->office;
341
                                                if (!empty($row_ra->organization)) $tmp[] = $row_ra->organization;
941 daniel-mar 342
                                                $tmp = implode(', ', $tmp);
343
 
975 daniel-mar 344
                                                $out[] = $this->_oidip_attr('ra-contact-name', $row_ra->personal_name.(!empty($tmp) ? " ($tmp)" : '')); // DO NOT TRANSLATE!
345
                                                if (!$this->allowRAView($row_ra, $authTokens)) {
346
                                                        if (!empty($row_ra->street) || !empty($row_ra->zip_town) || !empty($row_ra->country)) {
941 daniel-mar 347
                                                                $out[] = $this->_oidip_attr('ra-address', /*_L*/('(redacted)')); // DO NOT TRANSLATE!
348
                                                        }
975 daniel-mar 349
                                                        $out[] = $this->_oidip_attr('ra-phone', (!empty($row_ra->phone) ? /*_L*/('(redacted)') : '')); // DO NOT TRANSLATE!
350
                                                        $out[] = $this->_oidip_attr('ra-mobile', (!empty($row_ra->mobile) ? /*_L*/('(redacted)') : '')); // DO NOT TRANSLATE!
351
                                                        $out[] = $this->_oidip_attr('ra-fax', (!empty($row_ra->fax) ? /*_L*/('(redacted)') : '')); // DO NOT TRANSLATE!
941 daniel-mar 352
                                                } else {
353
                                                        $address = array();
975 daniel-mar 354
                                                        if (!empty($row_ra->street))   $address[] = $row_ra->street; // DO NOT TRANSLATE!
355
                                                        if (!empty($row_ra->zip_town)) $address[] = $row_ra->zip_town; // DO NOT TRANSLATE!
356
                                                        if (!empty($row_ra->country))  $address[] = $row_ra->country; // DO NOT TRANSLATE!
941 daniel-mar 357
                                                        if (count($address) > 0) $out[] = $this->_oidip_attr('ra-address', implode("\n",$address));
975 daniel-mar 358
                                                        $out[] = $this->_oidip_attr('ra-phone', $row_ra->phone); // DO NOT TRANSLATE!
359
                                                        $out[] = $this->_oidip_attr('ra-mobile', $row_ra->mobile); // DO NOT TRANSLATE!
360
                                                        $out[] = $this->_oidip_attr('ra-fax', $row_ra->fax); // DO NOT TRANSLATE!
941 daniel-mar 361
                                                }
975 daniel-mar 362
                                                $out[] = $this->_oidip_attr('ra-email', $obj->getRaMail()); // DO NOT TRANSLATE!
941 daniel-mar 363
 
364
                                                // $this->_oidip_attr('ra-url', ...); Not used.
365
 
975 daniel-mar 366
                                                $ra = new OIDplusRA($obj->getRaMail());
941 daniel-mar 367
                                                if ($ra->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
975 daniel-mar 368
                                                        $ra->whoisRaAttributes($obj->getRaMail(), $out); /** @phpstan-ignore-line */
941 daniel-mar 369
                                                }
370
 
371
                                                foreach (OIDplus::getPagePlugins() as $plugin) {
372
                                                        if ($plugin->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
975 daniel-mar 373
                                                                $plugin->whoisRaAttributes($obj->getRaMail(), $out);
941 daniel-mar 374
                                                        }
375
                                                }
376
 
975 daniel-mar 377
                                                // yes, we use row_ra->privacy() instead of $this->allowRAView(), becuase $this->allowRAView=true if auth token is given; and we want to inform the person that they content they are viewing is confidential!
378
                                                if ($row_ra->privacy) {
941 daniel-mar 379
                                                        $out[] = $this->_oidip_attr('ra-attribute', 'confidential'); // DO NOT TRANSLATE!
380
                                                }
381
 
975 daniel-mar 382
                                                if ($row_ra->registered) $out[] = $this->_oidip_attr('ra-created', date('Y-m-d H:i:s', strtotime($row_ra->registered))); // DO NOT TRANSLATE!
383
                                                if ($row_ra->updated)    $out[] = $this->_oidip_attr('ra-updated', date('Y-m-d H:i:s', strtotime($row_ra->updated))); // DO NOT TRANSLATE!
941 daniel-mar 384
                                        } else {
975 daniel-mar 385
                                                $out[] = $this->_oidip_attr('ra', ($obj && !empty($obj->getRaMail()) ? $obj->getRaMail() : /*_L*/('Unknown'))); // DO NOT TRANSLATE!
386
                                                if ($obj) {
941 daniel-mar 387
                                                        foreach (OIDplus::getPagePlugins() as $plugin) {
388
                                                                if ($plugin->implementsFeature('1.3.6.1.4.1.37476.2.5.2.3.4')) {
975 daniel-mar 389
                                                                        $plugin->whoisRaAttributes($obj->getRaMail(), $out);
941 daniel-mar 390
                                                                }
391
                                                        }
392
                                                }
393
                                                $out[] = $this->_oidip_attr('ra-status', 'Information unavailable'); // DO NOT TRANSLATE!
394
                                        }
395
                                }
396
                        }
397
                }
398
 
399
                // Upgrade legacy $out to extended $out format
400
 
401
                $this->_oidip_newout_format($out);
402
 
403
                // Trim all values
404
 
405
                foreach ($out as &$tmp) {
406
                        $tmp['value'] = trim($tmp['value']);
407
                }
408
 
409
                // Step 2: Format output
410
 
411
                if ($format == 'text') {
412
                        $out_type = 'text/plain; charset=UTF-8';
413
 
414
                        $longest_key = 0;
415
                        foreach ($out as $data) {
416
                                $longest_key = max($longest_key, strlen(trim($data['name'])));
417
                        }
418
 
419
                        ob_start();
420
 
421
                        //$out_content .= '% ' . str_repeat('*', OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80)-2)."\r\n";
422
 
423
                        foreach ($out as $data) {
424
                                if ($data['name'] == '') {
425
                                        $out_content .= "\r\n";
426
                                        continue;
427
                                }
428
 
429
                                $key = $data['name'];
430
                                $value = $data['value'];
431
 
432
                                // Normalize line-breaks to \r\n, otherwise mb_wordwrap won't work correctly
433
                                $value = str_replace("\r\n", "\n", $value);
434
                                $value = str_replace("\r", "\n", $value);
435
                                $value = str_replace("\n", "\r\n", $value);
436
 
437
                                $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");
438
                                $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);
439
 
440
                                if (!empty($value)) {
441
                                        $out_content .= $key.':' . str_repeat(' ', $longest_key-strlen($key)) . str_repeat(' ', OIDplus::config()->getValue('webwhois_output_format_spacer', 2)) . $value . "\r\n";
442
                                }
443
                        }
444
 
445
                        //$out_content .= '% ' . str_repeat('*', OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80)-2)."\r\n";
446
 
447
                        $cont = ob_get_contents();
448
                        ob_end_clean();
449
 
450
                        $out_content .= $cont;
451
 
452
                        if (OIDplus::getPkiStatus()) {
453
                                $signature = '';
454
                                if (@openssl_sign($cont, $signature, OIDplus::getSystemPrivateKey())) {
455
                                        $signature = base64_encode($signature);
456
                                        $signature = mb_wordwrap($signature, OIDplus::config()->getValue('webwhois_output_format_max_line_length', 80) - strlen('% '), "\r\n", true);
457
                                        $signature = "% -----BEGIN RSA SIGNATURE-----\r\n".
458
                                                     preg_replace('/^/m', '% ', $signature)."\r\n".
459
                                                     "% -----END RSA SIGNATURE-----\r\n";
460
                                        $out_content .= $signature;
461
                                }
462
                        }
463
                }
464
 
465
                if ($format == 'json') {
466
                        $ary = array();
467
 
468
                        $current_section = array();
469
                        $ary[] = &$current_section;
470
 
471
                        foreach ($out as $data) {
472
                                if ($data['name'] == '') {
473
                                        unset($current_section);
474
                                        $current_section = array();
475
                                        $ary[] = &$current_section;
476
                                } else {
477
                                        $key = $data['name'];
478
                                        $val = trim($data['value']);
479
                                        if (!empty($val)) {
480
                                                if (!isset($current_section[$key])) {
481
                                                        $current_section[$key] = $val;
482
                                                } elseif (is_array($current_section[$key])) {
483
                                                        $current_section[$key][] = $val;
484
                                                } else {
485
                                                        $current_section[$key] = array($current_section[$key], $val);
486
                                                }
487
                                        }
488
                                }
489
                        }
490
 
491
                        // Change $ary=[["query",...], ["object",...], ["ra",...]]
492
                        // to     $bry=["querySection"=>["query",...], "objectsection"=>["object",...], "raSection"=>["ra",...]]
493
                        $bry = array();
494
                        foreach ($ary as $cry) {
495
                                $dry = array_keys($cry);
496
                                if (count($dry) == 0) continue;
497
                                $bry[$dry[0].'Section'] = $cry; /** @phpstan-ignore-line */ // PHPStan thinks that count($dry) is always 0
498
                        }
499
 
500
                        // Remove 'ra-', 'ra1-', ... field prefixes from JSON (the prefix is only for the text view)
501
                        foreach ($bry as $section_name => &$cry) { /** @phpstan-ignore-line */ // PHPStan thinks that $bry is empty
502
                                if (preg_match('@^(ra\d*)\\Section@', $section_name, $m)) {
503
                                        $ra_id = str_replace('Section', '', $section_name);
504
                                        $dry = array();
505
                                        foreach ($cry as $name => $val) {
506
                                                if ($name == $ra_id) $name = 'ra'; // First field is always 'ra' even in 'ra1Section' etc.
507
                                                $name = preg_replace('@^('.preg_quote($ra_id,'@').')\\-@', '', $name);
508
                                                $dry[$name] = $val;
509
                                        }
510
                                        $cry = $dry;
511
                                }
512
                        }
513
 
514
                        $ary = array(
515
                                // We use the URN here, because $id of the schema also uses the URN
516
                                '$schema' => $this->JSON_SCHEMA_URN,
517
 
518
                                // 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)
519
                                'oidip' => $bry
520
                        );
521
 
522
                        $json = json_encode($ary);
523
 
524
                        if (OIDplus::getPkiStatus()) {
525
                                try {
526
                                        require_once __DIR__.'/whois/json/security.inc.php';
527
                                        $json = oidplus_json_sign($json, OIDplus::getSystemPrivateKey(), OIDplus::getSystemPublicKey());
528
                                } catch (Exception $e) {
529
                                        // die($e->getMessage());
530
                                }
531
                        }
532
 
533
                        // Good JSON schema validator here: https://www.jsonschemavalidator.net
534
                        $out_type = 'application/json; charset=UTF-8';
535
                        $out_content .= $json;
536
                }
537
 
538
                if ($format == 'xml') {
539
                        $xml_oidip = '<oidip><section>';
540
                        foreach ($out as $data) {
541
                                if ($data['name'] == '') {
542
                                        $xml_oidip .= '</section><section>';
543
                                } else {
544
                                        if ($data['xmlns'] != '') {
545
                                                $key = $data['xmlns'].':'.$data['name'];
546
                                        } else {
547
                                                $key = $data['name'];
548
                                        }
549
                                        $val = trim($data['value']);
550
                                        if (!empty($val)) {
551
                                                if (strpos($val,"\n") !== false) {
552
                                                        $val = str_replace(']]>', ']]]]><![CDATA[>', $val); // Escape ']]>'
553
                                                        $xml_oidip .= "<$key><![CDATA[$val]]></$key>";
554
                                                } else {
555
                                                        $xml_oidip .= "<$key>".htmlspecialchars($val, ENT_XML1)."</$key>";
556
                                                }
557
                                        }
558
                                }
559
                        }
560
                        $xml_oidip .= '</section></oidip>';
561
 
562
                        $xml_oidip = preg_replace('@<section><(.+)>(.+)</section>@ismU', '<\\1Section><\\1>\\2</\\1Section>', $xml_oidip);
563
 
564
                        // Remove 'ra-', 'ra1-', ... field prefixes from XML (the prefix is only for the text view)
565
                        $xml_oidip = preg_replace('@<(/{0,1})ra\\d*-@', '<\\1', $xml_oidip);
566
 
567
                        // <ra1Section><ra1>...</ra1> => <ra1Section><ra>...</ra>
568
                        $xml_oidip = preg_replace('@<ra(\\d+)Section><ra\\1>(.+)</ra\\1>@U', '<ra\\1Section><ra>\\2</ra>', $xml_oidip);
569
 
570
                        /* Debug:
571
                        $out_type = 'application/xml; charset=UTF-8';
572
                        $out_content .= $xml_oidip;
573
                        die();
574
                        */
575
 
576
                        // Very good XSD validator here: https://www.liquid-technologies.com/online-xsd-validator
577
                        // Also good: https://www.utilities-online.info/xsdvalidation (but does not accept &amp; or &quot; results in "Premature end of data in tag description line ...")
578
                        // Note:
579
                        // - These do not support XSD 1.1
580
                        // - You need to host http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd yourself, otherwise there will be a timeout in the validation!!!
581
 
582
                        $extra_schemas = array();
583
                        foreach ($out as $data) {
584
                                if (isset($data['xmlns']) && ($data['xmlns'] != '') && !isset($extra_schemas[$data['xmlns']])) {
585
                                        $extra_schemas[$data['xmlns']] = array($data['xmlschema'], $data['xmlschemauri']);
586
                                }
587
                        }
588
 
589
                        $xml  = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?'.'>'."\n";
590
                        $xml .= '<root xmlns="'.$this->XML_SCHEMA_URN.'"'."\n";
591
                        $xml .= '      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
592
                        foreach ($extra_schemas as $xmlns => list($schema,$schemauri)) {
593
                                $xml .= '      xmlns:'.$xmlns.'="'.$schema.'"'."\n";
594
                        }
595
                        $xml .= '      xsi:schemaLocation="'.$this->XML_SCHEMA_URN.' '.$this->XML_SCHEMA_URL;
596
                        foreach ($extra_schemas as $xmlns => list($schema,$schemauri)) {
597
                                $xml .= ' '.$schema.' '.$schemauri;
598
                        }
599
                        $xml .= '">'."\n";
600
                        $xml .= $xml_oidip."\n";
601
                        $xml .= '</root>';
602
 
603
                        if (!OIDplus::getPkiStatus()) {
604
                                $xml .= '<!-- Cannot add signature: OIDplus PKI is not initialized (OpenSSL missing?) -->';
605
                        } else if (!class_exists('DOMDocument')) {
606
                                $xml .= '<!-- Cannot add signature: "PHP-XML" extension not installed -->';
607
                        } else {
608
                                try {
609
                                        require_once __DIR__.'/whois/xml/security.inc.php';
610
                                        $xml = oidplus_xml_sign($xml, OIDplus::getSystemPrivateKey(), OIDplus::getSystemPublicKey());
611
                                } catch (Exception $e) {
612
                                        $xml .= '<!-- Cannot add signature: '.$e.' -->';
613
                                }
614
                        }
615
 
616
                        $out_type = 'application/xml; charset=UTF-8';
617
                        $out_content .= $xml;
618
                }
619
 
620
                return array($out_content, $out_type);
621
        }
622
 
623
        protected function show_asn1_appendix($id) {
624
                if (substr($id,0,4) === 'oid:') {
625
                        $appendix_asn1ids = array();
975 daniel-mar 626
                        $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($id));
627
                        while ($row_asn = $res_asn->fetch_object()) {
628
                                $appendix_asn1ids[] = $row_asn->name;
941 daniel-mar 629
                        }
630
 
631
                        $appendix = implode(', ', $appendix_asn1ids);
632
                        if (!empty($appendix)) $appendix = " ($appendix)";
633
                } else {
634
                        $appendix = '';
635
                }
636
                return $appendix;
637
        }
638
 
639
        protected function is_root($id) {
640
                return empty(explode(':',$id,2)[1]);
641
        }
642
 
643
        protected function authTokenAccepted($content, $authTokens) {
644
                foreach ($authTokens as $token) {
645
                        if (OIDplusPagePublicWhois::genWhoisAuthToken($content) == $token) return true;
646
                }
647
                return false;
648
        }
649
 
650
        protected function allowObjectView($obj, $authTokens) {
651
                // Master auth token (TODO: Have an object-master-token and a ra-master-token?)
652
                $authToken = trim(OIDplus::config()->getValue('whois_auth_token'));
653
                if (empty($authToken)) $authToken = false;
654
                if ($authToken && in_array($authToken, $authTokens)) return true;
655
 
656
                // Per-OID auth tokens
975 daniel-mar 657
 
941 daniel-mar 658
                $curid = $obj->nodeId();
975 daniel-mar 659
                while ($test_obj = OIDplusObject::findFitting($curid)) {
941 daniel-mar 660
                        // Example: You have an auth Token for 2.999.1.2.3
661
                        // This allows you to view 2.999.1.2.3 and all of its children,
662
                        // as long as they are not confidential (then you need their auth token).
663
                        // 2, 2.999, 2.999.1 and 2.999.1.2 are visible,
664
                        // (because their existence is now obvious).
975 daniel-mar 665
                        if ($test_obj->isConfidential() && !$this->authTokenAccepted($curid, $authTokens)) return false;
666
                        $curid = $test_obj->getParent();
941 daniel-mar 667
                }
668
 
669
                // Allow
670
                return true;
671
        }
672
 
975 daniel-mar 673
        protected function allowRAView($row_ra, $authTokens) {
941 daniel-mar 674
                // Master auth token (TODO: Have an object-master-token and a ra-master-token?)
675
                $authToken = trim(OIDplus::config()->getValue('whois_auth_token'));
676
                if (empty($authToken)) $authToken = false;
677
                if ($authToken && in_array($authToken, $authTokens)) return true;
678
 
679
                // Per-RA auth tokens
975 daniel-mar 680
                if ($row_ra->privacy && !$this->authTokenAccepted('ra:'.$row_ra->ra_name, $authTokens)) return false;
941 daniel-mar 681
 
682
                // Allow
683
                return true;
684
        }
685
 
686
        protected function _oidip_attr($name, $value) {
687
                return array(
688
                        'xmlns' => '',
689
                        'xmlschema' => '',
690
                        'xmlschemauri' => '',
691
                        'name' => $name,
692
                        'value' => $value
693
                );
694
        }
695
 
696
        protected function _oidip_newout_format(&$out) {
697
                foreach ($out as &$line) {
698
                        if (is_string($line)) {
699
                                $ary = explode(':', $line, 2);
700
                                $key = trim($ary[0]);
701
                                $value = isset($ary[1]) ? trim($ary[1]) : '';
702
 
703
                                $line = array(
704
                                        'xmlns' => '',
705
                                        'xmlschema' => '',
706
                                        'xmlschemauri' => '',
707
                                        'name' => $key,
708
                                        'value' => $value
709
                                );
710
                        } else if (is_array($line)) {
711
                                if (!isset($line['xmlns']))        $line['xmlns'] = '';
712
                                if (!isset($line['xmlschema']))    $line['xmlschema'] = '';
713
                                if (!isset($line['xmlschemauri'])) $line['xmlschemauri'] = '';
714
                                if (!isset($line['name']))         $line['name'] = '';
715
                                if (!isset($line['value']))        $line['value'] = '';
716
                        } else {
717
                                assert(false);
718
                        }
719
                }
720
        }
721
 
722
}