Subversion Repositories oidplus

Rev

Rev 790 | Rev 801 | 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
5
 * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
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
 
20
if (!defined('INSIDE_OIDPLUS')) die();
21
 
22
class OIDplusOid extends OIDplusObject {
23
        private $oid;
24
 
25
        public function __construct($oid) {
26
                $bak_oid = $oid;
27
 
28
                $oid = sanitizeOID($oid, 'auto');
29
                if ($oid === false) {
30
                        throw new OIDplusException(_L('Invalid OID %1',$bak_oid));
31
                }
32
 
33
                if (($oid != '') && (!oid_valid_dotnotation($oid, false, true, 0))) {
34
                        // avoid OIDs like 3.0
35
                        throw new OIDplusException(_L('Invalid OID %1',$bak_oid));
36
                }
37
 
38
                $this->oid = $oid;
39
        }
40
 
41
        public static function parse($node_id) {
42
                @list($namespace, $oid) = explode(':', $node_id, 2);
43
                if ($namespace !== 'oid') return false;
44
                return new self($oid);
45
        }
46
 
47
        public static function objectTypeTitle() {
48
                return _L('Object Identifier (OID)');
49
        }
50
 
51
        public static function objectTypeTitleShort() {
52
                return _L('OID');
53
        }
54
 
55
        public static function ns() {
56
                return 'oid';
57
        }
58
 
59
        public static function root() {
60
                return 'oid:';
61
        }
62
 
63
        public function isRoot() {
64
                return $this->oid == '';
65
        }
66
 
67
        public function nodeId($with_ns=true) {
68
                return $with_ns ? 'oid:'.$this->oid : $this->oid;
69
        }
70
 
71
        public function addString($str) {
72
                if (!$this->isRoot()) {
73
                        if (strpos($str,'.') !== false) throw new OIDplusException(_L('Please only submit one arc (not an absolute OID or multiple arcs).'));
74
                }
75
 
76
                return $this->appendArcs($str)->nodeId();
77
        }
78
 
79
        public function crudShowId(OIDplusObject $parent) {
80
                if ($parent instanceof OIDplusOid) {
81
                        return $this->deltaDotNotation($parent);
82
                }
83
        }
84
 
85
        public function jsTreeNodeName(OIDplusObject $parent = null) {
86
                if ($parent == null) return $this->objectTypeTitle();
87
                if ($parent instanceof OIDplusOid) {
88
                        return $this->viewGetArcAsn1s($parent);
89
                } else {
90
                        return '';
91
                }
92
        }
93
 
94
        public function defaultTitle() {
95
                return _L('OID %1',$this->oid);
96
        }
97
 
98
        public function isLeafNode() {
99
                return false;
100
        }
101
 
753 daniel-mar 102
        private function getTechInfo() {
103
                $tech_info = array();
104
 
105
                $tmp = _L('Dot notation');
106
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#14" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
107
                $tech_info[$tmp] = $this->getDotNotation();
108
 
109
                $tmp = _L('ASN.1 notation');
110
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#17" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
111
                $tech_info[$tmp] = $this->getAsn1Notation();
112
 
113
                $tmp = _L('OID-IRI notation');
114
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#iri" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
115
                $tech_info[$tmp] = $this->getIriNotation();
116
 
117
                $tmp = _L('WEID notation');
118
                $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="https://weid.info/" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
119
                $tech_info[$tmp] = $this->getWeidNotation();
120
 
121
                return $tech_info;
122
        }
123
 
772 daniel-mar 124
        protected function isClassCWeid() {
125
                $dist = oid_distance($this->oid, '1.3.6.1.4.1.37553.8');
126
                if ($dist === false) return false;
127
                return $dist >= 0;
128
        }
129
 
635 daniel-mar 130
        public function getContentPage(&$title, &$content, &$icon) {
772 daniel-mar 131
                if ($this->isClassCWeid()) {
132
                        // TODO: Also change treeview menu mini-icon?
800 daniel-mar 133
                        $icon = file_exists(__DIR__.'/img/weid_icon.png') ? OIDplus::webPath(__DIR__,true).'img/weid_icon.png' : '';
772 daniel-mar 134
                } else {
800 daniel-mar 135
                        $icon = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webPath(__DIR__,true).'img/main_icon.png' : '';
772 daniel-mar 136
                }
635 daniel-mar 137
 
138
                if ($this->isRoot()) {
139
                        $title = OIDplusOid::objectTypeTitle();
140
 
141
                        $res = OIDplus::db()->query("select id from ###objects where parent = ?", array(self::root()));
790 daniel-mar 142
                        if ($res->any()) {
635 daniel-mar 143
                                $content = _L('Please select an OID in the tree view at the left to show its contents.');
144
                        } else {
145
                                $content = _L('Currently, no OID is registered in the system.');
146
                        }
147
 
148
                        if (!$this->isLeafNode()) {
149
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
150
                                        $content .= '<h2>'._L('Manage your root OIDs').'</h2>';
151
                                } else {
152
                                        $content .= '<h2>'._L('Root OIDs').'</h2>';
153
                                }
154
                                $content .= '%%CRUD%%';
155
                        }
156
                } else {
157
                        $title = $this->getTitle();
158
 
753 daniel-mar 159
                        $tech_info = $this->getTechInfo();
160
                        $tech_info_html = '';
161
                        if (count($tech_info) > 0) {
162
                                $tech_info_html .= '<h2>'._L('Technical information').'</h2>';
163
                                $tech_info_html .= '<table border="0">';
164
                                foreach ($tech_info as $key => $value) {
165
                                        $tech_info_html .= '<tr><td>'.$key.': </td><td><code>'.$value.'</code></td></tr>';
166
                                }
167
                                $tech_info_html .= '</table>';
168
                        }
635 daniel-mar 169
 
753 daniel-mar 170
                        $content = $tech_info_html;
171
 
172
                        $content .= '<h2>'._L('Description').'</h2>%%DESC%%'.
173
                                    '<h2>'._L('Registration Authority').'</h2>%%RA_INFO%%';
174
 
635 daniel-mar 175
                        if (!$this->isLeafNode()) {
176
                                if ($this->userHasWriteRights()) {
177
                                        $content .= '<h2>'._L('Create or change subsequent objects').'</h2>';
178
                                } else {
179
                                        $content .= '<h2>'._L('Subsequent objects').'</h2>';
180
                                }
181
                                $content .= '%%CRUD%%';
182
                        }
183
                }
184
        }
185
 
186
        # ---
187
 
683 daniel-mar 188
        // Gets the last arc of an WEID
635 daniel-mar 189
        public function weidArc() {
689 daniel-mar 190
                // Dirty hack: We prepend '0.' in front of the OID to enforce the
191
                //             creation of a Class A weid (weid:root:) . Otherwise we could not
192
                //             get the hidden arc value "8" from "weid:4" (which is actually "weid:pen:SZ5-8-?"
193
                $weid = WeidOidConverter::oid2weid('0.'.$this->getDotNotation());
635 daniel-mar 194
                if ($weid === false) return false;
683 daniel-mar 195
                $ary = explode(':', $weid);
196
                $weid = array_pop($ary); // remove namespace and sub-namespace if existing
635 daniel-mar 197
                $x = explode('-', $weid);
198
                if (count($x) < 2) return ''; // WEID root arc. Has no name
199
                return $x[count($x)-2];
200
        }
201
 
202
        public function getWeidNotation($withAbbr=true) {
203
                $weid = WeidOidConverter::oid2weid($this->getDotNotation());
204
                if ($withAbbr) {
683 daniel-mar 205
                        $ary = explode(':', $weid);
206
                        $weid = array_pop($ary); // remove namespace and sub-namespace if existing
207
                        $ns = implode(':', $ary).':';
208
 
635 daniel-mar 209
                        $weid_arcs = explode('-', $weid);
210
                        foreach ($weid_arcs as $i => &$weid) {
211
                                if ($i == count($weid_arcs)-1) {
212
                                        $weid = '<abbr title="'._L('weLuhn check digit').'">'.$weid.'</abbr>';
213
                                } else {
214
                                        $oid_arcs = explode('.',$this->oid);
215
                                        $weid_num = $oid_arcs[(count($oid_arcs)-1)-(count($weid_arcs)-1)+($i+1)];
216
                                        if ($weid_num != $weid) {
217
                                                $weid = '<abbr title="'._L('Numeric value').': '.$weid_num.'">'.$weid.'</abbr>';
218
                                        }
219
                                }
220
                        }
683 daniel-mar 221
                        $base_arc = '???';
222
                        if ($ns === 'weid:')      $base_arc = '1.3.6.1.4.1.37553.8';
223
                        if ($ns === 'weid:pen:')  $base_arc = '1.3.6.1.4.1';
688 daniel-mar 224
                        if ($ns === 'weid:root:') $base_arc = _L('OID tree root');
683 daniel-mar 225
 
226
                        $weid = '<abbr title="'._L('Base OID').': '.$base_arc.'">' . rtrim($ns,':') . '</abbr>:' . implode('-',$weid_arcs);
635 daniel-mar 227
                }
228
                return $weid;
229
        }
230
 
231
        public function appendArcs(String $arcs) {
232
                $out = new self($this->oid);
233
 
234
                if ($out->isRoot()) {
235
                        $out->oid .= $arcs;
236
                } else {
237
                        $out->oid .= '.' . $arcs;
238
                }
239
 
240
                $bak_oid = $out->oid;
241
                $out->oid = sanitizeOID($out->oid);
242
                if ($out->oid === false) throw new OIDplusException(_L('%1 is not a valid OID!',$bak_oid));
243
 
244
                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_ID_LENGTH')-strlen('oid:');
245
                if (strlen($out->oid) > $maxlen) {
246
                        throw new OIDplusException(_L('The resulting OID "%1" is too long (max allowed length: %2).',$out->oid,$maxlen));
247
                }
248
 
249
                $depth = 0;
250
                foreach (explode('.',$out->oid) as $arc) {
251
                        if (strlen($arc) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE')) {
252
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE');
253
                                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));
254
                        }
255
                        $depth++;
256
                }
257
                if ($depth > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH')) {
258
                        $maxdepth = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH');
259
                        throw new OIDplusException(_L('OID %1 has too many arcs (current depth %2, max depth %3)',$out->oid,$depth,$maxdepth));
260
                }
261
 
262
                return $out;
263
        }
264
 
265
        public function deltaDotNotation(OIDplusOid $parent) {
266
                if (!$parent->isRoot()) {
267
                        if (substr($this->oid, 0, strlen($parent->oid)+1) == $parent->oid.'.') {
268
                                return substr($this->oid, strlen($parent->oid)+1);
269
                        } else {
270
                                return false;
271
                        }
272
                } else {
273
                        return $this->oid;
274
                }
275
        }
276
 
277
        public function viewGetArcAsn1s(OIDplusOid $parent=null, $separator = ' | ') {
278
                $asn_ids = array();
279
 
280
                if (is_null($parent)) $parent = OIDplusOid::parse('oid:');
281
 
282
                $part = $this->deltaDotNotation($parent);
283
 
284
                if (strpos($part, '.') === false) {
285
                        $res2 = OIDplus::db()->query("select name from ###asn1id where oid = ? order by lfd", array("oid:".$this->oid));
286
                        while ($row2 = $res2->fetch_array()) {
287
                                $asn_ids[] = $row2['name'].'('.$part.')';
288
                        }
289
                }
290
 
291
                if (count($asn_ids) == 0) $asn_ids = array($part);
292
                return implode($separator, $asn_ids);
293
        }
294
 
295
        public function getAsn1Notation($withAbbr=true) {
296
                $asn1_notation = '';
297
                $arcs = explode('.', $this->oid);
298
 
299
                foreach ($arcs as $arc) {
300
                        $res = OIDplus::db()->query("select name, standardized from ###asn1id where oid = ? order by lfd", array('oid:'.implode('.',$arcs)));
301
 
302
                        $names = array();
303
                        while ($row = $res->fetch_array()) {
304
                                $names[] = $row['name']."(".end($arcs).")";
305
                                if ($row['standardized']) {
306
                                        $names[] = $row['name'];
307
                                }
308
                        }
309
 
310
                        $numeric = array_pop($arcs);
311
                        if (count($names) > 1) {
312
                                $first_name = array_shift($names);
313
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names);
314
                                if ($withAbbr) {
315
                                        $asn1_notation = '<abbr title="'.$abbr.'">'.$first_name.'</abbr> '.$asn1_notation;
316
                                } else {
317
                                        $asn1_notation = $first_name.' '.$asn1_notation;
318
                                }
319
                        } else if (count($names) == 1) {
320
                                $asn1_notation = array_shift($names).' '.$asn1_notation;
321
                        } else {
322
                                $asn1_notation = $numeric.' '.$asn1_notation;
323
                        }
324
                }
325
 
758 daniel-mar 326
                return "{ ".trim($asn1_notation)." }";
635 daniel-mar 327
        }
328
 
329
        public function getIriNotation($withAbbr=true) {
330
                $iri_notation = '';
331
                $arcs = explode('.', $this->oid);
332
 
333
                foreach ($arcs as $arc) {
334
                        $res = OIDplus::db()->query("select name, longarc from ###iri where oid = ? order by lfd", array('oid:'.implode('.',$arcs)));
335
 
336
                        $is_longarc = false;
337
                        $names = array();
338
                        while ($row = $res->fetch_array()) {
339
                                $is_longarc = $row['longarc'];
340
                                $names[] = $row['name'];
341
 
342
                                if ($is_longarc) {
343
                                        $names[] = 'Joint-ISO-ITU-T/'.$row['name']; // Long arcs can only be inside root OID 2
344
                                }
345
                        }
346
 
347
                        $names[] = array_pop($arcs);
348
                        if (count($names) > 2) {
349
                                $first_name = array_shift($names);
350
                                $numeric = array_pop($names);
351
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names).'&#10;'._L('Numeric value').': '.$numeric;
352
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
353
                        } else if (count($names) > 1) {
354
                                $first_name = array_shift($names);
355
                                $abbr = _L('Numeric value').': '.array_shift($names);
356
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
357
                        } else if (count($names) == 1) {
358
                                $iri_notation = array_shift($names) . '/' . $iri_notation;
359
                        }
360
 
361
                        if ($is_longarc) break; // we don't write /ITU-T/ at the beginning, when /ITU-T/xyz is a long arc
362
                }
363
                $iri_notation = '/' . substr($iri_notation, 0, strlen($iri_notation)-1);
364
 
365
                return $iri_notation;
366
        }
367
 
368
        public function getDotNotation() {
369
                return $this->oid;
370
        }
371
 
372
        public function isWellKnown() {
373
                $res = OIDplus::db()->query("select oid from ###asn1id where oid = ? and well_known = ?", array("oid:".$this->oid,true));
790 daniel-mar 374
                if ($res->any()) return true;
635 daniel-mar 375
 
376
                $res = OIDplus::db()->query("select oid from ###iri where oid = ? and well_known = ?", array("oid:".$this->oid,true));
790 daniel-mar 377
                if ($res->any()) return true;
635 daniel-mar 378
 
379
                return false;
380
        }
381
 
382
        public function replaceAsn1Ids($demandedASN1s=array(), $simulate=false) {
383
                if ($this->isWellKnown()) {
384
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
385
                }
386
 
387
                // First do a few checks
388
                foreach ($demandedASN1s as &$asn1) {
389
                        $asn1 = trim($asn1);
390
 
391
                        if (strlen($asn1) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN')) {
392
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN');
393
                                throw new OIDplusException(_L('ASN.1 alphanumeric identifier "%1" is too long (max allowed length %2)',$asn1,$maxlen));
394
                        }
395
 
396
                        // Validate identifier
397
                        if (!oid_id_is_valid($asn1)) throw new OIDplusException(_L('"%1" is not a valid ASN.1 identifier!',$asn1));
398
 
399
                        // Check if the (real) parent has any conflict
400
                        // Unlike IRI identifiers, ASN.1 identifiers may be used multiple times (not recommended), except if one of them is standardized
401
                        $res = OIDplus::db()->query("select oid from ###asn1id where name = ? and standardized = ?", array($asn1,true));
402
                        while ($row = $res->fetch_array()) {
403
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
404
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
405
                                   ($check_oid !== $this->oid))                    // different OID
406
                                {
407
                                        throw new OIDplusException(_L('ASN.1 identifier "%1" is a standardized identifier belonging to OID %2',$asn1,$check_oid));
408
                                }
409
                        }
410
                }
411
 
412
                // Now do the real replacement
413
                if (!$simulate) {
414
                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array("oid:".$this->oid));
415
                        foreach ($demandedASN1s as &$asn1) {
416
                                OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array("oid:".$this->oid, $asn1));
417
                        }
418
                }
419
        }
420
 
421
        public function replaceIris($demandedIris=array(), $simulate=false) {
422
                if ($this->isWellKnown()) {
423
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
424
                }
425
 
426
                // First do a few checks
427
                foreach ($demandedIris as &$iri) {
428
                        $iri = trim($iri);
429
 
430
                        if (strlen($iri) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN')) {
431
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN');
432
                                throw new OIDplusException(_L('Unicode label "%1" is too long (max allowed length %2)',$iri,$maxlen));
433
                        }
434
 
435
                        // Validate identifier
436
                        if (!iri_arc_valid($iri, false)) throw new OIDplusException(_L('"%1" is not a valid IRI!',$iri));
437
 
438
                        // Check if the (real) parent has any conflict
439
                        $res = OIDplus::db()->query("select oid from ###iri where name = ?", array($iri));
440
                        while ($row = $res->fetch_array()) {
441
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
442
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
443
                                   ($check_oid !== $this->oid))                    // different OID
444
                                {
445
                                        throw new OIDplusException(_L('IRI "%1" is already used by another OID (%2)',$iri,$check_oid));
446
                                }
447
                        }
448
                }
449
 
450
                // Now do the real replacement
451
                if (!$simulate) {
452
                        OIDplus::db()->query("delete from ###iri where oid = ?", array("oid:".$this->oid));
453
                        foreach ($demandedIris as &$iri) {
454
                                OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array("oid:".$this->oid, $iri));
455
                        }
456
                }
457
        }
458
 
459
        public function one_up() {
460
                return self::parse(self::ns().':'.oid_up($this->oid));
461
        }
462
 
463
        public function distance($to) {
464
                if (!is_object($to)) $to = OIDplusObject::parse($to);
465
                if (!($to instanceof $this)) return false;
466
                return oid_distance($to->oid, $this->oid);
467
        }
468
 
469
        public function getAltIds() {
470
                if ($this->isRoot()) return array();
471
                $ids = parent::getAltIds();
472
                if ($uuid = oid_to_uuid($this->oid)) {
473
                        $ids[] = new OIDplusAltId('guid', $uuid, _L('GUID representation of this OID'));
474
                }
475
                $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'));
476
                $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'));
477
                return $ids;
478
        }
479
 
480
        public function getDirectoryName() {
481
                if ($this->isRoot()) return $this->ns();
482
                $oid = $this->nodeId(false);
483
                return $this->ns().'_'.str_replace('.', '_', $oid);
484
        }
800 daniel-mar 485
 
486
        public function rootIconname($mode) {
487
                return 'img/'.$mode.'_icon16.png';
488
        }
635 daniel-mar 489
}