Subversion Repositories oidplus

Rev

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