Subversion Repositories oidplus

Rev

Rev 753 | Rev 772 | 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
 
635 daniel-mar 124
        public function getContentPage(&$title, &$content, &$icon) {
644 daniel-mar 125
                $icon = file_exists(__DIR__.'/icon_big.png') ? OIDplus::webPath(__DIR__,true).'icon_big.png' : '';
635 daniel-mar 126
 
127
                if ($this->isRoot()) {
128
                        $title = OIDplusOid::objectTypeTitle();
129
 
130
                        $res = OIDplus::db()->query("select id from ###objects where parent = ?", array(self::root()));
131
                        if ($res->num_rows() > 0) {
132
                                $content = _L('Please select an OID in the tree view at the left to show its contents.');
133
                        } else {
134
                                $content = _L('Currently, no OID is registered in the system.');
135
                        }
136
 
137
                        if (!$this->isLeafNode()) {
138
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
139
                                        $content .= '<h2>'._L('Manage your root OIDs').'</h2>';
140
                                } else {
141
                                        $content .= '<h2>'._L('Root OIDs').'</h2>';
142
                                }
143
                                $content .= '%%CRUD%%';
144
                        }
145
                } else {
146
                        $title = $this->getTitle();
147
 
753 daniel-mar 148
                        $tech_info = $this->getTechInfo();
149
                        $tech_info_html = '';
150
                        if (count($tech_info) > 0) {
151
                                $tech_info_html .= '<h2>'._L('Technical information').'</h2>';
152
                                $tech_info_html .= '<table border="0">';
153
                                foreach ($tech_info as $key => $value) {
154
                                        $tech_info_html .= '<tr><td>'.$key.': </td><td><code>'.$value.'</code></td></tr>';
155
                                }
156
                                $tech_info_html .= '</table>';
157
                        }
635 daniel-mar 158
 
753 daniel-mar 159
                        $content = $tech_info_html;
160
 
161
                        $content .= '<h2>'._L('Description').'</h2>%%DESC%%'.
162
                                    '<h2>'._L('Registration Authority').'</h2>%%RA_INFO%%';
163
 
635 daniel-mar 164
                        if (!$this->isLeafNode()) {
165
                                if ($this->userHasWriteRights()) {
166
                                        $content .= '<h2>'._L('Create or change subsequent objects').'</h2>';
167
                                } else {
168
                                        $content .= '<h2>'._L('Subsequent objects').'</h2>';
169
                                }
170
                                $content .= '%%CRUD%%';
171
                        }
172
                }
173
        }
174
 
175
        # ---
176
 
683 daniel-mar 177
        // Gets the last arc of an WEID
635 daniel-mar 178
        public function weidArc() {
689 daniel-mar 179
                // Dirty hack: We prepend '0.' in front of the OID to enforce the
180
                //             creation of a Class A weid (weid:root:) . Otherwise we could not
181
                //             get the hidden arc value "8" from "weid:4" (which is actually "weid:pen:SZ5-8-?"
182
                $weid = WeidOidConverter::oid2weid('0.'.$this->getDotNotation());
635 daniel-mar 183
                if ($weid === false) return false;
683 daniel-mar 184
                $ary = explode(':', $weid);
185
                $weid = array_pop($ary); // remove namespace and sub-namespace if existing
635 daniel-mar 186
                $x = explode('-', $weid);
187
                if (count($x) < 2) return ''; // WEID root arc. Has no name
188
                return $x[count($x)-2];
189
        }
190
 
191
        public function getWeidNotation($withAbbr=true) {
192
                $weid = WeidOidConverter::oid2weid($this->getDotNotation());
193
                if ($withAbbr) {
683 daniel-mar 194
                        $ary = explode(':', $weid);
195
                        $weid = array_pop($ary); // remove namespace and sub-namespace if existing
196
                        $ns = implode(':', $ary).':';
197
 
635 daniel-mar 198
                        $weid_arcs = explode('-', $weid);
199
                        foreach ($weid_arcs as $i => &$weid) {
200
                                if ($i == count($weid_arcs)-1) {
201
                                        $weid = '<abbr title="'._L('weLuhn check digit').'">'.$weid.'</abbr>';
202
                                } else {
203
                                        $oid_arcs = explode('.',$this->oid);
204
                                        $weid_num = $oid_arcs[(count($oid_arcs)-1)-(count($weid_arcs)-1)+($i+1)];
205
                                        if ($weid_num != $weid) {
206
                                                $weid = '<abbr title="'._L('Numeric value').': '.$weid_num.'">'.$weid.'</abbr>';
207
                                        }
208
                                }
209
                        }
683 daniel-mar 210
                        $base_arc = '???';
211
                        if ($ns === 'weid:')      $base_arc = '1.3.6.1.4.1.37553.8';
212
                        if ($ns === 'weid:pen:')  $base_arc = '1.3.6.1.4.1';
688 daniel-mar 213
                        if ($ns === 'weid:root:') $base_arc = _L('OID tree root');
683 daniel-mar 214
 
215
                        $weid = '<abbr title="'._L('Base OID').': '.$base_arc.'">' . rtrim($ns,':') . '</abbr>:' . implode('-',$weid_arcs);
635 daniel-mar 216
                }
217
                return $weid;
218
        }
219
 
220
        public function appendArcs(String $arcs) {
221
                $out = new self($this->oid);
222
 
223
                if ($out->isRoot()) {
224
                        $out->oid .= $arcs;
225
                } else {
226
                        $out->oid .= '.' . $arcs;
227
                }
228
 
229
                $bak_oid = $out->oid;
230
                $out->oid = sanitizeOID($out->oid);
231
                if ($out->oid === false) throw new OIDplusException(_L('%1 is not a valid OID!',$bak_oid));
232
 
233
                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_ID_LENGTH')-strlen('oid:');
234
                if (strlen($out->oid) > $maxlen) {
235
                        throw new OIDplusException(_L('The resulting OID "%1" is too long (max allowed length: %2).',$out->oid,$maxlen));
236
                }
237
 
238
                $depth = 0;
239
                foreach (explode('.',$out->oid) as $arc) {
240
                        if (strlen($arc) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE')) {
241
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE');
242
                                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));
243
                        }
244
                        $depth++;
245
                }
246
                if ($depth > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH')) {
247
                        $maxdepth = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH');
248
                        throw new OIDplusException(_L('OID %1 has too many arcs (current depth %2, max depth %3)',$out->oid,$depth,$maxdepth));
249
                }
250
 
251
                return $out;
252
        }
253
 
254
        public function deltaDotNotation(OIDplusOid $parent) {
255
                if (!$parent->isRoot()) {
256
                        if (substr($this->oid, 0, strlen($parent->oid)+1) == $parent->oid.'.') {
257
                                return substr($this->oid, strlen($parent->oid)+1);
258
                        } else {
259
                                return false;
260
                        }
261
                } else {
262
                        return $this->oid;
263
                }
264
        }
265
 
266
        public function viewGetArcAsn1s(OIDplusOid $parent=null, $separator = ' | ') {
267
                $asn_ids = array();
268
 
269
                if (is_null($parent)) $parent = OIDplusOid::parse('oid:');
270
 
271
                $part = $this->deltaDotNotation($parent);
272
 
273
                if (strpos($part, '.') === false) {
274
                        $res2 = OIDplus::db()->query("select name from ###asn1id where oid = ? order by lfd", array("oid:".$this->oid));
275
                        while ($row2 = $res2->fetch_array()) {
276
                                $asn_ids[] = $row2['name'].'('.$part.')';
277
                        }
278
                }
279
 
280
                if (count($asn_ids) == 0) $asn_ids = array($part);
281
                return implode($separator, $asn_ids);
282
        }
283
 
284
        public function getAsn1Notation($withAbbr=true) {
285
                $asn1_notation = '';
286
                $arcs = explode('.', $this->oid);
287
 
288
                foreach ($arcs as $arc) {
289
                        $res = OIDplus::db()->query("select name, standardized from ###asn1id where oid = ? order by lfd", array('oid:'.implode('.',$arcs)));
290
 
291
                        $names = array();
292
                        while ($row = $res->fetch_array()) {
293
                                $names[] = $row['name']."(".end($arcs).")";
294
                                if ($row['standardized']) {
295
                                        $names[] = $row['name'];
296
                                }
297
                        }
298
 
299
                        $numeric = array_pop($arcs);
300
                        if (count($names) > 1) {
301
                                $first_name = array_shift($names);
302
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names);
303
                                if ($withAbbr) {
304
                                        $asn1_notation = '<abbr title="'.$abbr.'">'.$first_name.'</abbr> '.$asn1_notation;
305
                                } else {
306
                                        $asn1_notation = $first_name.' '.$asn1_notation;
307
                                }
308
                        } else if (count($names) == 1) {
309
                                $asn1_notation = array_shift($names).' '.$asn1_notation;
310
                        } else {
311
                                $asn1_notation = $numeric.' '.$asn1_notation;
312
                        }
313
                }
314
 
758 daniel-mar 315
                return "{ ".trim($asn1_notation)." }";
635 daniel-mar 316
        }
317
 
318
        public function getIriNotation($withAbbr=true) {
319
                $iri_notation = '';
320
                $arcs = explode('.', $this->oid);
321
 
322
                foreach ($arcs as $arc) {
323
                        $res = OIDplus::db()->query("select name, longarc from ###iri where oid = ? order by lfd", array('oid:'.implode('.',$arcs)));
324
 
325
                        $is_longarc = false;
326
                        $names = array();
327
                        while ($row = $res->fetch_array()) {
328
                                $is_longarc = $row['longarc'];
329
                                $names[] = $row['name'];
330
 
331
                                if ($is_longarc) {
332
                                        $names[] = 'Joint-ISO-ITU-T/'.$row['name']; // Long arcs can only be inside root OID 2
333
                                }
334
                        }
335
 
336
                        $names[] = array_pop($arcs);
337
                        if (count($names) > 2) {
338
                                $first_name = array_shift($names);
339
                                $numeric = array_pop($names);
340
                                $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names).'&#10;'._L('Numeric value').': '.$numeric;
341
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
342
                        } else if (count($names) > 1) {
343
                                $first_name = array_shift($names);
344
                                $abbr = _L('Numeric value').': '.array_shift($names);
345
                                $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
346
                        } else if (count($names) == 1) {
347
                                $iri_notation = array_shift($names) . '/' . $iri_notation;
348
                        }
349
 
350
                        if ($is_longarc) break; // we don't write /ITU-T/ at the beginning, when /ITU-T/xyz is a long arc
351
                }
352
                $iri_notation = '/' . substr($iri_notation, 0, strlen($iri_notation)-1);
353
 
354
                return $iri_notation;
355
        }
356
 
357
        public function getDotNotation() {
358
                return $this->oid;
359
        }
360
 
361
        public function isWellKnown() {
362
                $res = OIDplus::db()->query("select oid from ###asn1id where oid = ? and well_known = ?", array("oid:".$this->oid,true));
363
                if ($res->num_rows() > 0) return true;
364
 
365
                $res = OIDplus::db()->query("select oid from ###iri where oid = ? and well_known = ?", array("oid:".$this->oid,true));
366
                if ($res->num_rows() > 0) return true;
367
 
368
                return false;
369
        }
370
 
371
        public function replaceAsn1Ids($demandedASN1s=array(), $simulate=false) {
372
                if ($this->isWellKnown()) {
373
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
374
                }
375
 
376
                // First do a few checks
377
                foreach ($demandedASN1s as &$asn1) {
378
                        $asn1 = trim($asn1);
379
 
380
                        if (strlen($asn1) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN')) {
381
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN');
382
                                throw new OIDplusException(_L('ASN.1 alphanumeric identifier "%1" is too long (max allowed length %2)',$asn1,$maxlen));
383
                        }
384
 
385
                        // Validate identifier
386
                        if (!oid_id_is_valid($asn1)) throw new OIDplusException(_L('"%1" is not a valid ASN.1 identifier!',$asn1));
387
 
388
                        // Check if the (real) parent has any conflict
389
                        // Unlike IRI identifiers, ASN.1 identifiers may be used multiple times (not recommended), except if one of them is standardized
390
                        $res = OIDplus::db()->query("select oid from ###asn1id where name = ? and standardized = ?", array($asn1,true));
391
                        while ($row = $res->fetch_array()) {
392
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
393
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
394
                                   ($check_oid !== $this->oid))                    // different OID
395
                                {
396
                                        throw new OIDplusException(_L('ASN.1 identifier "%1" is a standardized identifier belonging to OID %2',$asn1,$check_oid));
397
                                }
398
                        }
399
                }
400
 
401
                // Now do the real replacement
402
                if (!$simulate) {
403
                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array("oid:".$this->oid));
404
                        foreach ($demandedASN1s as &$asn1) {
405
                                OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array("oid:".$this->oid, $asn1));
406
                        }
407
                }
408
        }
409
 
410
        public function replaceIris($demandedIris=array(), $simulate=false) {
411
                if ($this->isWellKnown()) {
412
                        throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
413
                }
414
 
415
                // First do a few checks
416
                foreach ($demandedIris as &$iri) {
417
                        $iri = trim($iri);
418
 
419
                        if (strlen($iri) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN')) {
420
                                $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN');
421
                                throw new OIDplusException(_L('Unicode label "%1" is too long (max allowed length %2)',$iri,$maxlen));
422
                        }
423
 
424
                        // Validate identifier
425
                        if (!iri_arc_valid($iri, false)) throw new OIDplusException(_L('"%1" is not a valid IRI!',$iri));
426
 
427
                        // Check if the (real) parent has any conflict
428
                        $res = OIDplus::db()->query("select oid from ###iri where name = ?", array($iri));
429
                        while ($row = $res->fetch_array()) {
430
                                $check_oid = OIDplusOid::parse($row['oid'])->oid;
431
                                if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
432
                                   ($check_oid !== $this->oid))                    // different OID
433
                                {
434
                                        throw new OIDplusException(_L('IRI "%1" is already used by another OID (%2)',$iri,$check_oid));
435
                                }
436
                        }
437
                }
438
 
439
                // Now do the real replacement
440
                if (!$simulate) {
441
                        OIDplus::db()->query("delete from ###iri where oid = ?", array("oid:".$this->oid));
442
                        foreach ($demandedIris as &$iri) {
443
                                OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array("oid:".$this->oid, $iri));
444
                        }
445
                }
446
        }
447
 
448
        public function one_up() {
449
                return self::parse(self::ns().':'.oid_up($this->oid));
450
        }
451
 
452
        public function distance($to) {
453
                if (!is_object($to)) $to = OIDplusObject::parse($to);
454
                if (!($to instanceof $this)) return false;
455
                return oid_distance($to->oid, $this->oid);
456
        }
457
 
458
        public function getAltIds() {
459
                if ($this->isRoot()) return array();
460
                $ids = parent::getAltIds();
461
                if ($uuid = oid_to_uuid($this->oid)) {
462
                        $ids[] = new OIDplusAltId('guid', $uuid, _L('GUID representation of this OID'));
463
                }
464
                $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'));
465
                $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'));
466
                return $ids;
467
        }
468
 
469
        public function getDirectoryName() {
470
                if ($this->isRoot()) return $this->ns();
471
                $oid = $this->nodeId(false);
472
                return $this->ns().'_'.str_replace('.', '_', $oid);
473
        }
474
}