Subversion Repositories oidplus

Rev

Rev 1440 | Rev 1466 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  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 OIDplusAid extends OIDplusObject {
  27.         /**
  28.          * @var string
  29.          */
  30.         private $aid;
  31.  
  32.         /**
  33.          * @param string $aid
  34.          */
  35.         public function __construct(string $aid) {
  36.                 // TODO: syntax checks
  37.                 $this->aid = $aid;
  38.         }
  39.  
  40.         /**
  41.          * @param string $node_id
  42.          * @return OIDplusAid|null
  43.          */
  44.         public static function parse(string $node_id)/*: ?OIDplusAid*/ {
  45.                 @list($namespace, $aid) = explode(':', $node_id, 2);
  46.                 if ($namespace !== self::ns()) return null;
  47.                 return new self($aid);
  48.         }
  49.  
  50.         /**
  51.          * @return string
  52.          */
  53.         public static function objectTypeTitle(): string {
  54.                 return _L('Application Identifier (ISO/IEC 7816)');
  55.         }
  56.  
  57.         /**
  58.          * @return string
  59.          */
  60.         public static function objectTypeTitleShort(): string {
  61.                 return _L('AID');
  62.         }
  63.  
  64.         /**
  65.          * @return string
  66.          */
  67.         public static function ns(): string {
  68.                 return 'aid';
  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->aid == '';
  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->aid : $this->aid;
  91.         }
  92.  
  93.         /**
  94.          * @param string $str
  95.          * @return string
  96.          * @throws OIDplusException
  97.          */
  98.         public function addString(string $str): string {
  99.                 $m = array();
  100.  
  101.                 $str = str_replace(' ','',$str);
  102.                 $str = str_replace(':','',$str);
  103.  
  104.                 if (!preg_match('@^[0-9a-fA-F]+$@', $str, $m)) {
  105.                         throw new OIDplusException(_L('AID part needs to be hexadecimal'));
  106.                 }
  107.  
  108.                 if (strlen($this->nodeId(false).$str) > 32) {
  109.                         throw new OIDplusException(_L('An AID has a maximum length of 16 bytes'));
  110.                 }
  111.  
  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.                 /*
  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.                 }
  126.                 */
  127.  
  128.                 return $this->nodeId(true).strtoupper($str);
  129.         }
  130.  
  131.         /**
  132.          * @param OIDplusObject $parent
  133.          * @return string
  134.          * @throws OIDplusException
  135.          */
  136.         public function crudShowId(OIDplusObject $parent): string {
  137.                 return $this->chunkedNotation(false);
  138.         }
  139.  
  140.         /**
  141.          * @return string
  142.          * @throws OIDplusException
  143.          */
  144.         public function crudInsertPrefix(): string {
  145.                 return $this->isRoot() ? '' : $this->chunkedNotation(false);
  146.         }
  147.  
  148.         /**
  149.          * @param OIDplusObject|null $parent
  150.          * @return string
  151.          */
  152.         public function jsTreeNodeName(OIDplusObject $parent = null): string {
  153.                 if ($parent == null) return $this->objectTypeTitle();
  154.                 return substr($this->nodeId(), strlen($parent->nodeId()));
  155.         }
  156.  
  157.         /**
  158.          * @return string
  159.          */
  160.         public function defaultTitle(): string {
  161.                 //return $this->aid;
  162.                 return rtrim(chunk_split($this->aid, 2, ' '), ' ');
  163.         }
  164.  
  165.         /**
  166.          * @return bool
  167.          */
  168.         public function isLeafNode(): bool {
  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);
  172.         }
  173.  
  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) {
  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()) {
  189.                                 $content  = '<p>'._L('Please select an item in the tree view at the left to show its contents.').'</p>';
  190.                         } else {
  191.                                 $content  = '<p>'._L('Currently, no Application Identifiers are registered in the system.').'</p>';
  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>';
  207.  
  208.                         $tmp = decode_aid($this->aid,true);
  209.                         $tmp = htmlentities($tmp);
  210.                         $tmp = str_replace(' ','&nbsp;',$tmp);
  211.                         $tmp = nl2br($tmp);
  212.                         $tmp = preg_replace('@(warning|invalid|error|illegal(&nbsp;usage){0,1})@i', '<span class="aid_decoder_errortext">\\1</span>', $tmp);
  213.  
  214.                         # TODO: am besten farbmarkierung innerhalb c_literal_machen ? mit <abbr> und dann <abbr> irgendwie behandeln?
  215.                         $tmp = preg_replace('@(\\\\\\d{3})@i', '<span class="aid_decoder_specialhexchar">\\1</span>', $tmp);
  216.                         $tmp = preg_replace('@(\\\\x[0-9A-Fa-f]{2})@i', '<span class="aid_decoder_specialhexchar">\\1</span>', $tmp);
  217.  
  218.                         $content .= '<h2>'._L('Decoding').'</h2>';
  219.                         $content .= '<table border="0">';
  220.                         $content .= '<div style="overflow:auto;white-space:nowrap"><code>'.$tmp.'</code></div>';
  221.                         $content .= '</table>';
  222.  
  223.                         $content .= '<h2>'._L('Description').'</h2>%%DESC%%';
  224.                         if ($this->userHasWriteRights()) {
  225.                                 $content .= '<h2>'._L('Create or change subordinate objects').'</h2>';
  226.                         } else {
  227.                                 $content .= '<h2>'._L('Subordinate objects').'</h2>';
  228.                         }
  229.                         $content .= '%%CRUD%%';
  230.                 }
  231.         }
  232.  
  233.         # ---
  234.  
  235.         /**
  236.          * @param bool $withAbbr
  237.          * @return string
  238.          * @throws OIDplusException
  239.          */
  240.         public function chunkedNotation(bool $withAbbr=true): string {
  241.                 $curid = self::root().$this->aid;
  242.  
  243.                 $obj = OIDplusObject::findFitting($curid);
  244.                 if (!$obj) return $this->aid;
  245.  
  246.                 $hints = array();
  247.                 $lengths = array(strlen($curid));
  248.                 while ($obj = OIDplusObject::findFitting($curid)) {
  249.                         $objParent = $obj->getParent();
  250.                         if (!$objParent) break;
  251.                         $curid = $objParent->nodeId();
  252.                         $hints[] = $obj->getTitle();
  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) {
  270.                         $hint = array_shift($hints);
  271.                         $full[] = $withAbbr && ($hint !== '') ? '<abbr title="'.htmlentities($hint).'">'.$c.'</abbr>' : $c;
  272.                 }
  273.                 return implode(' ', $full);
  274.         }
  275.  
  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));
  281.         }
  282.  
  283.         /**
  284.          * @param OIDplusObject|string $to
  285.          * @return int|null
  286.          */
  287.         public function distance($to) {
  288.                 if (!is_object($to)) $to = OIDplusObject::parse($to);
  289.                 if (!$to) return null;
  290.                 if (!($to instanceof $this)) return null;
  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++) {
  301.                         if ($ary[$i] != $bry[$i]) return null;
  302.                 }
  303.  
  304.                 return strlen($ary) - strlen($bry);
  305.         }
  306.  
  307.         /**
  308.          * @return array|OIDplusAltId[]
  309.          * @throws OIDplusException
  310.          */
  311.         public function getAltIds(): array {
  312.                 if ($this->isRoot()) return array();
  313.                 $ids = parent::getAltIds();
  314.  
  315.                 $aid = $this->nodeId(false);
  316.                 $aid = strtoupper($aid);
  317.  
  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.  
  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.  
  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.  
  365.                 // ViaThinkSoft "Foreign" AIDs
  366.  
  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)'));
  382.                                 $ids[] = new OIDplusAltId('iana-pen', $pen, _L('IANA Private Enterprise Number (PEN)'));
  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.  
  404.                 // (VTS F2) MAC address (EUI/ELI/...) + PIX
  405.                 // Resolve only if there is no PIX
  406.                 if (str_starts_with($aid,'D276000186F2')) {
  407.                         $size_nibble = substr($aid,strlen('D276000186F2'),1);
  408.                         if ($size_nibble != '') {
  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) {
  413.                                         $mac_type = mac_type(str_pad($mac, 12, '0', STR_PAD_RIGHT));
  414.                                         $ids[] = new OIDplusAltId('mac', $mac, $mac_type);
  415.                                 }
  416.                         }
  417.                 }
  418.  
  419.                 // (VTS F3 01) USB-IF VendorID + ProductID + PIX
  420.                 // Resolve only if there is no PIX
  421.                 if (str_starts_with($aid,'D276000186F301')) {
  422.                         $rest = substr($aid,strlen('D276000186F301'));
  423.                         if (strlen($rest) == 4) {
  424.                                 $vid = $rest;
  425.                                 $ids[] = new OIDplusAltId('usb-vendor-id', $vid, _L('USB-IF (usb.org) VendorID'));
  426.                         } else if (strlen($rest) == 8) {
  427.                                 $vid_pid = substr($rest, 0, 4) . ':' . substr($rest, 4);;
  428.                                 $ids[] = new OIDplusAltId('usb-vendor-product-id', $vid_pid, _L('USB-IF (usb.org) VendorID/ProductID'));
  429.                         }
  430.                 }
  431.  
  432.                 // (VTS F3 02) PCI-SIG VendorID + ProductID + PIX
  433.                 // Resolve only if there is no PIX
  434.                 if (str_starts_with($aid,'D276000186F302')) {
  435.                         $rest = substr($aid,strlen('D276000186F302'));
  436.                         if (strlen($rest) == 4) {
  437.                                 $vid = $rest;
  438.                                 $ids[] = new OIDplusAltId('pci-vendor-id', $vid, _L('PCI-SIG (pcisig.com) VendorID'));
  439.                         } else if (strlen($rest) == 8) {
  440.                                 $vid_pid = substr($rest, 0, 4) . ':' . substr($rest, 4);;
  441.                                 $ids[] = new OIDplusAltId('pci-vendor-product-id', $vid_pid, _L('PCI-SIG (pcisig.com)VendorID/ProductID'));
  442.                         }
  443.                 }
  444.  
  445.                 // (VTS F4) D-U-N-S number + PIX
  446.                 // Resolve only if there is no PIX
  447.                 if (str_starts_with($aid,'D276000186F4')) {
  448.                         $rest = substr($aid,strlen('D276000186F4'));
  449.                         $p = strpos($rest,'F');
  450.                         if ($p !== false) {
  451.                                 $duns = substr($rest,0,$p);
  452.                                 $pix = substr($rest,$p+1);
  453.                         } else {
  454.                                 $duns = $rest;
  455.                                 $pix = '';
  456.                         }
  457.                         if (($pix === '') && preg_match('/^[0-9]+$/',$duns,$m)) {
  458.                                 $ids[] = new OIDplusAltId('duns', $duns, _L('Data Universal Numbering System (D-U-N-S)'));
  459.                         }
  460.                 }
  461.  
  462.                 // (VTS F5) GS1 number + PIX
  463.                 // Resolve only if there is no PIX
  464.                 if (str_starts_with($aid,'D276000186F5')) {
  465.                         $rest = substr($aid,strlen('D276000186F5'));
  466.                         $p = strpos($rest,'F');
  467.                         if ($p !== false) {
  468.                                 $gs1 = substr($rest,0,$p);
  469.                                 $pix = substr($rest,$p+1);
  470.                         } else {
  471.                                 $gs1 = $rest;
  472.                                 $pix = '';
  473.                         }
  474.                         if (($pix === '') && preg_match('/^[0-9]+$/',$gs1,$m)) {
  475.                                 $ids[] = new OIDplusAltId('gs1', $gs1, _L('GS1 Based IDs (GLN/GTIN/SSCC/...)'), ' ('._L('without check-digit').')');
  476.                         }
  477.                 }
  478.  
  479.                 // (VTS F6) OID<->AID, no PIX
  480.                 if (str_starts_with($aid,'D276000186F6')) {
  481.                         $der = substr($aid,strlen('D276000186F6'));
  482.                         $len = strlen($der);
  483.                         if ($len%2 == 0) {
  484.                                 $len /= 2;
  485.                                 $len = str_pad("$len", 2, '0', STR_PAD_LEFT);
  486.                                 $type = '06'; // absolute OID
  487.                                 $der = "$type $len $der";
  488.                                 $oid = \OidDerConverter::derToOID(\OidDerConverter::hexStrToArray($der));
  489.                                 if ($oid) {
  490.                                         $oid = ltrim($oid,'.');
  491.                                         $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
  492.                                 }
  493.                         }
  494.                 }
  495.  
  496.                 // 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)
  497.                 // TODO: If it has no pix, then resolve it !!! but how do we know if there is a PIX or a part ID ?
  498.  
  499.                 return $ids;
  500.         }
  501.  
  502.         /**
  503.          * @return string
  504.          */
  505.         public function getDirectoryName(): string {
  506.                 if ($this->isRoot()) return $this->ns();
  507.                 return $this->ns().'_'.$this->nodeId(false); // safe, because there are only AIDs
  508.         }
  509.  
  510.         /**
  511.          * @param string $mode
  512.          * @return string
  513.          */
  514.         public static function treeIconFilename(string $mode): string {
  515.                 return 'img/'.$mode.'_icon16.png';
  516.         }
  517. }
  518.