Subversion Repositories oidplus

Rev

Rev 1439 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1248 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
5
 * Copyright 2019 - 2023 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
namespace ViaThinkSoft\OIDplus;
21
 
22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
26
class OIDplusMac extends OIDplusObject {
27
        /**
28
         * @var int|string
29
         */
30
        private $number;
31
 
32
        /**
33
         * @param string|int $number
34
         */
35
        public function __construct($number) {
36
                // TODO: syntax checks
37
                $this->number = $number;
38
        }
39
 
40
        /**
41
         * @param string $node_id
42
         * @return OIDplusMac|null
43
         */
44
        public static function parse(string $node_id)/*: ?OIDplusMac*/ {
45
                @list($namespace, $number) = explode(':', $node_id, 2);
46
                if ($namespace !== self::ns()) return null;
47
                return new self($number);
48
        }
49
 
50
        /**
51
         * @return string
52
         */
53
        public static function objectTypeTitle(): string {
1255 daniel-mar 54
                return _L('MAC adresses (EUI/ELI/AAI/SAI)');
1248 daniel-mar 55
        }
56
 
57
        /**
58
         * @return string
59
         */
60
        public static function objectTypeTitleShort(): string {
1255 daniel-mar 61
                return _L('MAC');
1248 daniel-mar 62
        }
63
 
64
        /**
65
         * @return string
66
         */
67
        public static function ns(): string {
68
                return 'mac';
69
        }
70
 
71
        /**
72
         * @return string
73
         */
74
        public static function root(): string {
75
                return self::ns().':';
76
        }
77
 
78
        /**
79
         * @return bool
80
         */
81
        public function isRoot(): bool {
82
                return $this->number == '';
83
        }
84
 
85
        /**
86
         * @param bool $with_ns
87
         * @return string
88
         */
89
        public function nodeId(bool $with_ns=true): string {
90
                return $with_ns ? self::root().$this->number : $this->number;
91
        }
92
 
93
        /**
94
         * @param string $str
95
         * @return string
96
         * @throws OIDplusException
97
         */
98
        public function addString(string $str): string {
1261 daniel-mar 99
                $str = str_replace(array('-', ':', ' '), '', $str);
1248 daniel-mar 100
                $str = strtoupper($str);
101
 
102
                $test = preg_replace('@[0-9A-F]@', '', $str);
1254 daniel-mar 103
                if ($test != '') throw new OIDplusException(_L("Invalid characters entered"));
1248 daniel-mar 104
 
1255 daniel-mar 105
                $new_mac = $this->nodeId(false) . $str;
1254 daniel-mar 106
 
1255 daniel-mar 107
                $type = mac_type(str_pad($new_mac, 12, '0', STR_PAD_RIGHT));
108
                $type = substr($type, 0, 3);
1248 daniel-mar 109
 
1255 daniel-mar 110
                if (($type == 'ELI') || ($type == 'EUI')) {
111
                        if ($this->isRoot() && (strlen($str) < 6)) {
112
                                throw new OIDplusException(_L("The first node must be at least 24 bits long, since this is the smallest assignment for OUI/CID from IEEE."));
113
                        }
1248 daniel-mar 114
                }
115
 
1256 daniel-mar 116
                if (($type == 'ELI') || ($type == 'EUI') || ($type == 'AAI') || ($type == 'SAI')) {
117
                        // Note: AAI-48, AAI-64, SAI-48, and SAI-64 are defined in IEEE 802c-2017
1255 daniel-mar 118
                        if (strlen($new_mac) > 16) {
119
                                throw new OIDplusException(_L("The max length of an EUI-64 or ELI-64 is 64 bit"));
120
                        }
121
                }
122
 
123
                return $this->root().$new_mac;
1248 daniel-mar 124
        }
125
 
126
        /**
127
         * @param OIDplusObject $parent
128
         * @return string
129
         * @throws OIDplusException
130
         */
131
        public function crudShowId(OIDplusObject $parent): string {
1260 daniel-mar 132
                //return $this->chunkedNotation(false);
133
                return rtrim(chunk_split($this->number, 2, '-'), '-');
1248 daniel-mar 134
        }
135
 
136
        /**
137
         * @return string
138
         * @throws OIDplusException
139
         */
140
        public function crudInsertPrefix(): string {
141
                return $this->isRoot() ? '' : $this->chunkedNotation(false);
142
        }
143
 
144
        /**
145
         * @param OIDplusObject|null $parent
146
         * @return string
147
         */
148
        public function jsTreeNodeName(OIDplusObject $parent = null): string {
149
                if ($parent == null) return $this->objectTypeTitle();
150
                return substr($this->nodeId(), strlen($parent->nodeId()));
151
        }
152
 
153
        /**
154
         * @return string
155
         */
156
        public function defaultTitle(): string {
1261 daniel-mar 157
                //return $this->number;
158
                return rtrim(chunk_split($this->number, 2, '-'), '-');
1248 daniel-mar 159
        }
160
 
161
        /**
162
         * @return bool
163
         */
164
        public function isLeafNode(): bool {
165
                // Problem with this approach: If we are EUI-48 and want to add more (to get EUI-64), we couldn't.
166
                /*
167
                return mac_valid($this->nodeId(false));
168
                */
169
                return eui_bits($this->nodeId(false)) == 64;
170
        }
171
 
172
        /**
1324 daniel-mar 173
         * @return array
174
         */
175
        private function getTechInfo(): array {
176
                $tech_info = array();
177
 
178
                ob_start();
179
                try {
180
                        // TODO: OIDplus should download the *.txt files at "web-data"
181
                        decode_mac(mac_canonize($this->nodeId(false)));
182
                        $tech_info = ob_get_contents();
183
 
184
 
185
                        $lines = explode("\n", $tech_info);
186
                        $tech_info = [];
187
                        $key = '';
188
                        foreach ($lines as $line) {
189
                                $m1 = explode(':', $line);
190
                                if (!isset($m1[1])) $m1 = array($key, $m1[0]);
191
                                $key = $m1[0];
192
                                if (isset($tech_info[$key])) {
193
                                        $value = $tech_info[$key].'<br>'.$m1[1];
194
                                } else {
195
                                        $value = $m1[1];
196
                                }
197
                                $tech_info[$key] = $value;
198
                        }
199
 
200
                } catch (\Exception $e) {
201
                        $tech_info = [];
202
                }
203
                ob_end_clean();
204
 
205
                return $tech_info;
206
        }
207
 
208
        /**
1248 daniel-mar 209
         * @param string $title
210
         * @param string $content
211
         * @param string $icon
212
         * @return void
213
         * @throws OIDplusException
214
         */
215
        public function getContentPage(string &$title, string &$content, string &$icon) {
216
                $icon = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
217
 
218
                if ($this->isRoot()) {
219
                        $title = OIDplusMac::objectTypeTitle();
220
 
221
                        $res = OIDplus::db()->query("select * from ###objects where parent = ?", array(self::root()));
222
                        if ($res->any()) {
223
                                $content  = '<p>'._L('Please select an item in the tree view at the left to show its contents.').'</p>';
224
                        } else {
1258 daniel-mar 225
                                $content  = '<p>'._L('Currently, no MAC addresses are registered in the system.').'</p>';
1248 daniel-mar 226
                        }
227
 
228
                        if (!$this->isLeafNode()) {
229
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
230
                                        $content .= '<h2>'._L('Manage root objects').'</h2>';
231
                                } else {
232
                                        $content .= '<h2>'._L('Available objects').'</h2>';
233
                                }
234
                                $content .= '%%CRUD%%';
235
                        }
236
                } else {
237
                        $title = $this->getTitle();
238
 
1324 daniel-mar 239
                        $tech_info = $this->getTechInfo();
240
                        $tech_info_html = '';
241
                        if (count($tech_info) > 0) {
242
                                $tech_info_html .= '<h2>'._L('Technical information').'</h2>';
1386 daniel-mar 243
                                $tech_info_html .= '<div style="overflow:auto"><table border="0">';
1324 daniel-mar 244
                                foreach ($tech_info as $key => $value) {
1386 daniel-mar 245
                                        $tech_info_html .= '<tr><td valign="top" style="white-space: nowrap;">'.$key.': </td><td><code>'.$value.'</code></td></tr>';
1324 daniel-mar 246
                                }
1387 daniel-mar 247
                                $tech_info_html .= '</table></div>';
1248 daniel-mar 248
                        }
1324 daniel-mar 249
                        $content = $tech_info_html;
1248 daniel-mar 250
 
1250 daniel-mar 251
                        $chunked = $this->chunkedNotation(true);
252
                        if (!mac_valid($this->number)) {
253
                                $chunked .= ' ...';
254
                        }
255
 
1255 daniel-mar 256
                        $type = '';
257
                        try {
258
                                $type_raw = mac_type(str_pad($this->number, 12, '0', STR_PAD_RIGHT));
259
                                if (preg_match('@(.+) \\((.+)\\)@ismU', $type_raw, $m)) {
260
                                        $type_short = $m[1];
261
                                        $type_long = $m[2];
262
                                        $type = '<abbr title="'.htmlentities($type_long).'">'.htmlentities($type_short).'</abbr>';
263
                                } else {
264
                                        $type = htmlentities($type_raw);
265
                                }
266
                        } catch (\Exception $e) {};
1253 daniel-mar 267
 
1255 daniel-mar 268
                        $content  = '<h2>'.$type.' <strong>'.$chunked.'</strong></h2>';
1250 daniel-mar 269
                        $content .= $tech_info_html;
270
 
1248 daniel-mar 271
                        if ($this->isLeafNode()) {
272
                                $content .= '<h2>'._L('Description').'</h2>%%DESC%%';
273
                        } else {
274
                                $content .= '<h2>'._L('Description').'</h2>%%DESC%%';
275
                                if ($this->userHasWriteRights()) {
276
                                        $content .= '<h2>'._L('Create or change subordinate objects').'</h2>';
277
                                } else {
278
                                        $content .= '<h2>'._L('Subordinate objects').'</h2>';
279
                                }
280
                                $content .= '%%CRUD%%';
281
                        }
282
                }
283
        }
284
 
285
        # ---
286
 
287
        /**
288
         * @param bool $withAbbr
289
         * @return string
290
         * @throws OIDplusException
291
         */
292
        public function chunkedNotation(bool $withAbbr=true): string {
293
                $curid = self::root().$this->number;
294
 
295
                $obj = OIDplusObject::findFitting($curid);
296
                if (!$obj) return $this->number;
297
 
298
                $hints = array();
299
                $lengths = array(strlen($curid));
300
                while ($obj = OIDplusObject::findFitting($curid)) {
301
                        $objParent = $obj->getParent();
302
                        if (!$objParent) break;
1255 daniel-mar 303
                        $curid = $objParent->nodeId(true);
1248 daniel-mar 304
                        $hints[] = $obj->getTitle();
305
                        $lengths[] = strlen($curid);
306
                }
307
 
308
                array_shift($lengths);
309
                $chunks = array();
310
 
311
                $full = self::root().$this->number;
312
                foreach ($lengths as $len) {
313
                        $chunks[] = substr($full, $len);
314
                        $full = substr($full, 0, $len);
315
                }
316
 
317
                $hints = array_reverse($hints);
318
                $chunks = array_reverse($chunks);
319
 
320
                $full = array();
321
                foreach ($chunks as $c) {
322
                        $hint = array_shift($hints);
323
                        $full[] = $withAbbr && ($hint !== '') ? '<abbr title="'.htmlentities($hint).'">'.$c.'</abbr>' : $c;
324
                }
1250 daniel-mar 325
 
1248 daniel-mar 326
                return implode(' ', $full);
327
        }
328
 
329
        /**
330
         * @return OIDplusMac|null
331
         */
332
        public function one_up()/*: ?OIDplusMac*/ {
333
                return self::parse($this->ns().':'.substr($this->number,0,strlen($this->number)-1));
334
        }
335
 
336
        /**
337
         * @param string $a
338
         * @param string $b
339
         * @return false|int
340
         */
341
        private static function distance_(string $a, string $b) {
342
                $min_len = min(strlen($a), strlen($b));
343
 
344
                for ($i=0; $i<$min_len; $i++) {
345
                        if ($a[$i] != $b[$i]) return false;
346
                }
347
 
348
                return strlen($a) - strlen($b);
349
        }
350
 
351
        /**
352
         * @param OIDplusObject|string $to
353
         * @return int|null
354
         */
355
        public function distance($to) {
356
                if (!is_object($to)) $to = OIDplusObject::parse($to);
357
                if (!$to) return null;
358
                if (!($to instanceof $this)) return null;
359
 
360
                if ($this->number == $to->number) return 0;
361
 
362
                $b = $this->number;
363
                $a = $to->number;
364
                $tmp = self::distance_($a, $b);
365
                if ($tmp !== false) return $tmp;
366
 
367
                return null;
368
        }
369
 
370
        /**
371
         * @return array|OIDplusAltId[]
372
         * @throws OIDplusException
373
         */
374
        public function getAltIds(): array {
375
                if ($this->isRoot()) return array();
376
                $ids = parent::getAltIds();
377
 
1256 daniel-mar 378
                // (VTS F2) MAC address (EUI/ELI/...) to AID (PIX allowed)
379
                $size_nibble = strlen($this->number)-1;
1261 daniel-mar 380
                if (($size_nibble >= 0) && ($size_nibble <= 0xF)) {
1440 daniel-mar 381
                        $aid = 'D276000186F2'.strtoupper(dechex($size_nibble)).$this->number;
382
                        if ((strlen($aid)%2) == 1) $aid .= 'F';
1256 daniel-mar 383
                        $aid_is_ok = aid_canonize($aid);
1439 daniel-mar 384
                        if ($aid_is_ok) $ids[] = new OIDplusAltId('aid', $aid, _L('Application Identifier (ISO/IEC 7816)'), ' ('._L('Optional PIX allowed, without prefix').')', 'https://hosted.oidplus.com/viathinksoft/?goto=aid%3AD276000186F2');
1256 daniel-mar 385
                }
1248 daniel-mar 386
 
387
                return $ids;
388
        }
389
 
390
        /**
391
         * @return string
392
         */
393
        public function getDirectoryName(): string {
394
                if ($this->isRoot()) return $this->ns();
395
                return $this->ns().'_'.$this->nodeId(false); // safe, because there are only hexadecimal numbers
396
        }
397
 
398
        /**
399
         * @param string $mode
400
         * @return string
401
         */
402
        public static function treeIconFilename(string $mode): string {
403
                return 'img/'.$mode.'_icon16.png';
404
        }
405
}