Subversion Repositories oidplus

Rev

Rev 1420 | Rev 1459 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
919 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
919 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;
919 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
 
919 daniel-mar 26
class OIDplusAid extends OIDplusObject {
1130 daniel-mar 27
        /**
28
         * @var string
29
         */
919 daniel-mar 30
        private $aid;
31
 
1116 daniel-mar 32
        /**
1130 daniel-mar 33
         * @param string $aid
1116 daniel-mar 34
         */
1130 daniel-mar 35
        public function __construct(string $aid) {
919 daniel-mar 36
                // TODO: syntax checks
37
                $this->aid = $aid;
38
        }
39
 
1116 daniel-mar 40
        /**
41
         * @param string $node_id
42
         * @return OIDplusAid|null
43
         */
44
        public static function parse(string $node_id)/*: ?OIDplusAid*/ {
919 daniel-mar 45
                @list($namespace, $aid) = explode(':', $node_id, 2);
1116 daniel-mar 46
                if ($namespace !== self::ns()) return null;
919 daniel-mar 47
                return new self($aid);
48
        }
49
 
1116 daniel-mar 50
        /**
51
         * @return string
52
         */
53
        public static function objectTypeTitle(): string {
959 daniel-mar 54
                return _L('Application Identifier (ISO/IEC 7816)');
919 daniel-mar 55
        }
56
 
1116 daniel-mar 57
        /**
58
         * @return string
59
         */
60
        public static function objectTypeTitleShort(): string {
919 daniel-mar 61
                return _L('AID');
62
        }
63
 
1116 daniel-mar 64
        /**
65
         * @return string
66
         */
67
        public static function ns(): string {
919 daniel-mar 68
                return 'aid';
69
        }
70
 
1116 daniel-mar 71
        /**
72
         * @return string
73
         */
74
        public static function root(): string {
919 daniel-mar 75
                return self::ns().':';
76
        }
77
 
1116 daniel-mar 78
        /**
79
         * @return bool
80
         */
81
        public function isRoot(): bool {
919 daniel-mar 82
                return $this->aid == '';
83
        }
84
 
1116 daniel-mar 85
        /**
86
         * @param bool $with_ns
87
         * @return string
88
         */
89
        public function nodeId(bool $with_ns=true): string {
919 daniel-mar 90
                return $with_ns ? self::root().$this->aid : $this->aid;
91
        }
92
 
1116 daniel-mar 93
        /**
94
         * @param string $str
95
         * @return string
96
         * @throws OIDplusException
97
         */
98
        public function addString(string $str): string {
919 daniel-mar 99
                $m = array();
922 daniel-mar 100
 
101
                $str = str_replace(' ','',$str);
931 daniel-mar 102
                $str = str_replace(':','',$str);
922 daniel-mar 103
 
919 daniel-mar 104
                if (!preg_match('@^[0-9a-fA-F]+$@', $str, $m)) {
105
                        throw new OIDplusException(_L('AID part needs to be hexadecimal'));
106
                }
107
 
925 daniel-mar 108
                if (strlen($this->nodeId(false).$str) > 32) {
924 daniel-mar 109
                        throw new OIDplusException(_L('An AID has a maximum length of 16 bytes'));
919 daniel-mar 110
                }
111
 
930 daniel-mar 112
                // removed, because for D2 76 00 01 86 F... it makes sense to have your root (which is inside a foreign RID) being your OIDplus root
113
                /*
925 daniel-mar 114
                $pre   = $this->nodeId(false);
115
                $add   = strtoupper($str);
116
                $after = $pre.$add;
117
                $rid = '?';
118
                $pix = '?';
119
                $p = aid_split_rid_pix($after, $rid, $pix);
120
                if ($p > 1) { // Why $p>1? For "F", there is no RID. We allow that somebody include "F" in the first node
121
                        if ((strlen($pre)<$p) && (strlen($after)>$p)) {
122
                                $rid = substr($rid,strlen($pre));
123
                                throw new OIDplusException(_L('This node would mix RID (registry ID) and PIX (application specific). Please split it into two nodes "%1" and "%2".',$rid,$pix));
124
                        }
125
                }
930 daniel-mar 126
                */
925 daniel-mar 127
 
128
                return $this->nodeId(true).strtoupper($str);
919 daniel-mar 129
        }
130
 
1116 daniel-mar 131
        /**
132
         * @param OIDplusObject $parent
133
         * @return string
1130 daniel-mar 134
         * @throws OIDplusException
1116 daniel-mar 135
         */
136
        public function crudShowId(OIDplusObject $parent): string {
919 daniel-mar 137
                return $this->chunkedNotation(false);
138
        }
139
 
1116 daniel-mar 140
        /**
141
         * @return string
1130 daniel-mar 142
         * @throws OIDplusException
1116 daniel-mar 143
         */
144
        public function crudInsertPrefix(): string {
919 daniel-mar 145
                return $this->isRoot() ? '' : $this->chunkedNotation(false);
146
        }
147
 
1116 daniel-mar 148
        /**
149
         * @param OIDplusObject|null $parent
150
         * @return string
151
         */
152
        public function jsTreeNodeName(OIDplusObject $parent = null): string {
919 daniel-mar 153
                if ($parent == null) return $this->objectTypeTitle();
154
                return substr($this->nodeId(), strlen($parent->nodeId()));
155
        }
156
 
1116 daniel-mar 157
        /**
158
         * @return string
159
         */
160
        public function defaultTitle(): string {
1261 daniel-mar 161
                //return $this->aid;
162
                return rtrim(chunk_split($this->aid, 2, ' '), ' ');
919 daniel-mar 163
        }
164
 
1116 daniel-mar 165
        /**
166
         * @return bool
167
         */
168
        public function isLeafNode(): bool {
962 daniel-mar 169
                // We don't know when an AID is "leaf", because an AID can have an arbitary length <= 16 Bytes.
170
                // But if it is 16 bytes long (32 nibbles), then we are 100% certain that it is a leaf node.
171
                return (strlen($this->nodeId(false)) == 32);
919 daniel-mar 172
        }
173
 
1116 daniel-mar 174
        /**
175
         * @param string $title
176
         * @param string $content
177
         * @param string $icon
178
         * @return void
179
         * @throws OIDplusException
180
         */
181
        public function getContentPage(string &$title, string &$content, string &$icon) {
919 daniel-mar 182
                $icon = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
183
 
184
                if ($this->isRoot()) {
185
                        $title = OIDplusAid::objectTypeTitle();
186
 
187
                        $res = OIDplus::db()->query("select * from ###objects where parent = ?", array(self::root()));
188
                        if ($res->any()) {
962 daniel-mar 189
                                $content  = '<p>'._L('Please select an item in the tree view at the left to show its contents.').'</p>';
919 daniel-mar 190
                        } else {
962 daniel-mar 191
                                $content  = '<p>'._L('Currently, no Application Identifiers are registered in the system.').'</p>';
919 daniel-mar 192
                        }
193
 
194
                        if (!$this->isLeafNode()) {
195
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
196
                                        $content .= '<h2>'._L('Manage root objects').'</h2>';
197
                                } else {
198
                                        $content .= '<h2>'._L('Available objects').'</h2>';
199
                                }
200
                                $content .= '%%CRUD%%';
201
                        }
202
                } else {
203
                        $title = $this->getTitle();
204
 
205
                        $chunked = $this->chunkedNotation(true);
206
                        $content = '<h2>'.$chunked.'</h2>';
922 daniel-mar 207
 
924 daniel-mar 208
                        $tmp = decode_aid($this->aid,true);
209
                        $tmp = htmlentities($tmp);
210
                        $tmp = str_replace(' ','&nbsp;',$tmp);
211
                        $tmp = nl2br($tmp);
1401 daniel-mar 212
                        $tmp = preg_replace('@(warning|invalid|error|illegal(&nbsp;usage){0,1})@i', '<span class="aid_decoder_errortext">\\1</span>', $tmp);
1420 daniel-mar 213
 
214
                        # TODO: am besten farbmarkierung innerhalb c_literal_machen ? mit <abbr> und dann <abbr> irgendwie behandeln?
1401 daniel-mar 215
                        $tmp = preg_replace('@(\\\\\\d{3})@i', '<span class="aid_decoder_specialhexchar">\\1</span>', $tmp);
1420 daniel-mar 216
                        $tmp = preg_replace('@(\\\\x[0-9A-Fa-f]{2})@i', '<span class="aid_decoder_specialhexchar">\\1</span>', $tmp);
924 daniel-mar 217
 
922 daniel-mar 218
                        $content .= '<h2>'._L('Decoding').'</h2>';
219
                        $content .= '<table border="0">';
1391 daniel-mar 220
                        $content .= '<div style="overflow:auto;white-space:nowrap"><code>'.$tmp.'</code></div>';
922 daniel-mar 221
                        $content .= '</table>';
222
 
924 daniel-mar 223
                        $content .= '<h2>'._L('Description').'</h2>%%DESC%%';
919 daniel-mar 224
                        if ($this->userHasWriteRights()) {
928 daniel-mar 225
                                $content .= '<h2>'._L('Create or change subordinate objects').'</h2>';
919 daniel-mar 226
                        } else {
928 daniel-mar 227
                                $content .= '<h2>'._L('Subordinate objects').'</h2>';
919 daniel-mar 228
                        }
229
                        $content .= '%%CRUD%%';
230
                }
231
        }
232
 
233
        # ---
234
 
1116 daniel-mar 235
        /**
1130 daniel-mar 236
         * @param bool $withAbbr
1116 daniel-mar 237
         * @return string
238
         * @throws OIDplusException
239
         */
1130 daniel-mar 240
        public function chunkedNotation(bool $withAbbr=true): string {
919 daniel-mar 241
                $curid = self::root().$this->aid;
242
 
977 daniel-mar 243
                $obj = OIDplusObject::findFitting($curid);
244
                if (!$obj) return $this->aid;
919 daniel-mar 245
 
246
                $hints = array();
247
                $lengths = array(strlen($curid));
977 daniel-mar 248
                while ($obj = OIDplusObject::findFitting($curid)) {
249
                        $objParent = $obj->getParent();
250
                        if (!$objParent) break;
251
                        $curid = $objParent->nodeId();
252
                        $hints[] = $obj->getTitle();
919 daniel-mar 253
                        $lengths[] = strlen($curid);
254
                }
255
 
256
                array_shift($lengths);
257
                $chunks = array();
258
 
259
                $full = self::root().$this->aid;
260
                foreach ($lengths as $len) {
261
                        $chunks[] = substr($full, $len);
262
                        $full = substr($full, 0, $len);
263
                }
264
 
265
                $hints = array_reverse($hints);
266
                $chunks = array_reverse($chunks);
267
 
268
                $full = array();
269
                foreach ($chunks as $c) {
934 daniel-mar 270
                        $hint = array_shift($hints);
271
                        $full[] = $withAbbr && ($hint !== '') ? '<abbr title="'.htmlentities($hint).'">'.$c.'</abbr>' : $c;
919 daniel-mar 272
                }
273
                return implode(' ', $full);
274
        }
275
 
1116 daniel-mar 276
        /**
277
         * @return OIDplusAid|null
278
         */
279
        public function one_up()/*: ?OIDplusAid*/ {
280
                return self::parse($this->ns().':'.substr($this->aid,0,strlen($this->aid)-1));
919 daniel-mar 281
        }
282
 
1116 daniel-mar 283
        /**
1130 daniel-mar 284
         * @param OIDplusObject|string $to
1116 daniel-mar 285
         * @return int|null
286
         */
919 daniel-mar 287
        public function distance($to) {
288
                if (!is_object($to)) $to = OIDplusObject::parse($to);
1121 daniel-mar 289
                if (!$to) return null;
1116 daniel-mar 290
                if (!($to instanceof $this)) return null;
919 daniel-mar 291
 
292
                $a = $to->aid;
293
                $b = $this->aid;
294
 
295
                $ary = $a;
296
                $bry = $b;
297
 
298
                $min_len = min(strlen($ary), strlen($bry));
299
 
300
                for ($i=0; $i<$min_len; $i++) {
1116 daniel-mar 301
                        if ($ary[$i] != $bry[$i]) return null;
919 daniel-mar 302
                }
303
 
304
                return strlen($ary) - strlen($bry);
305
        }
306
 
1116 daniel-mar 307
        /**
308
         * @return array|OIDplusAltId[]
309
         * @throws OIDplusException
310
         */
311
        public function getAltIds(): array {
930 daniel-mar 312
                if ($this->isRoot()) return array();
313
                $ids = parent::getAltIds();
314
 
945 daniel-mar 315
                $aid = $this->nodeId(false);
316
                $aid = strtoupper($aid);
317
 
1077 daniel-mar 318
                // ViaThinkSoft proprietary AIDs
319
 
320
                // (VTS B1) Members
321
                if ($aid == 'D276000186B1') {
322
                        $oid = '1.3.6.1.4.1.37476.1';
323
                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
324
                }
325
 
326
                if (preg_match('@^D276000186B1(....)$@', $aid, $m)) {
327
                        $oid = '1.3.6.1.4.1.37476.1.'.ltrim($m[1],'0');
328
                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
329
                }
330
 
331
                // (VTS B2) Products
332
                if ($aid == 'D276000186B2') {
333
                        $oid = '1.3.6.1.4.1.37476.2';
334
                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
335
                }
336
 
337
                if (preg_match('@^D276000186B2(....)$@', $aid, $m)) {
338
                        $oid = '1.3.6.1.4.1.37476.2.'.ltrim($m[1],'0');
339
                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
340
                }
341
 
1078 daniel-mar 342
                // (VTS B2 00 05) OIDplus Information Objects AID
343
                // Attention: D276000186B20005 does NOT represent 1.3.6.1.4.1.37476.30.9
344
                //            because the mapping to OIDplus systems only applies for 00......-7F...... (31 bit hash)
345
 
346
                if (preg_match('@^D276000186B20005([0-7].......)$@', $aid, $m)) {
347
                        $oid = '1.3.6.1.4.1.37476.30.9.'.hexdec($m[1]);
348
                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
349
                }
350
 
351
                if (preg_match('@^D276000186B20005([0-7].......)([0-7].......)$@', $aid, $m)) {
352
                        $oid = '1.3.6.1.4.1.37476.30.9.'.hexdec($m[1]).'.'.hexdec($m[2]);
353
                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
354
                }
355
 
1079 daniel-mar 356
                // ViaThinkSoft "Example" AID
357
 
358
                if ($aid == 'D276000186E0') {
359
                        // Note that the OID object type plugin also maps children of 2.999 to AID,
360
                        // using a hash. But since this is not unique and cannot be reverted,
361
                        // we cannot have an reverse lookup/map.
362
                        $ids[] = new OIDplusAltId('oid', '2.999', _L('Object Identifier (OID)'), ' ('._L('Optional PIX allowed, without prefix').')');
363
                }
364
 
930 daniel-mar 365
                // ViaThinkSoft "Foreign" AIDs
366
 
945 daniel-mar 367
                // (VTS F0) IANA PEN + PIX
368
                // Resolve only if there is no PIX
369
                if (str_starts_with($aid,'D276000186F0')) {
370
                        $rest = substr($aid,strlen('D276000186F0'));
371
                        $p = strpos($rest,'F');
372
                        if ($p !== false) {
373
                                $pen = substr($rest,0,$p);
374
                                $pix = substr($rest,$p+1);
375
                        } else {
376
                                $pen = $rest;
377
                                $pix = '';
378
                        }
379
                        if (($pix === '') && preg_match('/^[0-9]+$/',$pen,$m)) {
380
                                $oid = '1.3.6.1.4.1.'.$pen;
381
                                $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
961 daniel-mar 382
                                $ids[] = new OIDplusAltId('iana-pen', $pen, _L('IANA Private Enterprise Number (PEN)'));
945 daniel-mar 383
                        }
384
                }
385
 
386
                // (VTS F1) ViaThinkSoft FreeOID + PIX
387
                // Resolve only if there is no PIX
388
                if (str_starts_with($aid,'D276000186F1')) {
389
                        $rest = substr($aid,strlen('D276000186F1'));
390
                        $p = strpos($rest,'F');
391
                        if ($p !== false) {
392
                                $number = substr($rest,0,$p);
393
                                $pix = substr($rest,$p+1);
394
                        } else {
395
                                $number = $rest;
396
                                $pix = '';
397
                        }
398
                        if (($pix === '') && preg_match('/^[0-9]+$/',$number,$m)) {
399
                                $oid = '1.3.6.1.4.1.37476.9000.'.$number;
400
                                $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
401
                        }
402
                }
403
 
1256 daniel-mar 404
                // (VTS F2) MAC address (EUI/ELI/...) + PIX
945 daniel-mar 405
                // Resolve only if there is no PIX
406
                if (str_starts_with($aid,'D276000186F2')) {
1256 daniel-mar 407
                        $size_nibble = substr($aid,strlen('D276000186F2'),1);
1257 daniel-mar 408
                        if ($size_nibble != '') {
1440 daniel-mar 409
                                $mac = substr($aid, strlen('D276000186F2'.$size_nibble), hexdec($size_nibble) + 1);
410
                                $test_aid = 'D276000186F2'.$size_nibble.$mac;
411
                                if (strlen($test_aid)%2 == 1) $test_aid .= 'F'; // padding
412
                                if ($aid == $test_aid) {
1257 daniel-mar 413
                                        $mac_type = mac_type(str_pad($mac, 12, '0', STR_PAD_RIGHT));
414
                                        $ids[] = new OIDplusAltId('mac', $mac, $mac_type);
415
                                }
945 daniel-mar 416
                        }
417
                }
418
 
419
                // (VTS F3) USB-IF VendorID + PIX
420
                // Resolve only if there is no PIX
421
                if (str_starts_with($aid,'D276000186F3')) {
422
                        $rest = substr($aid,strlen('D276000186F3'));
423
                        if (strlen($rest) == 4) {
424
                                $vid = $rest;
425
                                $ids[] = new OIDplusAltId('usb-vendor-id', $vid, _L('USB-IF (usb.org) VendorID'));
426
                        }
427
                }
428
 
429
                // (VTS F4) D-U-N-S number + PIX
430
                // Resolve only if there is no PIX
431
                if (str_starts_with($aid,'D276000186F4')) {
432
                        $rest = substr($aid,strlen('D276000186F4'));
433
                        $p = strpos($rest,'F');
434
                        if ($p !== false) {
435
                                $duns = substr($rest,0,$p);
436
                                $pix = substr($rest,$p+1);
437
                        } else {
438
                                $duns = $rest;
439
                                $pix = '';
440
                        }
441
                        if (($pix === '') && preg_match('/^[0-9]+$/',$duns,$m)) {
442
                                $ids[] = new OIDplusAltId('duns', $duns, _L('Data Universal Numbering System (D-U-N-S)'));
443
                        }
444
                }
445
 
446
                // (VTS F5) GS1 number + PIX
447
                // Resolve only if there is no PIX
448
                if (str_starts_with($aid,'D276000186F5')) {
449
                        $rest = substr($aid,strlen('D276000186F5'));
450
                        $p = strpos($rest,'F');
451
                        if ($p !== false) {
452
                                $gs1 = substr($rest,0,$p);
453
                                $pix = substr($rest,$p+1);
454
                        } else {
455
                                $gs1 = $rest;
456
                                $pix = '';
457
                        }
458
                        if (($pix === '') && preg_match('/^[0-9]+$/',$gs1,$m)) {
459
                                $ids[] = new OIDplusAltId('gs1', $gs1, _L('GS1 Based IDs (GLN/GTIN/SSCC/...)'), ' ('._L('without check-digit').')');
460
                        }
461
                }
462
 
463
                // (VTS F6) OID<->AID, no PIX
930 daniel-mar 464
                if (str_starts_with($aid,'D276000186F6')) {
465
                        $der = substr($aid,strlen('D276000186F6'));
466
                        $len = strlen($der);
467
                        if ($len%2 == 0) {
468
                                $len /= 2;
469
                                $len = str_pad("$len", 2, '0', STR_PAD_LEFT);
470
                                $type = '06'; // absolute OID
471
                                $der = "$type $len $der";
1050 daniel-mar 472
                                $oid = \OidDerConverter::derToOID(\OidDerConverter::hexStrToArray($der));
930 daniel-mar 473
                                if ($oid) {
474
                                        $oid = ltrim($oid,'.');
475
                                        $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
476
                                }
477
                        }
478
                }
479
 
933 daniel-mar 480
                // The case E8... (Standard OID 1.0) doesn't need to be addressed here, because it is already shown in the AID decoder (and it is ambiguous since DER and PIX are mixed)
945 daniel-mar 481
                // TODO: If it has no pix, then resolve it !!! but how do we know if there is a PIX or a part ID ?
933 daniel-mar 482
 
930 daniel-mar 483
                return $ids;
484
        }
485
 
1116 daniel-mar 486
        /**
487
         * @return string
488
         */
489
        public function getDirectoryName(): string {
919 daniel-mar 490
                if ($this->isRoot()) return $this->ns();
491
                return $this->ns().'_'.$this->nodeId(false); // safe, because there are only AIDs
492
        }
493
 
1116 daniel-mar 494
        /**
495
         * @param string $mode
496
         * @return string
497
         */
498
        public static function treeIconFilename(string $mode): string {
919 daniel-mar 499
                return 'img/'.$mode.'_icon16.png';
500
        }
501
}