Subversion Repositories oidplus

Rev

Rev 1162 | Rev 1280 | 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
                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
        /**
356
         * @return array
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
        /**
372
         * @return array
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
538
                        $res = OIDplus::db()->query("select oid from ###asn1id where name = ? and standardized = ?", array($asn1,true));
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
                }
548
 
549
                // Now do the real replacement
550
                if (!$simulate) {
551
                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array("oid:".$this->oid));
1162 daniel-mar 552
                        foreach ($demandedASN1s as $asn1) {
635 daniel-mar 553
                                OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array("oid:".$this->oid, $asn1));
554
                        }
555
                }
556
        }
557
 
1116 daniel-mar 558
        /**
559
         * @param array $demandedIris
560
         * @param bool $simulate
561
         * @return void
562
         * @throws OIDplusException
563
         */
564
        public function replaceIris(array $demandedIris=array(), bool $simulate=false) {
635 daniel-mar 565
                if ($this->isWellKnown()) {
566
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
567
                }
568
 
569
                // First do a few checks
570
                foreach ($demandedIris as &$iri) {
571
                        $iri = trim($iri);
572
 
573
                        if (strlen($iri) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN')) {
574
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN');
575
                                throw new OIDplusException(_L('Unicode label "%1" is too long (max allowed length %2)',$iri,$maxlen));
576
                        }
577
 
578
                        // Validate identifier
579
                        if (!iri_arc_valid($iri, false)) throw new OIDplusException(_L('"%1" is not a valid IRI!',$iri));
580
 
581
                        // Check if the (real) parent has any conflict
582
                        $res = OIDplus::db()->query("select oid from ###iri where name = ?", array($iri));
583
                        while ($row = $res->fetch_array()) {
584
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
585
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
586
                                   ($check_oid !== $this->oid))                    // different OID
587
                                {
588
                                        throw new OIDplusException(_L('IRI "%1" is already used by another OID (%2)',$iri,$check_oid));
589
                                }
590
                        }
591
                }
592
 
593
                // Now do the real replacement
594
                if (!$simulate) {
595
                        OIDplus::db()->query("delete from ###iri where oid = ?", array("oid:".$this->oid));
1162 daniel-mar 596
                        foreach ($demandedIris as $iri) {
635 daniel-mar 597
                                OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array("oid:".$this->oid, $iri));
598
                        }
599
                }
600
        }
601
 
1116 daniel-mar 602
        /**
603
         * @return OIDplusOid|null
604
         */
605
        public function one_up()/*: ?OIDplusOid*/ {
635 daniel-mar 606
                return self::parse(self::ns().':'.oid_up($this->oid));
607
        }
608
 
1116 daniel-mar 609
        /**
1130 daniel-mar 610
         * @param OIDplusObject|string $to
1116 daniel-mar 611
         * @return int|null
612
         */
635 daniel-mar 613
        public function distance($to) {
614
                if (!is_object($to)) $to = OIDplusObject::parse($to);
1121 daniel-mar 615
                if (!$to) return null;
1116 daniel-mar 616
                if (!($to instanceof $this)) return null;
617
                $res = oid_distance($to->oid, $this->oid);
618
                return $res !== false ? $res : null;
635 daniel-mar 619
        }
620
 
1116 daniel-mar 621
        /**
622
         * @return array|OIDplusAltId[]
623
         * @throws OIDplusException
624
         */
625
        public function getAltIds(): array {
635 daniel-mar 626
                if ($this->isRoot()) return array();
627
                $ids = parent::getAltIds();
945 daniel-mar 628
 
635 daniel-mar 629
                if ($uuid = oid_to_uuid($this->oid)) {
929 daniel-mar 630
                        // UUID-OIDs are representation of an UUID
635 daniel-mar 631
                        $ids[] = new OIDplusAltId('guid', $uuid, _L('GUID representation of this OID'));
929 daniel-mar 632
                } else {
633
                        // All other OIDs can be formed into an UUID by making them a namebased OID
634
                        // You could theoretically also do this to an UUID-OID, but we exclude this case to avoid that users are confused
635
                        $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'));
636
                        $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 637
                }
930 daniel-mar 638
 
1078 daniel-mar 639
                $oid_parts = explode('.',$this->nodeId(false));
640
 
1077 daniel-mar 641
                // (VTS B1) Members
642
                if ($this->nodeId(false) == '1.3.6.1.4.1.37476.1') {
643
                        $aid = 'D276000186B1';
644
                        $aid_is_ok = aid_canonize($aid);
645
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
646
                } else {
647
                        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')) {
648
                                $number = str_pad($oid_parts[8],4,'0',STR_PAD_LEFT);
649
                                $aid = 'D276000186B1'.$number;
650
                                $aid_is_ok = aid_canonize($aid);
1079 daniel-mar 651
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')');
1077 daniel-mar 652
                        }
653
                }
654
 
655
                // (VTS B2) Products
656
                if ($this->nodeId(false) == '1.3.6.1.4.1.37476.2') {
657
                        $aid = 'D276000186B2';
658
                        $aid_is_ok = aid_canonize($aid);
659
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
660
                } else {
661
                        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')) {
662
                                $number = str_pad($oid_parts[8],4,'0',STR_PAD_LEFT);
663
                                $aid = 'D276000186B2'.$number;
664
                                $aid_is_ok = aid_canonize($aid);
1079 daniel-mar 665
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')');
1077 daniel-mar 666
                        }
667
                }
668
 
1078 daniel-mar 669
                // (VTS B2 00 05) OIDplus System AID / Information Object AID
670
                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')) {
671
                        $sid = $oid_parts[9];
672
                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
673
                        $aid = 'D276000186B20005'.$sid_hex;
674
                        $aid_is_ok = aid_canonize($aid);
675
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus System Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
676
                }
677
                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')) {
678
                        $sid = $oid_parts[9];
679
                        $obj = $oid_parts[10];
680
                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
681
                        $obj_hex = strtoupper(str_pad(dechex((int)$obj),8,'0',STR_PAD_LEFT));
682
                        $aid = 'D276000186B20005'.$sid_hex.$obj_hex;
683
                        $aid_is_ok = aid_canonize($aid);
684
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus Information Object Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
685
                }
686
 
945 daniel-mar 687
                // (VTS F0) IANA PEN to AID Mapping (PIX allowed)
688
                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')) {
689
                        $pen = $oid_parts[6];
690
                        $aid = 'D276000186F0'.$pen;
691
                        if (strlen($aid)%2 == 1) $aid .= 'F';
692
                        $aid_is_ok = aid_canonize($aid);
959 daniel-mar 693
                        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 694
                        $ids[] = new OIDplusAltId('iana-pen', $pen, _L('IANA Private Enterprise Number (PEN)'));
945 daniel-mar 695
                }
696
 
697
                // (VTS F1) FreeOID to AID Mapping (PIX allowed)
698
                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')) {
699
                        $number = $oid_parts[8];
700
                        $aid = 'D276000186F1'.$number;
701
                        if (strlen($aid)%2 == 1) $aid .= 'F';
702
                        $aid_is_ok = aid_canonize($aid);
959 daniel-mar 703
                        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 704
                }
705
 
706
                // (VTS F6) Mapping OID-to-AID if possible
933 daniel-mar 707
                try {
1050 daniel-mar 708
                        $test_der = \OidDerConverter::hexarrayToStr(\OidDerConverter::oidToDER($this->nodeId(false)));
709
                } catch (\Exception $e) {
933 daniel-mar 710
                        $test_der = '00'; // error, should not happen
711
                }
712
                if (substr($test_der,0,3) == '06 ') { // 06 = ASN.1 type of Absolute ID
957 daniel-mar 713
                        if (($oid_parts[0] == '2') && ($oid_parts[1] == '999')) {
933 daniel-mar 714
                                // Note that "ViaThinkSoft E0" AID are not unique!
715
                                // OIDplus will use the relative DER of the 2.999.xx OID as PIX
716
                                $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 717
                                $aid_is_ok = aid_canonize($aid_candidate);
933 daniel-mar 718
                                if (!$aid_is_ok) {
719
                                        // If DER encoding is not possible (too long), then we will use a 32 bit small hash.
720
                                        $small_hash = str_pad(dechex(smallhash($this->nodeId(false))),8,'0',STR_PAD_LEFT);
721
                                        $aid_candidate = 'D2 76 00 01 86 E0 ' . strtoupper(implode(' ',str_split($small_hash,2)));
945 daniel-mar 722
                                        $aid_is_ok = aid_canonize($aid_candidate);
933 daniel-mar 723
                                }
959 daniel-mar 724
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'));
957 daniel-mar 725
                        } 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')) {
726
                                // Illegal usage of E8 by German BSI, plus using E8+Len+OID instead of E8+OID like ISO does
727
                                // PIX probably not used
728
                                $aid_candidate = 'E8 '.substr($test_der, strlen('06 ')); // Remove ASN.1 06=Type
729
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 730
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'));
957 daniel-mar 731
                        } else if (($oid_parts[0] == '1') && ($oid_parts[1] == '0')) {
933 daniel-mar 732
                                // ISO Standard AID (OID 1.0.xx)
733
                                // Optional PIX allowed
734
                                $aid_candidate = 'E8 '.substr($test_der, strlen('06 xx ')); // Remove ASN.1 06=Type and xx=Length
945 daniel-mar 735
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 736
                                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 737
                        } else {
933 daniel-mar 738
                                // All other OIDs can be mapped using the "ViaThinkSoft F6" scheme, but only if the DER encoding is not too long
739
                                // No PIX allowed
740
                                $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 741
                                $aid_is_ok = aid_canonize($aid_candidate);
959 daniel-mar 742
                                if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid_candidate, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
930 daniel-mar 743
                        }
744
                }
745
 
635 daniel-mar 746
                return $ids;
747
        }
748
 
1116 daniel-mar 749
        /**
750
         * @return string
751
         */
752
        public function getDirectoryName(): string {
635 daniel-mar 753
                if ($this->isRoot()) return $this->ns();
754
                $oid = $this->nodeId(false);
755
                return $this->ns().'_'.str_replace('.', '_', $oid);
756
        }
800 daniel-mar 757
 
1116 daniel-mar 758
        /**
759
         * @param string $mode
760
         * @return string
761
         */
762
        public static function treeIconFilename(string $mode): string {
800 daniel-mar 763
                return 'img/'.$mode.'_icon16.png';
764
        }
635 daniel-mar 765
}