Subversion Repositories oidplus

Rev

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