Subversion Repositories oidplus

Rev

Rev 977 | Rev 1077 | 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 - 2022 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. class OIDplusAid extends OIDplusObject {
  23.         private $aid;
  24.  
  25.         public function __construct($aid) {
  26.                 // TODO: syntax checks
  27.                 $this->aid = $aid;
  28.         }
  29.  
  30.         public static function parse($node_id) {
  31.                 @list($namespace, $aid) = explode(':', $node_id, 2);
  32.                 if ($namespace !== self::ns()) return false;
  33.                 return new self($aid);
  34.         }
  35.  
  36.         public static function objectTypeTitle() {
  37.                 return _L('Application Identifier (ISO/IEC 7816)');
  38.         }
  39.  
  40.         public static function objectTypeTitleShort() {
  41.                 return _L('AID');
  42.         }
  43.  
  44.         public static function ns() {
  45.                 return 'aid';
  46.         }
  47.  
  48.         public static function root() {
  49.                 return self::ns().':';
  50.         }
  51.  
  52.         public function isRoot() {
  53.                 return $this->aid == '';
  54.         }
  55.  
  56.         public function nodeId($with_ns=true) {
  57.                 return $with_ns ? self::root().$this->aid : $this->aid;
  58.         }
  59.  
  60.         public function addString($str) {
  61.                 $m = array();
  62.  
  63.                 $str = str_replace(' ','',$str);
  64.                 $str = str_replace(':','',$str);
  65.  
  66.                 if (!preg_match('@^[0-9a-fA-F]+$@', $str, $m)) {
  67.                         throw new OIDplusException(_L('AID part needs to be hexadecimal'));
  68.                 }
  69.  
  70.                 if (strlen($this->nodeId(false).$str) > 32) {
  71.                         throw new OIDplusException(_L('An AID has a maximum length of 16 bytes'));
  72.                 }
  73.  
  74.                 // 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
  75.                 /*
  76.                 $pre   = $this->nodeId(false);
  77.                 $add   = strtoupper($str);
  78.                 $after = $pre.$add;
  79.                 $rid = '?';
  80.                 $pix = '?';
  81.                 $p = aid_split_rid_pix($after, $rid, $pix);
  82.                 if ($p > 1) { // Why $p>1? For "F", there is no RID. We allow that somebody include "F" in the first node
  83.                         if ((strlen($pre)<$p) && (strlen($after)>$p)) {
  84.                                 $rid = substr($rid,strlen($pre));
  85.                                 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));
  86.                         }
  87.                 }
  88.                 */
  89.  
  90.                 return $this->nodeId(true).strtoupper($str);
  91.         }
  92.  
  93.         public function crudShowId(OIDplusObject $parent) {
  94.                 return $this->chunkedNotation(false);
  95.         }
  96.  
  97.         public function crudInsertPrefix() {
  98.                 return $this->isRoot() ? '' : $this->chunkedNotation(false);
  99.         }
  100.  
  101.         public function jsTreeNodeName(OIDplusObject $parent = null) {
  102.                 if ($parent == null) return $this->objectTypeTitle();
  103.                 return substr($this->nodeId(), strlen($parent->nodeId()));
  104.         }
  105.  
  106.         public function defaultTitle() {
  107.                 return $this->aid;
  108.         }
  109.  
  110.         public function isLeafNode() {
  111.                 // We don't know when an AID is "leaf", because an AID can have an arbitary length <= 16 Bytes.
  112.                 // But if it is 16 bytes long (32 nibbles), then we are 100% certain that it is a leaf node.
  113.                 return (strlen($this->nodeId(false)) == 32);
  114.         }
  115.  
  116.         public function getContentPage(&$title, &$content, &$icon) {
  117.                 $icon = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
  118.  
  119.                 if ($this->isRoot()) {
  120.                         $title = OIDplusAid::objectTypeTitle();
  121.  
  122.                         $res = OIDplus::db()->query("select * from ###objects where parent = ?", array(self::root()));
  123.                         if ($res->any()) {
  124.                                 $content  = '<p>'._L('Please select an item in the tree view at the left to show its contents.').'</p>';
  125.                         } else {
  126.                                 $content  = '<p>'._L('Currently, no Application Identifiers are registered in the system.').'</p>';
  127.                         }
  128.  
  129.                         if (!$this->isLeafNode()) {
  130.                                 if (OIDplus::authUtils()->isAdminLoggedIn()) {
  131.                                         $content .= '<h2>'._L('Manage root objects').'</h2>';
  132.                                 } else {
  133.                                         $content .= '<h2>'._L('Available objects').'</h2>';
  134.                                 }
  135.                                 $content .= '%%CRUD%%';
  136.                         }
  137.                 } else {
  138.                         $title = $this->getTitle();
  139.  
  140.                         $chunked = $this->chunkedNotation(true);
  141.                         $content = '<h2>'.$chunked.'</h2>';
  142.  
  143.                         $tmp = decode_aid($this->aid,true);
  144.                         $tmp = htmlentities($tmp);
  145.                         $tmp = str_replace(' ','&nbsp;',$tmp);
  146.                         $tmp = nl2br($tmp);
  147.                         $tmp = preg_replace('@(warning|invalid|error|illegal(&nbsp;usage){0,1})@i', '<span class="errortext">\\1</span>', $tmp);
  148.                         $tmp = preg_replace('@(\\\\\\d{3})@i', '<span class="specialhexchar">\\1</span>', $tmp);
  149.  
  150.                         $content .= '<h2>'._L('Decoding').'</h2>';
  151.                         $content .= '<table border="0">';
  152.                         $content .= '<code>'.$tmp.'</code>';
  153.                         $content .= '</table>';
  154.  
  155.                         $content .= '<h2>'._L('Description').'</h2>%%DESC%%';
  156.                         if ($this->userHasWriteRights()) {
  157.                                 $content .= '<h2>'._L('Create or change subordinate objects').'</h2>';
  158.                         } else {
  159.                                 $content .= '<h2>'._L('Subordinate objects').'</h2>';
  160.                         }
  161.                         $content .= '%%CRUD%%';
  162.                 }
  163.         }
  164.  
  165.         # ---
  166.  
  167.         public function chunkedNotation($withAbbr=true) {
  168.                 $curid = self::root().$this->aid;
  169.  
  170.                 $obj = OIDplusObject::findFitting($curid);
  171.                 if (!$obj) return $this->aid;
  172.  
  173.                 $hints = array();
  174.                 $lengths = array(strlen($curid));
  175.                 while ($obj = OIDplusObject::findFitting($curid)) {
  176.                         $objParent = $obj->getParent();
  177.                         if (!$objParent) break;
  178.                         $curid = $objParent->nodeId();
  179.                         $hints[] = $obj->getTitle();
  180.                         $lengths[] = strlen($curid);
  181.                 }
  182.  
  183.                 array_shift($lengths);
  184.                 $chunks = array();
  185.  
  186.                 $full = self::root().$this->aid;
  187.                 foreach ($lengths as $len) {
  188.                         $chunks[] = substr($full, $len);
  189.                         $full = substr($full, 0, $len);
  190.                 }
  191.  
  192.                 $hints = array_reverse($hints);
  193.                 $chunks = array_reverse($chunks);
  194.  
  195.                 $full = array();
  196.                 foreach ($chunks as $c) {
  197.                         $hint = array_shift($hints);
  198.                         $full[] = $withAbbr && ($hint !== '') ? '<abbr title="'.htmlentities($hint).'">'.$c.'</abbr>' : $c;
  199.                 }
  200.                 return implode(' ', $full);
  201.         }
  202.  
  203.         public function one_up() {
  204.                 return OIDplusObject::parse($this->ns().':'.substr($this->aid,0,strlen($this->aid)-1));
  205.         }
  206.  
  207.         public function distance($to) {
  208.                 if (!is_object($to)) $to = OIDplusObject::parse($to);
  209.                 if (!($to instanceof $this)) return false;
  210.  
  211.                 $a = $to->aid;
  212.                 $b = $this->aid;
  213.  
  214.                 $ary = $a;
  215.                 $bry = $b;
  216.  
  217.                 $min_len = min(strlen($ary), strlen($bry));
  218.  
  219.                 for ($i=0; $i<$min_len; $i++) {
  220.                         if ($ary[$i] != $bry[$i]) return false;
  221.                 }
  222.  
  223.                 return strlen($ary) - strlen($bry);
  224.         }
  225.  
  226.         public function getAltIds() {
  227.                 if ($this->isRoot()) return array();
  228.                 $ids = parent::getAltIds();
  229.  
  230.                 $aid = $this->nodeId(false);
  231.                 $aid = strtoupper($aid);
  232.  
  233.                 // ViaThinkSoft "Foreign" AIDs
  234.  
  235.                 // (VTS F0) IANA PEN + PIX
  236.                 // Resolve only if there is no PIX
  237.                 if (str_starts_with($aid,'D276000186F0')) {
  238.                         $rest = substr($aid,strlen('D276000186F0'));
  239.                         $p = strpos($rest,'F');
  240.                         if ($p !== false) {
  241.                                 $pen = substr($rest,0,$p);
  242.                                 $pix = substr($rest,$p+1);
  243.                         } else {
  244.                                 $pen = $rest;
  245.                                 $pix = '';
  246.                         }
  247.                         if (($pix === '') && preg_match('/^[0-9]+$/',$pen,$m)) {
  248.                                 $oid = '1.3.6.1.4.1.'.$pen;
  249.                                 $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
  250.                                 $ids[] = new OIDplusAltId('iana-pen', $pen, _L('IANA Private Enterprise Number (PEN)'));
  251.                         }
  252.                 }
  253.  
  254.                 // (VTS F1) ViaThinkSoft FreeOID + PIX
  255.                 // Resolve only if there is no PIX
  256.                 if (str_starts_with($aid,'D276000186F1')) {
  257.                         $rest = substr($aid,strlen('D276000186F1'));
  258.                         $p = strpos($rest,'F');
  259.                         if ($p !== false) {
  260.                                 $number = substr($rest,0,$p);
  261.                                 $pix = substr($rest,$p+1);
  262.                         } else {
  263.                                 $number = $rest;
  264.                                 $pix = '';
  265.                         }
  266.                         if (($pix === '') && preg_match('/^[0-9]+$/',$number,$m)) {
  267.                                 $oid = '1.3.6.1.4.1.37476.9000.'.$number;
  268.                                 $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
  269.                         }
  270.                 }
  271.  
  272.                 // (VTS F2) MAC address + PIX
  273.                 // Resolve only if there is no PIX
  274.                 if (str_starts_with($aid,'D276000186F2')) {
  275.                         $rest = substr($aid,strlen('D276000186F2'));
  276.                         if (strlen($rest) == 12) {
  277.                                 $mac = $rest;
  278.                                 $ids[] = new OIDplusAltId('mac', $mac, _L('MAC address'));
  279.                         }
  280.                 }
  281.  
  282.                 // (VTS F3) USB-IF VendorID + PIX
  283.                 // Resolve only if there is no PIX
  284.                 if (str_starts_with($aid,'D276000186F3')) {
  285.                         $rest = substr($aid,strlen('D276000186F3'));
  286.                         if (strlen($rest) == 4) {
  287.                                 $vid = $rest;
  288.                                 $ids[] = new OIDplusAltId('usb-vendor-id', $vid, _L('USB-IF (usb.org) VendorID'));
  289.                         }
  290.                 }
  291.  
  292.                 // (VTS F4) D-U-N-S number + PIX
  293.                 // Resolve only if there is no PIX
  294.                 if (str_starts_with($aid,'D276000186F4')) {
  295.                         $rest = substr($aid,strlen('D276000186F4'));
  296.                         $p = strpos($rest,'F');
  297.                         if ($p !== false) {
  298.                                 $duns = substr($rest,0,$p);
  299.                                 $pix = substr($rest,$p+1);
  300.                         } else {
  301.                                 $duns = $rest;
  302.                                 $pix = '';
  303.                         }
  304.                         if (($pix === '') && preg_match('/^[0-9]+$/',$duns,$m)) {
  305.                                 $ids[] = new OIDplusAltId('duns', $duns, _L('Data Universal Numbering System (D-U-N-S)'));
  306.                         }
  307.                 }
  308.  
  309.                 // (VTS F5) GS1 number + PIX
  310.                 // Resolve only if there is no PIX
  311.                 if (str_starts_with($aid,'D276000186F5')) {
  312.                         $rest = substr($aid,strlen('D276000186F5'));
  313.                         $p = strpos($rest,'F');
  314.                         if ($p !== false) {
  315.                                 $gs1 = substr($rest,0,$p);
  316.                                 $pix = substr($rest,$p+1);
  317.                         } else {
  318.                                 $gs1 = $rest;
  319.                                 $pix = '';
  320.                         }
  321.                         if (($pix === '') && preg_match('/^[0-9]+$/',$gs1,$m)) {
  322.                                 $ids[] = new OIDplusAltId('gs1', $gs1, _L('GS1 Based IDs (GLN/GTIN/SSCC/...)'), ' ('._L('without check-digit').')');
  323.                         }
  324.                 }
  325.  
  326.                 // (VTS F6) OID<->AID, no PIX
  327.                 if (str_starts_with($aid,'D276000186F6')) {
  328.                         $der = substr($aid,strlen('D276000186F6'));
  329.                         $len = strlen($der);
  330.                         if ($len%2 == 0) {
  331.                                 $len /= 2;
  332.                                 $len = str_pad("$len", 2, '0', STR_PAD_LEFT);
  333.                                 $type = '06'; // absolute OID
  334.                                 $der = "$type $len $der";
  335.                                 $oid = \OidDerConverter::derToOID(\OidDerConverter::hexStrToArray($der));
  336.                                 if ($oid) {
  337.                                         $oid = ltrim($oid,'.');
  338.                                         $ids[] = new OIDplusAltId('oid', $oid, _L('Object Identifier (OID)'));
  339.                                 }
  340.                         }
  341.                 }
  342.  
  343.                 // 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)
  344.                 // TODO: If it has no pix, then resolve it !!! but how do we know if there is a PIX or a part ID ?
  345.  
  346.                 return $ids;
  347.         }
  348.  
  349.         public function getDirectoryName() {
  350.                 if ($this->isRoot()) return $this->ns();
  351.                 return $this->ns().'_'.$this->nodeId(false); // safe, because there are only AIDs
  352.         }
  353.  
  354.         public static function treeIconFilename($mode) {
  355.                 return 'img/'.$mode.'_icon16.png';
  356.         }
  357. }
  358.