Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
635 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
635 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
635 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
 
635 daniel-mar 26
class OIDplusOid extends OIDplusObject {
1130 daniel-mar 27
        /**
28
         * @var string
29
         */
635 daniel-mar 30
        private $oid;
31
 
1116 daniel-mar 32
        /**
1130 daniel-mar 33
         * @param string $oid
1116 daniel-mar 34
         * @throws OIDplusException
35
         */
1130 daniel-mar 36
        public function __construct(string $oid) {
635 daniel-mar 37
                $bak_oid = $oid;
38
 
39
                $oid = sanitizeOID($oid, 'auto');
40
                if ($oid === false) {
41
                        throw new OIDplusException(_L('Invalid OID %1',$bak_oid));
42
                }
43
 
44
                if (($oid != '') && (!oid_valid_dotnotation($oid, false, true, 0))) {
45
                        // avoid OIDs like 3.0
46
                        throw new OIDplusException(_L('Invalid OID %1',$bak_oid));
47
                }
48
 
49
                $this->oid = $oid;
50
        }
51
 
1116 daniel-mar 52
        /**
53
         * @param string $node_id
54
         * @return OIDplusOid|null
1130 daniel-mar 55
         * @throws OIDplusException
1116 daniel-mar 56
         */
57
        public static function parse(string $node_id)/*: ?OIDplusOid*/ {
635 daniel-mar 58
                @list($namespace, $oid) = explode(':', $node_id, 2);
1116 daniel-mar 59
                if ($namespace !== self::ns()) return null;
635 daniel-mar 60
                return new self($oid);
61
        }
62
 
1116 daniel-mar 63
        /**
64
         * @return string
65
         */
66
        public static function objectTypeTitle(): string {
635 daniel-mar 67
                return _L('Object Identifier (OID)');
68
        }
69
 
1116 daniel-mar 70
        /**
71
         * @return string
72
         */
73
        public static function objectTypeTitleShort(): string {
635 daniel-mar 74
                return _L('OID');
75
        }
76
 
1116 daniel-mar 77
        /**
78
         * @return string
79
         */
80
        public static function ns(): string {
635 daniel-mar 81
                return 'oid';
82
        }
83
 
1116 daniel-mar 84
        /**
85
         * @return string
86
         */
87
        public static function root(): string {
860 daniel-mar 88
                return self::ns().':';
635 daniel-mar 89
        }
90
 
1116 daniel-mar 91
        /**
92
         * @return bool
93
         */
94
        public function isRoot(): bool {
635 daniel-mar 95
                return $this->oid == '';
96
        }
97
 
1116 daniel-mar 98
        /**
99
         * @param bool $with_ns
100
         * @return string
101
         */
102
        public function nodeId(bool $with_ns=true): string {
859 daniel-mar 103
                return $with_ns ? self::root().$this->oid : $this->oid;
635 daniel-mar 104
        }
105
 
1116 daniel-mar 106
        /**
107
         * @param string $str
108
         * @return string
109
         * @throws OIDplusException
110
         */
111
        public function addString(string $str): string {
635 daniel-mar 112
                if (!$this->isRoot()) {
113
                        if (strpos($str,'.') !== false) throw new OIDplusException(_L('Please only submit one arc (not an absolute OID or multiple arcs).'));
114
                }
115
 
116
                return $this->appendArcs($str)->nodeId();
117
        }
118
 
1116 daniel-mar 119
        /**
120
         * @param OIDplusObject $parent
121
         * @return string
122
         */
123
        public function crudShowId(OIDplusObject $parent): string {
635 daniel-mar 124
                if ($parent instanceof OIDplusOid) {
125
                        return $this->deltaDotNotation($parent);
1116 daniel-mar 126
                } else {
127
                        return '';
635 daniel-mar 128
                }
129
        }
130
 
1116 daniel-mar 131
        /**
132
         * @param OIDplusObject|null $parent
133
         * @return string
1130 daniel-mar 134
         * @throws OIDplusException
1116 daniel-mar 135
         */
136
        public function jsTreeNodeName(OIDplusObject $parent = null): string {
635 daniel-mar 137
                if ($parent == null) return $this->objectTypeTitle();
138
                if ($parent instanceof OIDplusOid) {
139
                        return $this->viewGetArcAsn1s($parent);
140
                } else {
141
                        return '';
142
                }
143
        }
144
 
1116 daniel-mar 145
        /**
146
         * @return string
147
         */
148
        public function defaultTitle(): string {
635 daniel-mar 149
                return _L('OID %1',$this->oid);
150
        }
151
 
1116 daniel-mar 152
        /**
153
         * @return bool
154
         */
155
        public function isLeafNode(): bool {
635 daniel-mar 156
                return false;
157
        }
158
 
1116 daniel-mar 159
        /**
1280 daniel-mar 160
         * @return string[]
1130 daniel-mar 161
         * @throws OIDplusException
1116 daniel-mar 162
         */
163
        private function getTechInfo(): array {
753 daniel-mar 164
                $tech_info = array();
165
 
166
                $tmp = _L('Dot notation');
1371 daniel-mar 167
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#14" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
753 daniel-mar 168
                $tech_info[$tmp] = $this->getDotNotation();
169
 
170
                $tmp = _L('ASN.1 notation');
1371 daniel-mar 171
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#17" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
753 daniel-mar 172
                $tech_info[$tmp] = $this->getAsn1Notation();
173
 
174
                $tmp = _L('OID-IRI notation');
1371 daniel-mar 175
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#iri" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
753 daniel-mar 176
                $tech_info[$tmp] = $this->getIriNotation();
177
 
178
                $tmp = _L('WEID notation');
179
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="https://weid.info/" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
180
                $tech_info[$tmp] = $this->getWeidNotation();
181
 
930 daniel-mar 182
                $tmp = _L('DER encoding');
183
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="https://misc.daniel-marschall.de/asn.1/oid-converter/online.php" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
1050 daniel-mar 184
                $tech_info[$tmp] = str_replace(' ', ':', \OidDerConverter::hexarrayToStr(\OidDerConverter::oidToDER($this->nodeId(false))));
930 daniel-mar 185
 
753 daniel-mar 186
                return $tech_info;
187
        }
188
 
1116 daniel-mar 189
        /**
190
         * @return bool
191
         */
192
        protected function isClassCWeid(): bool {
772 daniel-mar 193
                $dist = oid_distance($this->oid, '1.3.6.1.4.1.37553.8');
194
                if ($dist === false) return false;
195
                return $dist >= 0;
196
        }
197
 
1116 daniel-mar 198
        /**
199
         * @param string $title
200
         * @param string $content
201
         * @param string $icon
202
         * @return void
203
         * @throws OIDplusException
204
         */
205
        public function getContentPage(string &$title, string &$content, string &$icon) {
772 daniel-mar 206
                if ($this->isClassCWeid()) {
207
                        // TODO: Also change treeview menu mini-icon?
801 daniel-mar 208
                        $icon = file_exists(__DIR__.'/img/weid_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/weid_icon.png' : '';
772 daniel-mar 209
                } else {
801 daniel-mar 210
                        $icon = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
772 daniel-mar 211
                }
635 daniel-mar 212
 
213
                if ($this->isRoot()) {
214
                        $title = OIDplusOid::objectTypeTitle();
215
 
216
                        $res = OIDplus::db()->query("select id from ###objects where parent = ?", array(self::root()));
790 daniel-mar 217
                        if ($res->any()) {
635 daniel-mar 218
                                $content = _L('Please select an OID in the tree view at the left to show its contents.');
219
                        } else {
220
                                $content = _L('Currently, no OID is registered in the system.');
221
                        }
222
 
223
                        if (!$this->isLeafNode()) {
224
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
225
                                        $content .= '<h2>'._L('Manage your root OIDs').'</h2>';
226
                                } else {
227
                                        $content .= '<h2>'._L('Root OIDs').'</h2>';
228
                                }
229
                                $content .= '%%CRUD%%';
230
                        }
231
                } else {
232
                        $title = $this->getTitle();
233
 
753 daniel-mar 234
                        $tech_info = $this->getTechInfo();
235
                        $tech_info_html = '';
236
                        if (count($tech_info) > 0) {
237
                                $tech_info_html .= '<h2>'._L('Technical information').'</h2>';
1386 daniel-mar 238
                                $tech_info_html .= '<div style="overflow:auto"><table border="0">';
753 daniel-mar 239
                                foreach ($tech_info as $key => $value) {
1386 daniel-mar 240
                                        $tech_info_html .= '<tr><td valign="top" style="white-space: nowrap;">'.$key.': </td><td><code>'.$value.'</code></td></tr>';
753 daniel-mar 241
                                }
1387 daniel-mar 242
                                $tech_info_html .= '</table></div>';
753 daniel-mar 243
                        }
635 daniel-mar 244
 
753 daniel-mar 245
                        $content = $tech_info_html;
246
 
247
                        $content .= '<h2>'._L('Description').'</h2>%%DESC%%'.
248
                                    '<h2>'._L('Registration Authority').'</h2>%%RA_INFO%%';
249
 
635 daniel-mar 250
                        if (!$this->isLeafNode()) {
251
                                if ($this->userHasWriteRights()) {
928 daniel-mar 252
                                        $content .= '<h2>'._L('Create or change subordinate objects').'</h2>';
635 daniel-mar 253
                                } else {
928 daniel-mar 254
                                        $content .= '<h2>'._L('Subordinate objects').'</h2>';
635 daniel-mar 255
                                }
256
                                $content .= '%%CRUD%%';
257
                        }
258
                }
259
        }
260
 
261
        # ---
262
 
1116 daniel-mar 263
        /**
264
         * Gets the last arc of an WEID
265
         * @return false|string
266
         */
635 daniel-mar 267
        public function weidArc() {
689 daniel-mar 268
                // Dirty hack: We prepend '0.' in front of the OID to enforce the
269
                //             creation of a Class A weid (weid:root:) . Otherwise we could not
270
                //             get the hidden arc value "8" from "weid:4" (which is actually "weid:pen:SZ5-8-?"
1050 daniel-mar 271
                $weid = \Frdl\Weid\WeidOidConverter::oid2weid('0.'.$this->getDotNotation());
635 daniel-mar 272
                if ($weid === false) return false;
683 daniel-mar 273
                $ary = explode(':', $weid);
274
                $weid = array_pop($ary); // remove namespace and sub-namespace if existing
635 daniel-mar 275
                $x = explode('-', $weid);
276
                if (count($x) < 2) return ''; // WEID root arc. Has no name
277
                return $x[count($x)-2];
278
        }
279
 
1116 daniel-mar 280
        /**
281
         * @param bool $withAbbr
282
         * @return string
283
         */
284
        public function getWeidNotation(bool $withAbbr=true): string {
1050 daniel-mar 285
                $weid = \Frdl\Weid\WeidOidConverter::oid2weid($this->getDotNotation());
635 daniel-mar 286
                if ($withAbbr) {
683 daniel-mar 287
                        $ary = explode(':', $weid);
288
                        $weid = array_pop($ary); // remove namespace and sub-namespace if existing
289
                        $ns = implode(':', $ary).':';
290
 
635 daniel-mar 291
                        $weid_arcs = explode('-', $weid);
292
                        foreach ($weid_arcs as $i => &$weid) {
293
                                if ($i == count($weid_arcs)-1) {
294
                                        $weid = '<abbr title="'._L('weLuhn check digit').'">'.$weid.'</abbr>';
295
                                } else {
296
                                        $oid_arcs = explode('.',$this->oid);
297
                                        $weid_num = $oid_arcs[(count($oid_arcs)-1)-(count($weid_arcs)-1)+($i+1)];
298
                                        if ($weid_num != $weid) {
299
                                                $weid = '<abbr title="'._L('Numeric value').': '.$weid_num.'">'.$weid.'</abbr>';
300
                                        }
301
                                }
302
                        }
683 daniel-mar 303
                        $base_arc = '???';
304
                        if ($ns === 'weid:')      $base_arc = '1.3.6.1.4.1.37553.8';
305
                        if ($ns === 'weid:pen:')  $base_arc = '1.3.6.1.4.1';
688 daniel-mar 306
                        if ($ns === 'weid:root:') $base_arc = _L('OID tree root');
683 daniel-mar 307
 
308
                        $weid = '<abbr title="'._L('Base OID').': '.$base_arc.'">' . rtrim($ns,':') . '</abbr>:' . implode('-',$weid_arcs);
635 daniel-mar 309
                }
310
                return $weid;
311
        }
312
 
1116 daniel-mar 313
        /**
314
         * @param string|int $arcs
315
         * @return OIDplusOid
316
         * @throws OIDplusException
317
         */
318
        public function appendArcs($arcs): OIDplusOid {
635 daniel-mar 319
                $out = new self($this->oid);
320
 
321
                if ($out->isRoot()) {
322
                        $out->oid .= $arcs;
323
                } else {
324
                        $out->oid .= '.' . $arcs;
325
                }
326
 
327
                $bak_oid = $out->oid;
328
                $out->oid = sanitizeOID($out->oid);
329
                if ($out->oid === false) throw new OIDplusException(_L('%1 is not a valid OID!',$bak_oid));
330
 
859 daniel-mar 331
                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_ID_LENGTH')-strlen(self::root());
635 daniel-mar 332
                if (strlen($out->oid) > $maxlen) {
333
                        throw new OIDplusException(_L('The resulting OID "%1" is too long (max allowed length: %2).',$out->oid,$maxlen));
334
                }
335
 
336
                return $out;
337
        }
338
 
1116 daniel-mar 339
        /**
340
         * @param OIDplusOid $parent
341
         * @return false|string
342
         */
635 daniel-mar 343
        public function deltaDotNotation(OIDplusOid $parent) {
344
                if (!$parent->isRoot()) {
345
                        if (substr($this->oid, 0, strlen($parent->oid)+1) == $parent->oid.'.') {
346
                                return substr($this->oid, strlen($parent->oid)+1);
347
                        } else {
348
                                return false;
349
                        }
350
                } else {
351
                        return $this->oid;
352
                }
353
        }
354
 
1116 daniel-mar 355
        /**
1280 daniel-mar 356
         * @return OIDplusOidAsn1Id[]
1116 daniel-mar 357
         * @throws OIDplusException
358
         */
359
        public function getAsn1Ids(): array {
979 daniel-mar 360
                $asn_ids = array();
361
                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ? order by lfd", array("oid:".$this->oid));
362
                while ($row_asn = $res_asn->fetch_array()) {
363
                        $name = $row_asn['name'];
1119 daniel-mar 364
                        $standardized = $row_asn['standardized'] ?? false;
1121 daniel-mar 365
                        $well_known = $row_asn['well_known'] ?? false;
979 daniel-mar 366
                        $asn_ids[] = new OIDplusOidAsn1Id($name, $standardized, $well_known);
367
                }
368
                return $asn_ids;
369
        }
370
 
1116 daniel-mar 371
        /**
1280 daniel-mar 372
         * @return OIDplusOidIri[]
1116 daniel-mar 373
         * @throws OIDplusException
374
         */
375
        public function getIris(): array {
979 daniel-mar 376
                $iri_ids = array();
377
                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ? order by lfd", array("oid:".$this->oid));
378
                while ($row_iri = $res_iri->fetch_array()) {
379
                        $name = $row_iri['name'];
1119 daniel-mar 380
                        $longarc = $row_iri['longarc'] ?? false;
1121 daniel-mar 381
                        $well_known = $row_iri['well_known'] ?? false;
979 daniel-mar 382
                        $iri_ids[] = new OIDplusOidIri($name, $longarc, $well_known);
383
                }
384
                return $iri_ids;
385
        }
386
 
1116 daniel-mar 387
        /**
388
         * @param OIDplusOid|null $parent
1121 daniel-mar 389
         * @param string $separator
1116 daniel-mar 390
         * @return string
391
         * @throws OIDplusException
392
         */
1121 daniel-mar 393
        public function viewGetArcAsn1s(OIDplusOid $parent=null, string $separator = ' | '): string {
635 daniel-mar 394
                $asn_ids = array();
395
 
859 daniel-mar 396
                if (is_null($parent)) $parent = OIDplusOid::parse(self::root());
635 daniel-mar 397
 
398
                $part = $this->deltaDotNotation($parent);
399
 
400
                if (strpos($part, '.') === false) {
979 daniel-mar 401
                        $asn_id_objs = $this->getAsn1Ids();
402
                        foreach ($asn_id_objs as $asn_id_obj) {
403
                                $asn_ids[] = $asn_id_obj->getName().'('.$part.')';
635 daniel-mar 404
                        }
405
                }
406
 
407
                if (count($asn_ids) == 0) $asn_ids = array($part);
408
                return implode($separator, $asn_ids);
409
        }
410
 
1116 daniel-mar 411
        /**
412
         * @param bool $withAbbr
413
         * @return string
414
         * @throws OIDplusException
415
         */
416
        public function getAsn1Notation(bool $withAbbr=true): string {
635 daniel-mar 417
                $asn1_notation = '';
418
                $arcs = explode('.', $this->oid);
419
 
420
                foreach ($arcs as $arc) {
859 daniel-mar 421
                        $res = OIDplus::db()->query("select name, standardized from ###asn1id where oid = ? order by lfd", array(self::root().implode('.',$arcs)));
635 daniel-mar 422
 
423
                        $names = array();
424
                        while ($row = $res->fetch_array()) {
425
                                $names[] = $row['name']."(".end($arcs).")";
426
                                if ($row['standardized']) {
427
                                        $names[] = $row['name'];
428
                                }
429
                        }
430
 
431
                        $numeric = array_pop($arcs);
432
                        if (count($names) > 1) {
433
                                $first_name = array_shift($names);
434
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names);
435
                                if ($withAbbr) {
436
                                        $asn1_notation = '<abbr title="'.$abbr.'">'.$first_name.'</abbr> '.$asn1_notation;
437
                                } else {
438
                                        $asn1_notation = $first_name.' '.$asn1_notation;
439
                                }
440
                        } else if (count($names) == 1) {
441
                                $asn1_notation = array_shift($names).' '.$asn1_notation;
442
                        } else {
443
                                $asn1_notation = $numeric.' '.$asn1_notation;
444
                        }
445
                }
446
 
758 daniel-mar 447
                return "{ ".trim($asn1_notation)." }";
635 daniel-mar 448
        }
449
 
1116 daniel-mar 450
        /**
451
         * @param bool $withAbbr
452
         * @return string
453
         * @throws OIDplusException
454
         */
455
        public function getIriNotation(bool $withAbbr=true): string {
635 daniel-mar 456
                $iri_notation = '';
457
                $arcs = explode('.', $this->oid);
458
 
459
                foreach ($arcs as $arc) {
859 daniel-mar 460
                        $res = OIDplus::db()->query("select name, longarc from ###iri where oid = ? order by lfd", array(self::root().implode('.',$arcs)));
635 daniel-mar 461
 
462
                        $is_longarc = false;
463
                        $names = array();
464
                        while ($row = $res->fetch_array()) {
465
                                $is_longarc = $row['longarc'];
466
                                $names[] = $row['name'];
467
 
468
                                if ($is_longarc) {
469
                                        $names[] = 'Joint-ISO-ITU-T/'.$row['name']; // Long arcs can only be inside root OID 2
470
                                }
471
                        }
472
 
473
                        $names[] = array_pop($arcs);
474
                        if (count($names) > 2) {
475
                                $first_name = array_shift($names);
476
                                $numeric = array_pop($names);
477
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names).'&#10;'._L('Numeric value').': '.$numeric;
478
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
479
                        } else if (count($names) > 1) {
480
                                $first_name = array_shift($names);
481
                                $abbr = _L('Numeric value').': '.array_shift($names);
482
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
483
                        } else if (count($names) == 1) {
484
                                $iri_notation = array_shift($names) . '/' . $iri_notation;
485
                        }
486
 
487
                        if ($is_longarc) break; // we don't write /ITU-T/ at the beginning, when /ITU-T/xyz is a long arc
488
                }
1130 daniel-mar 489
                return '/' . substr($iri_notation, 0, strlen($iri_notation)-1);
635 daniel-mar 490
        }
491
 
1116 daniel-mar 492
        /**
493
         * @return string
494
         */
495
        public function getDotNotation(): string {
635 daniel-mar 496
                return $this->oid;
497
        }
498
 
1116 daniel-mar 499
        /**
500
         * @return bool
501
         * @throws OIDplusException
502
         */
503
        public function isWellKnown(): bool {
635 daniel-mar 504
                $res = OIDplus::db()->query("select oid from ###asn1id where oid = ? and well_known = ?", array("oid:".$this->oid,true));
790 daniel-mar 505
                if ($res->any()) return true;
635 daniel-mar 506
 
507
                $res = OIDplus::db()->query("select oid from ###iri where oid = ? and well_known = ?", array("oid:".$this->oid,true));
790 daniel-mar 508
                if ($res->any()) return true;
635 daniel-mar 509
 
510
                return false;
511
        }
512
 
1116 daniel-mar 513
        /**
514
         * @param array $demandedASN1s
515
         * @param bool $simulate
516
         * @return void
517
         * @throws OIDplusException
518
         */
519
        public function replaceAsn1Ids(array $demandedASN1s=array(), bool $simulate=false) {
635 daniel-mar 520
                if ($this->isWellKnown()) {
521
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
522
                }
523
 
524
                // First do a few checks
525
                foreach ($demandedASN1s as &$asn1) {
526
                        $asn1 = trim($asn1);
527
 
528
                        if (strlen($asn1) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN')) {
529
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN');
530
                                throw new OIDplusException(_L('ASN.1 alphanumeric identifier "%1" is too long (max allowed length %2)',$asn1,$maxlen));
531
                        }
532
 
533
                        // Validate identifier
534
                        if (!oid_id_is_valid($asn1)) throw new OIDplusException(_L('"%1" is not a valid ASN.1 identifier!',$asn1));
535
 
536
                        // Check if the (real) parent has any conflict
537
                        // Unlike IRI identifiers, ASN.1 identifiers may be used multiple times (not recommended), except if one of them is standardized
1302 daniel-mar 538
                        $res = OIDplus::db()->query("select oid from ###asn1id where name = ? and standardized = ?", array($asn1,true)); // Attention: Requires case-SENSITIVE database collation!!
635 daniel-mar 539
                        while ($row = $res->fetch_array()) {
540
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
541
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
542
                                   ($check_oid !== $this->oid))                    // different OID
543
                                {
544
                                        throw new OIDplusException(_L('ASN.1 identifier "%1" is a standardized identifier belonging to OID %2',$asn1,$check_oid));
545
                                }
546
                        }
547
                }
1316 daniel-mar 548
                unset($asn1); // Very important, otherwise we would modify the array if we later use "foreach ($demandedASN1s as $asn1)"
635 daniel-mar 549
 
550
                // Now do the real replacement
551
                if (!$simulate) {
552
                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array("oid:".$this->oid));
1162 daniel-mar 553
                        foreach ($demandedASN1s as $asn1) {
1316 daniel-mar 554
                                OIDplus::db()->query("insert into ###asn1id (oid, name, well_known, standardized) values (?, ?, ?, ?)", array("oid:".$this->oid, $asn1, false, false));
635 daniel-mar 555
                        }
556
                }
557
        }
558
 
1116 daniel-mar 559
        /**
560
         * @param array $demandedIris
561
         * @param bool $simulate
562
         * @return void
563
         * @throws OIDplusException
564
         */
565
        public function replaceIris(array $demandedIris=array(), bool $simulate=false) {
635 daniel-mar 566
                if ($this->isWellKnown()) {
567
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
568
                }
569
 
570
                // First do a few checks
571
                foreach ($demandedIris as &$iri) {
572
                        $iri = trim($iri);
573
 
574
                        if (strlen($iri) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN')) {
575
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN');
576
                                throw new OIDplusException(_L('Unicode label "%1" is too long (max allowed length %2)',$iri,$maxlen));
577
                        }
578
 
579
                        // Validate identifier
580
                        if (!iri_arc_valid($iri, false)) throw new OIDplusException(_L('"%1" is not a valid IRI!',$iri));
581
 
582
                        // Check if the (real) parent has any conflict
1302 daniel-mar 583
                        $res = OIDplus::db()->query("select oid from ###iri where name = ?", array($iri)); // Attention: Requires case-SENSITIVE database collation!!
635 daniel-mar 584
                        while ($row = $res->fetch_array()) {
585
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
586
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
587
                                   ($check_oid !== $this->oid))                    // different OID
588
                                {
589
                                        throw new OIDplusException(_L('IRI "%1" is already used by another OID (%2)',$iri,$check_oid));
590
                                }
591
                        }
592
                }
1316 daniel-mar 593
                unset($iri); // Very important, otherwise we would modify the array if we later use "foreach ($demandedIris as $iri)"
635 daniel-mar 594
 
595
                // Now do the real replacement
596
                if (!$simulate) {
597
                        OIDplus::db()->query("delete from ###iri where oid = ?", array("oid:".$this->oid));
1162 daniel-mar 598
                        foreach ($demandedIris as $iri) {
1316 daniel-mar 599
                                OIDplus::db()->query("insert into ###iri (oid, name, longarc, well_known) values (?, ?, ?, ?)", array("oid:".$this->oid, $iri, false, false));
635 daniel-mar 600
                        }
601
                }
602
        }
603
 
1116 daniel-mar 604
        /**
605
         * @return OIDplusOid|null
606
         */
607
        public function one_up()/*: ?OIDplusOid*/ {
635 daniel-mar 608
                return self::parse(self::ns().':'.oid_up($this->oid));
609
        }
610
 
1116 daniel-mar 611
        /**
1130 daniel-mar 612
         * @param OIDplusObject|string $to
1116 daniel-mar 613
         * @return int|null
614
         */
635 daniel-mar 615
        public function distance($to) {
616
                if (!is_object($to)) $to = OIDplusObject::parse($to);
1121 daniel-mar 617
                if (!$to) return null;
1116 daniel-mar 618
                if (!($to instanceof $this)) return null;
619
                $res = oid_distance($to->oid, $this->oid);
620
                return $res !== false ? $res : null;
635 daniel-mar 621
        }
622
 
1116 daniel-mar 623
        /**
1280 daniel-mar 624
         * @return OIDplusAltId[]
1116 daniel-mar 625
         * @throws OIDplusException
626
         */
627
        public function getAltIds(): array {
635 daniel-mar 628
                if ($this->isRoot()) return array();
629
                $ids = parent::getAltIds();
945 daniel-mar 630
 
1438 daniel-mar 631
                // R74n "Multiplane", see https://r74n.com/multiplane/
632
                // Vendor space:
633
                // [0x2aabb] = 1.3.6.1.4.1.61117.1.[0x2aa00].[0xbb]
634
                // Every other space:
635
                // [0xcaabb] = 1.3.6.1.4.1.61117.1.[0xcaabb]
636
                $multiplane = null;
637
                $tmp_oid = oid_up($this->oid);
638
                for ($i=0; $i<=0xFF; $i++) {
639
                        $vid = 0x20000 + ($i<<16);
640
                        if ($tmp_oid == '1.3.6.1.4.1.61117.1.'.$vid) {
641
                                $j = (int)substr($this->oid,strlen($tmp_oid)+1);
642
                                if (($j>=0) && ($j<=0xFF)) {
643
                                        $xi = str_pad(dechex($i), 2, '0', STR_PAD_LEFT);
644
                                        $xj = str_pad(dechex($j), 2, '0', STR_PAD_LEFT);
645
                                        $multiplane = strtoupper('R2'.$xi.$xj);
646
                                }
647
                                break;
648
                        }
649
                }
650
                if ($tmp_oid == '1.3.6.1.4.1.61117.1') {
651
                        $ij = (int)substr($this->oid,strlen('1.3.6.1.4.1.61117.1.'));
652
                        if ((($ij>=0) && ($ij<=0x1FFFF)) || (($ij>0x30000) && ($ij<=0xFFFFF))) {
653
                                $multiplane = strtoupper('R'.str_pad(dechex($ij), 5, '0', STR_PAD_LEFT));
654
                        }
655
                }
656
                if (!is_null($multiplane)) {
657
                        $ids[] = new OIDplusAltId('r74n-multiplane', $multiplane, _L('R74n-Multiplane'));
658
                }
659
 
1328 daniel-mar 660
                if ($uuid = oid_to_uuid($this->oid)) {
929 daniel-mar 661
                        // UUID-OIDs are representation of an UUID
1424 daniel-mar 662
                        $ids[] = new OIDplusAltId('guid', $uuid, _L('UUID representation of this OID'));
929 daniel-mar 663
                } else {
664
                        // All other OIDs can be formed into an UUID by making them a namebased OID
665
                        // You could theoretically also do this to an UUID-OID, but we exclude this case to avoid that users are confused
666
                        $ids[] = new OIDplusAltId('guid', gen_uuid_md5_namebased(UUID_NAMEBASED_NS_OID, $this->oid), _L('Name based version 3 / MD5 UUID with namespace %1','UUID_NAMEBASED_NS_OID'));
667
                        $ids[] = new OIDplusAltId('guid', gen_uuid_sha1_namebased(UUID_NAMEBASED_NS_OID, $this->oid), _L('Name based version 5 / SHA1 UUID with namespace %1','UUID_NAMEBASED_NS_OID'));
635 daniel-mar 668
                }
930 daniel-mar 669
 
1078 daniel-mar 670
                $oid_parts = explode('.',$this->nodeId(false));
671
 
1077 daniel-mar 672
                // (VTS B1) Members
673
                if ($this->nodeId(false) == '1.3.6.1.4.1.37476.1') {
674
                        $aid = 'D276000186B1';
675
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 676
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186B1');
1077 daniel-mar 677
                } else {
678
                        if ((count($oid_parts) == 9) && ($oid_parts[0] == '1') && ($oid_parts[1] == '3') && ($oid_parts[2] == '6') && ($oid_parts[3] == '1') && ($oid_parts[4] == '4') && ($oid_parts[5] == '1') && ($oid_parts[6] == '37476') && ($oid_parts[7] == '1')) {
679
                                $number = str_pad($oid_parts[8],4,'0',STR_PAD_LEFT);
680
                                $aid = 'D276000186B1'.$number;
681
                                $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 682
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186B1');
1077 daniel-mar 683
                        }
684
                }
685
 
686
                // (VTS B2) Products
687
                if ($this->nodeId(false) == '1.3.6.1.4.1.37476.2') {
688
                        $aid = 'D276000186B2';
689
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 690
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186B2');
1077 daniel-mar 691
                } else {
692
                        if ((count($oid_parts) == 9) && ($oid_parts[0] == '1') && ($oid_parts[1] == '3') && ($oid_parts[2] == '6') && ($oid_parts[3] == '1') && ($oid_parts[4] == '4') && ($oid_parts[5] == '1') && ($oid_parts[6] == '37476') && ($oid_parts[7] == '2')) {
693
                                $number = str_pad($oid_parts[8],4,'0',STR_PAD_LEFT);
694
                                $aid = 'D276000186B2'.$number;
695
                                $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 696
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186B2');
1077 daniel-mar 697
                        }
698
                }
699
 
1078 daniel-mar 700
                // (VTS B2 00 05) OIDplus System AID / Information Object AID
701
                if ((count($oid_parts) == 10) && ($oid_parts[0] == '1') && ($oid_parts[1] == '3') && ($oid_parts[2] == '6') && ($oid_parts[3] == '1') && ($oid_parts[4] == '4') && ($oid_parts[5] == '1') && ($oid_parts[6] == '37476') && ($oid_parts[7] == '30') && ($oid_parts[8] == '9')) {
702
                        $sid = $oid_parts[9];
703
                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
704
                        $aid = 'D276000186B20005'.$sid_hex;
705
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 706
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus System Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186B20005');
1078 daniel-mar 707
                }
708
                else if ((count($oid_parts) == 11) && ($oid_parts[0] == '1') && ($oid_parts[1] == '3') && ($oid_parts[2] == '6') && ($oid_parts[3] == '1') && ($oid_parts[4] == '4') && ($oid_parts[5] == '1') && ($oid_parts[6] == '37476') && ($oid_parts[7] == '30') && ($oid_parts[8] == '9')) {
709
                        $sid = $oid_parts[9];
710
                        $obj = $oid_parts[10];
711
                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
712
                        $obj_hex = strtoupper(str_pad(dechex((int)$obj),8,'0',STR_PAD_LEFT));
713
                        $aid = 'D276000186B20005'.$sid_hex.$obj_hex;
714
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 715
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus Information Object Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186B20005');
1078 daniel-mar 716
                }
717
 
945 daniel-mar 718
                // (VTS F0) IANA PEN to AID Mapping (PIX allowed)
719
                if ((count($oid_parts) == 7) && ($oid_parts[0] == '1') && ($oid_parts[1] == '3') && ($oid_parts[2] == '6') && ($oid_parts[3] == '1') && ($oid_parts[4] == '4') && ($oid_parts[5] == '1')) {
720
                        $pen = $oid_parts[6];
721
                        $aid = 'D276000186F0'.$pen;
722
                        if (strlen($aid)%2 == 1) $aid .= 'F';
723
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 724
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, with "FF" prefix').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186F0');
961 daniel-mar 725
                        $ids[] = new OIDplusAltId('iana-pen', $pen, _L('IANA Private Enterprise Number (PEN)'));
945 daniel-mar 726
                }
727
 
728
                // (VTS F1) FreeOID to AID Mapping (PIX allowed)
729
                if ((count($oid_parts) == 9) && ($oid_parts[0] == '1') && ($oid_parts[1] == '3') && ($oid_parts[2] == '6') && ($oid_parts[3] == '1') && ($oid_parts[4] == '4') && ($oid_parts[5] == '1') && ($oid_parts[6] == '37476') && ($oid_parts[7] == '9000')) {
730
                        $number = $oid_parts[8];
731
                        $aid = 'D276000186F1'.$number;
732
                        if (strlen($aid)%2 == 1) $aid .= 'F';
733
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 734
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, with "FF" prefix').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186F1');
945 daniel-mar 735
                }
736
 
737
                // (VTS F6) Mapping OID-to-AID if possible
933 daniel-mar 738
                try {
1050 daniel-mar 739
                        $test_der = \OidDerConverter::hexarrayToStr(\OidDerConverter::oidToDER($this->nodeId(false)));
740
                } catch (\Exception $e) {
933 daniel-mar 741
                        $test_der = '00'; // error, should not happen
742
                }
743
                if (substr($test_der,0,3) == '06 ') { // 06 = ASN.1 type of Absolute ID
957 daniel-mar 744
                        if (($oid_parts[0] == '2') && ($oid_parts[1] == '999')) {
933 daniel-mar 745
                                // Note that "ViaThinkSoft E0" AID are not unique!
746
                                // OIDplus will use the relative DER of the 2.999.xx OID as PIX
747
                                $aid_candidate = 'D2 76 00 01 86 E0 ' . substr($test_der, strlen('06 xx 88 37 ')); // Remove ASN.1 06=Type, xx=Length and the 2.999 arcs "88 37"
945 daniel-mar 748
                                $aid_is_ok = aid_canonize($aid_candidate);
933 daniel-mar 749
                                if (!$aid_is_ok) {
750
                                        // If DER encoding is not possible (too long), then we will use a 32 bit small hash.
751
                                        $small_hash = str_pad(dechex(smallhash($this->nodeId(false))),8,'0',STR_PAD_LEFT);
752
                                        $aid_candidate = 'D2 76 00 01 86 E0 ' . strtoupper(implode(' ',str_split($small_hash,2)));
945 daniel-mar 753
                                        $aid_is_ok = aid_canonize($aid_candidate);
933 daniel-mar 754
                                }
1439 daniel-mar 755
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'), '', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186E0');
957 daniel-mar 756
                        } else if (($oid_parts[0] == '0') && ($oid_parts[1] == '4') && ($oid_parts[2] == '0') && ($oid_parts[3] == '127') && ($oid_parts[4] == '0') && ($oid_parts[5] == '7')) {
757
                                // Illegal usage of E8 by German BSI, plus using E8+Len+OID instead of E8+OID like ISO does
758
                                // PIX probably not used
759
                                $aid_candidate = 'E8 '.substr($test_der, strlen('06 ')); // Remove ASN.1 06=Type
760
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 761
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'));
957 daniel-mar 762
                        } else if (($oid_parts[0] == '1') && ($oid_parts[1] == '0')) {
933 daniel-mar 763
                                // ISO Standard AID (OID 1.0.xx)
764
                                // Optional PIX allowed
765
                                $aid_candidate = 'E8 '.substr($test_der, strlen('06 xx ')); // Remove ASN.1 06=Type and xx=Length
945 daniel-mar 766
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 767
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')');
930 daniel-mar 768
                        } else {
933 daniel-mar 769
                                // All other OIDs can be mapped using the "ViaThinkSoft F6" scheme, but only if the DER encoding is not too long
770
                                // No PIX allowed
771
                                $aid_candidate = 'D2 76 00 01 86 F6 '.substr($test_der, strlen('06 xx ')); // Remove ASN.1 06=Type and xx=Length
945 daniel-mar 772
                                $aid_is_ok = aid_canonize($aid_candidate);
1439 daniel-mar 773
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186F6');
930 daniel-mar 774
                        }
775
                }
776
 
635 daniel-mar 777
                return $ids;
778
        }
779
 
1116 daniel-mar 780
        /**
781
         * @return string
782
         */
783
        public function getDirectoryName(): string {
635 daniel-mar 784
                if ($this->isRoot()) return $this->ns();
785
                $oid = $this->nodeId(false);
786
                return $this->ns().'_'.str_replace('.', '_', $oid);
787
        }
800 daniel-mar 788
 
1116 daniel-mar 789
        /**
790
         * @param string $mode
791
         * @return string
792
         */
793
        public static function treeIconFilename(string $mode): string {
800 daniel-mar 794
                return 'img/'.$mode.'_icon16.png';
795
        }
635 daniel-mar 796
}