Subversion Repositories oidplus

Rev

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