Subversion Repositories oidplus

Rev

Rev 1121 | Rev 1162 | 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
        /**
160
         * @return array
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');
1052 daniel-mar 167
                $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 168
                $tech_info[$tmp] = $this->getDotNotation();
169
 
170
                $tmp = _L('ASN.1 notation');
1052 daniel-mar 171
                $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 172
                $tech_info[$tmp] = $this->getAsn1Notation();
173
 
174
                $tmp = _L('OID-IRI notation');
1052 daniel-mar 175
                $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 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>';
238
                                $tech_info_html .= '<table border="0">';
239
                                foreach ($tech_info as $key => $value) {
240
                                        $tech_info_html .= '<tr><td>'.$key.': </td><td><code>'.$value.'</code></td></tr>';
241
                                }
242
                                $tech_info_html .= '</table>';
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
                $depth = 0;
337
                foreach (explode('.',$out->oid) as $arc) {
338
                        if (strlen($arc) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE')) {
339
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE');
340
                                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));
341
                        }
342
                        $depth++;
343
                }
344
                if ($depth > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH')) {
345
                        $maxdepth = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH');
346
                        throw new OIDplusException(_L('OID %1 has too many arcs (current depth %2, max depth %3)',$out->oid,$depth,$maxdepth));
347
                }
348
 
349
                return $out;
350
        }
351
 
1116 daniel-mar 352
        /**
353
         * @param OIDplusOid $parent
354
         * @return false|string
355
         */
635 daniel-mar 356
        public function deltaDotNotation(OIDplusOid $parent) {
357
                if (!$parent->isRoot()) {
358
                        if (substr($this->oid, 0, strlen($parent->oid)+1) == $parent->oid.'.') {
359
                                return substr($this->oid, strlen($parent->oid)+1);
360
                        } else {
361
                                return false;
362
                        }
363
                } else {
364
                        return $this->oid;
365
                }
366
        }
367
 
1116 daniel-mar 368
        /**
369
         * @return array
370
         * @throws OIDplusException
371
         */
372
        public function getAsn1Ids(): array {
979 daniel-mar 373
                $asn_ids = array();
374
                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ? order by lfd", array("oid:".$this->oid));
375
                while ($row_asn = $res_asn->fetch_array()) {
376
                        $name = $row_asn['name'];
1119 daniel-mar 377
                        $standardized = $row_asn['standardized'] ?? false;
1121 daniel-mar 378
                        $well_known = $row_asn['well_known'] ?? false;
979 daniel-mar 379
                        $asn_ids[] = new OIDplusOidAsn1Id($name, $standardized, $well_known);
380
                }
381
                return $asn_ids;
382
        }
383
 
1116 daniel-mar 384
        /**
385
         * @return array
386
         * @throws OIDplusException
387
         */
388
        public function getIris(): array {
979 daniel-mar 389
                $iri_ids = array();
390
                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ? order by lfd", array("oid:".$this->oid));
391
                while ($row_iri = $res_iri->fetch_array()) {
392
                        $name = $row_iri['name'];
1119 daniel-mar 393
                        $longarc = $row_iri['longarc'] ?? false;
1121 daniel-mar 394
                        $well_known = $row_iri['well_known'] ?? false;
979 daniel-mar 395
                        $iri_ids[] = new OIDplusOidIri($name, $longarc, $well_known);
396
                }
397
                return $iri_ids;
398
        }
399
 
1116 daniel-mar 400
        /**
401
         * @param OIDplusOid|null $parent
1121 daniel-mar 402
         * @param string $separator
1116 daniel-mar 403
         * @return string
404
         * @throws OIDplusException
405
         */
1121 daniel-mar 406
        public function viewGetArcAsn1s(OIDplusOid $parent=null, string $separator = ' | '): string {
635 daniel-mar 407
                $asn_ids = array();
408
 
859 daniel-mar 409
                if (is_null($parent)) $parent = OIDplusOid::parse(self::root());
635 daniel-mar 410
 
411
                $part = $this->deltaDotNotation($parent);
412
 
413
                if (strpos($part, '.') === false) {
979 daniel-mar 414
                        $asn_id_objs = $this->getAsn1Ids();
415
                        foreach ($asn_id_objs as $asn_id_obj) {
416
                                $asn_ids[] = $asn_id_obj->getName().'('.$part.')';
635 daniel-mar 417
                        }
418
                }
419
 
420
                if (count($asn_ids) == 0) $asn_ids = array($part);
421
                return implode($separator, $asn_ids);
422
        }
423
 
1116 daniel-mar 424
        /**
425
         * @param bool $withAbbr
426
         * @return string
427
         * @throws OIDplusException
428
         */
429
        public function getAsn1Notation(bool $withAbbr=true): string {
635 daniel-mar 430
                $asn1_notation = '';
431
                $arcs = explode('.', $this->oid);
432
 
433
                foreach ($arcs as $arc) {
859 daniel-mar 434
                        $res = OIDplus::db()->query("select name, standardized from ###asn1id where oid = ? order by lfd", array(self::root().implode('.',$arcs)));
635 daniel-mar 435
 
436
                        $names = array();
437
                        while ($row = $res->fetch_array()) {
438
                                $names[] = $row['name']."(".end($arcs).")";
439
                                if ($row['standardized']) {
440
                                        $names[] = $row['name'];
441
                                }
442
                        }
443
 
444
                        $numeric = array_pop($arcs);
445
                        if (count($names) > 1) {
446
                                $first_name = array_shift($names);
447
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names);
448
                                if ($withAbbr) {
449
                                        $asn1_notation = '<abbr title="'.$abbr.'">'.$first_name.'</abbr> '.$asn1_notation;
450
                                } else {
451
                                        $asn1_notation = $first_name.' '.$asn1_notation;
452
                                }
453
                        } else if (count($names) == 1) {
454
                                $asn1_notation = array_shift($names).' '.$asn1_notation;
455
                        } else {
456
                                $asn1_notation = $numeric.' '.$asn1_notation;
457
                        }
458
                }
459
 
758 daniel-mar 460
                return "{ ".trim($asn1_notation)." }";
635 daniel-mar 461
        }
462
 
1116 daniel-mar 463
        /**
464
         * @param bool $withAbbr
465
         * @return string
466
         * @throws OIDplusException
467
         */
468
        public function getIriNotation(bool $withAbbr=true): string {
635 daniel-mar 469
                $iri_notation = '';
470
                $arcs = explode('.', $this->oid);
471
 
472
                foreach ($arcs as $arc) {
859 daniel-mar 473
                        $res = OIDplus::db()->query("select name, longarc from ###iri where oid = ? order by lfd", array(self::root().implode('.',$arcs)));
635 daniel-mar 474
 
475
                        $is_longarc = false;
476
                        $names = array();
477
                        while ($row = $res->fetch_array()) {
478
                                $is_longarc = $row['longarc'];
479
                                $names[] = $row['name'];
480
 
481
                                if ($is_longarc) {
482
                                        $names[] = 'Joint-ISO-ITU-T/'.$row['name']; // Long arcs can only be inside root OID 2
483
                                }
484
                        }
485
 
486
                        $names[] = array_pop($arcs);
487
                        if (count($names) > 2) {
488
                                $first_name = array_shift($names);
489
                                $numeric = array_pop($names);
490
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names).'&#10;'._L('Numeric value').': '.$numeric;
491
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
492
                        } else if (count($names) > 1) {
493
                                $first_name = array_shift($names);
494
                                $abbr = _L('Numeric value').': '.array_shift($names);
495
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
496
                        } else if (count($names) == 1) {
497
                                $iri_notation = array_shift($names) . '/' . $iri_notation;
498
                        }
499
 
500
                        if ($is_longarc) break; // we don't write /ITU-T/ at the beginning, when /ITU-T/xyz is a long arc
501
                }
1130 daniel-mar 502
                return '/' . substr($iri_notation, 0, strlen($iri_notation)-1);
635 daniel-mar 503
        }
504
 
1116 daniel-mar 505
        /**
506
         * @return string
507
         */
508
        public function getDotNotation(): string {
635 daniel-mar 509
                return $this->oid;
510
        }
511
 
1116 daniel-mar 512
        /**
513
         * @return bool
514
         * @throws OIDplusException
515
         */
516
        public function isWellKnown(): bool {
635 daniel-mar 517
                $res = OIDplus::db()->query("select oid from ###asn1id where oid = ? and well_known = ?", array("oid:".$this->oid,true));
790 daniel-mar 518
                if ($res->any()) return true;
635 daniel-mar 519
 
520
                $res = OIDplus::db()->query("select oid from ###iri where oid = ? and well_known = ?", array("oid:".$this->oid,true));
790 daniel-mar 521
                if ($res->any()) return true;
635 daniel-mar 522
 
523
                return false;
524
        }
525
 
1116 daniel-mar 526
        /**
527
         * @param array $demandedASN1s
528
         * @param bool $simulate
529
         * @return void
530
         * @throws OIDplusException
531
         */
532
        public function replaceAsn1Ids(array $demandedASN1s=array(), bool $simulate=false) {
635 daniel-mar 533
                if ($this->isWellKnown()) {
534
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
535
                }
536
 
537
                // First do a few checks
538
                foreach ($demandedASN1s as &$asn1) {
539
                        $asn1 = trim($asn1);
540
 
541
                        if (strlen($asn1) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN')) {
542
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN');
543
                                throw new OIDplusException(_L('ASN.1 alphanumeric identifier "%1" is too long (max allowed length %2)',$asn1,$maxlen));
544
                        }
545
 
546
                        // Validate identifier
547
                        if (!oid_id_is_valid($asn1)) throw new OIDplusException(_L('"%1" is not a valid ASN.1 identifier!',$asn1));
548
 
549
                        // Check if the (real) parent has any conflict
550
                        // Unlike IRI identifiers, ASN.1 identifiers may be used multiple times (not recommended), except if one of them is standardized
551
                        $res = OIDplus::db()->query("select oid from ###asn1id where name = ? and standardized = ?", array($asn1,true));
552
                        while ($row = $res->fetch_array()) {
553
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
554
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
555
                                   ($check_oid !== $this->oid))                    // different OID
556
                                {
557
                                        throw new OIDplusException(_L('ASN.1 identifier "%1" is a standardized identifier belonging to OID %2',$asn1,$check_oid));
558
                                }
559
                        }
560
                }
561
 
562
                // Now do the real replacement
563
                if (!$simulate) {
564
                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array("oid:".$this->oid));
565
                        foreach ($demandedASN1s as &$asn1) {
566
                                OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array("oid:".$this->oid, $asn1));
567
                        }
568
                }
569
        }
570
 
1116 daniel-mar 571
        /**
572
         * @param array $demandedIris
573
         * @param bool $simulate
574
         * @return void
575
         * @throws OIDplusException
576
         */
577
        public function replaceIris(array $demandedIris=array(), bool $simulate=false) {
635 daniel-mar 578
                if ($this->isWellKnown()) {
579
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
580
                }
581
 
582
                // First do a few checks
583
                foreach ($demandedIris as &$iri) {
584
                        $iri = trim($iri);
585
 
586
                        if (strlen($iri) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN')) {
587
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN');
588
                                throw new OIDplusException(_L('Unicode label "%1" is too long (max allowed length %2)',$iri,$maxlen));
589
                        }
590
 
591
                        // Validate identifier
592
                        if (!iri_arc_valid($iri, false)) throw new OIDplusException(_L('"%1" is not a valid IRI!',$iri));
593
 
594
                        // Check if the (real) parent has any conflict
595
                        $res = OIDplus::db()->query("select oid from ###iri where name = ?", array($iri));
596
                        while ($row = $res->fetch_array()) {
597
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
598
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
599
                                   ($check_oid !== $this->oid))                    // different OID
600
                                {
601
                                        throw new OIDplusException(_L('IRI "%1" is already used by another OID (%2)',$iri,$check_oid));
602
                                }
603
                        }
604
                }
605
 
606
                // Now do the real replacement
607
                if (!$simulate) {
608
                        OIDplus::db()->query("delete from ###iri where oid = ?", array("oid:".$this->oid));
609
                        foreach ($demandedIris as &$iri) {
610
                                OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array("oid:".$this->oid, $iri));
611
                        }
612
                }
613
        }
614
 
1116 daniel-mar 615
        /**
616
         * @return OIDplusOid|null
617
         */
618
        public function one_up()/*: ?OIDplusOid*/ {
635 daniel-mar 619
                return self::parse(self::ns().':'.oid_up($this->oid));
620
        }
621
 
1116 daniel-mar 622
        /**
1130 daniel-mar 623
         * @param OIDplusObject|string $to
1116 daniel-mar 624
         * @return int|null
625
         */
635 daniel-mar 626
        public function distance($to) {
627
                if (!is_object($to)) $to = OIDplusObject::parse($to);
1121 daniel-mar 628
                if (!$to) return null;
1116 daniel-mar 629
                if (!($to instanceof $this)) return null;
630
                $res = oid_distance($to->oid, $this->oid);
631
                return $res !== false ? $res : null;
635 daniel-mar 632
        }
633
 
1116 daniel-mar 634
        /**
635
         * @return array|OIDplusAltId[]
636
         * @throws OIDplusException
637
         */
638
        public function getAltIds(): array {
635 daniel-mar 639
                if ($this->isRoot()) return array();
640
                $ids = parent::getAltIds();
945 daniel-mar 641
 
635 daniel-mar 642
                if ($uuid = oid_to_uuid($this->oid)) {
929 daniel-mar 643
                        // UUID-OIDs are representation of an UUID
635 daniel-mar 644
                        $ids[] = new OIDplusAltId('guid', $uuid, _L('GUID representation of this OID'));
929 daniel-mar 645
                } else {
646
                        // All other OIDs can be formed into an UUID by making them a namebased OID
647
                        // You could theoretically also do this to an UUID-OID, but we exclude this case to avoid that users are confused
648
                        $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'));
649
                        $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 650
                }
930 daniel-mar 651
 
1078 daniel-mar 652
                $oid_parts = explode('.',$this->nodeId(false));
653
 
1077 daniel-mar 654
                // (VTS B1) Members
655
                if ($this->nodeId(false) == '1.3.6.1.4.1.37476.1') {
656
                        $aid = 'D276000186B1';
657
                        $aid_is_ok = aid_canonize($aid);
658
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
659
                } else {
660
                        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')) {
661
                                $number = str_pad($oid_parts[8],4,'0',STR_PAD_LEFT);
662
                                $aid = 'D276000186B1'.$number;
663
                                $aid_is_ok = aid_canonize($aid);
1079 daniel-mar 664
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')');
1077 daniel-mar 665
                        }
666
                }
667
 
668
                // (VTS B2) Products
669
                if ($this->nodeId(false) == '1.3.6.1.4.1.37476.2') {
670
                        $aid = 'D276000186B2';
671
                        $aid_is_ok = aid_canonize($aid);
672
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
673
                } else {
674
                        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')) {
675
                                $number = str_pad($oid_parts[8],4,'0',STR_PAD_LEFT);
676
                                $aid = 'D276000186B2'.$number;
677
                                $aid_is_ok = aid_canonize($aid);
1079 daniel-mar 678
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')');
1077 daniel-mar 679
                        }
680
                }
681
 
1078 daniel-mar 682
                // (VTS B2 00 05) OIDplus System AID / Information Object AID
683
                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')) {
684
                        $sid = $oid_parts[9];
685
                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
686
                        $aid = 'D276000186B20005'.$sid_hex;
687
                        $aid_is_ok = aid_canonize($aid);
688
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus System Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
689
                }
690
                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')) {
691
                        $sid = $oid_parts[9];
692
                        $obj = $oid_parts[10];
693
                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
694
                        $obj_hex = strtoupper(str_pad(dechex((int)$obj),8,'0',STR_PAD_LEFT));
695
                        $aid = 'D276000186B20005'.$sid_hex.$obj_hex;
696
                        $aid_is_ok = aid_canonize($aid);
697
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus Information Object Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
698
                }
699
 
945 daniel-mar 700
                // (VTS F0) IANA PEN to AID Mapping (PIX allowed)
701
                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')) {
702
                        $pen = $oid_parts[6];
703
                        $aid = 'D276000186F0'.$pen;
704
                        if (strlen($aid)%2 == 1) $aid .= 'F';
705
                        $aid_is_ok = aid_canonize($aid);
959 daniel-mar 706
                        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 707
                        $ids[] = new OIDplusAltId('iana-pen', $pen, _L('IANA Private Enterprise Number (PEN)'));
945 daniel-mar 708
                }
709
 
710
                // (VTS F1) FreeOID to AID Mapping (PIX allowed)
711
                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')) {
712
                        $number = $oid_parts[8];
713
                        $aid = 'D276000186F1'.$number;
714
                        if (strlen($aid)%2 == 1) $aid .= 'F';
715
                        $aid_is_ok = aid_canonize($aid);
959 daniel-mar 716
                        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 717
                }
718
 
719
                // (VTS F6) Mapping OID-to-AID if possible
933 daniel-mar 720
                try {
1050 daniel-mar 721
                        $test_der = \OidDerConverter::hexarrayToStr(\OidDerConverter::oidToDER($this->nodeId(false)));
722
                } catch (\Exception $e) {
933 daniel-mar 723
                        $test_der = '00'; // error, should not happen
724
                }
725
                if (substr($test_der,0,3) == '06 ') { // 06 = ASN.1 type of Absolute ID
957 daniel-mar 726
                        if (($oid_parts[0] == '2') && ($oid_parts[1] == '999')) {
933 daniel-mar 727
                                // Note that "ViaThinkSoft E0" AID are not unique!
728
                                // OIDplus will use the relative DER of the 2.999.xx OID as PIX
729
                                $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 730
                                $aid_is_ok = aid_canonize($aid_candidate);
933 daniel-mar 731
                                if (!$aid_is_ok) {
732
                                        // If DER encoding is not possible (too long), then we will use a 32 bit small hash.
733
                                        $small_hash = str_pad(dechex(smallhash($this->nodeId(false))),8,'0',STR_PAD_LEFT);
734
                                        $aid_candidate = 'D2 76 00 01 86 E0 ' . strtoupper(implode(' ',str_split($small_hash,2)));
945 daniel-mar 735
                                        $aid_is_ok = aid_canonize($aid_candidate);
933 daniel-mar 736
                                }
959 daniel-mar 737
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'));
957 daniel-mar 738
                        } 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')) {
739
                                // Illegal usage of E8 by German BSI, plus using E8+Len+OID instead of E8+OID like ISO does
740
                                // PIX probably not used
741
                                $aid_candidate = 'E8 '.substr($test_der, strlen('06 ')); // Remove ASN.1 06=Type
742
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 743
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'));
957 daniel-mar 744
                        } else if (($oid_parts[0] == '1') && ($oid_parts[1] == '0')) {
933 daniel-mar 745
                                // ISO Standard AID (OID 1.0.xx)
746
                                // Optional PIX allowed
747
                                $aid_candidate = 'E8 '.substr($test_der, strlen('06 xx ')); // Remove ASN.1 06=Type and xx=Length
945 daniel-mar 748
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 749
                                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 750
                        } else {
933 daniel-mar 751
                                // All other OIDs can be mapped using the "ViaThinkSoft F6" scheme, but only if the DER encoding is not too long
752
                                // No PIX allowed
753
                                $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 754
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 755
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
930 daniel-mar 756
                        }
757
                }
758
 
635 daniel-mar 759
                return $ids;
760
        }
761
 
1116 daniel-mar 762
        /**
763
         * @return string
764
         */
765
        public function getDirectoryName(): string {
635 daniel-mar 766
                if ($this->isRoot()) return $this->ns();
767
                $oid = $this->nodeId(false);
768
                return $this->ns().'_'.str_replace('.', '_', $oid);
769
        }
800 daniel-mar 770
 
1116 daniel-mar 771
        /**
772
         * @param string $mode
773
         * @return string
774
         */
775
        public static function treeIconFilename(string $mode): string {
800 daniel-mar 776
                return 'img/'.$mode.'_icon16.png';
777
        }
635 daniel-mar 778
}