Subversion Repositories oidplus

Rev

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