Subversion Repositories oidplus

Rev

Rev 790 | Rev 801 | 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 - 2021 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. if (!defined('INSIDE_OIDPLUS')) die();
  21.  
  22. class OIDplusOid extends OIDplusObject {
  23.         private $oid;
  24.  
  25.         public function __construct($oid) {
  26.                 $bak_oid = $oid;
  27.  
  28.                 $oid = sanitizeOID($oid, 'auto');
  29.                 if ($oid === false) {
  30.                         throw new OIDplusException(_L('Invalid OID %1',$bak_oid));
  31.                 }
  32.  
  33.                 if (($oid != '') && (!oid_valid_dotnotation($oid, false, true, 0))) {
  34.                         // avoid OIDs like 3.0
  35.                         throw new OIDplusException(_L('Invalid OID %1',$bak_oid));
  36.                 }
  37.  
  38.                 $this->oid = $oid;
  39.         }
  40.  
  41.         public static function parse($node_id) {
  42.                 @list($namespace, $oid) = explode(':', $node_id, 2);
  43.                 if ($namespace !== 'oid') return false;
  44.                 return new self($oid);
  45.         }
  46.  
  47.         public static function objectTypeTitle() {
  48.                 return _L('Object Identifier (OID)');
  49.         }
  50.  
  51.         public static function objectTypeTitleShort() {
  52.                 return _L('OID');
  53.         }
  54.  
  55.         public static function ns() {
  56.                 return 'oid';
  57.         }
  58.  
  59.         public static function root() {
  60.                 return 'oid:';
  61.         }
  62.  
  63.         public function isRoot() {
  64.                 return $this->oid == '';
  65.         }
  66.  
  67.         public function nodeId($with_ns=true) {
  68.                 return $with_ns ? 'oid:'.$this->oid : $this->oid;
  69.         }
  70.  
  71.         public function addString($str) {
  72.                 if (!$this->isRoot()) {
  73.                         if (strpos($str,'.') !== false) throw new OIDplusException(_L('Please only submit one arc (not an absolute OID or multiple arcs).'));
  74.                 }
  75.  
  76.                 return $this->appendArcs($str)->nodeId();
  77.         }
  78.  
  79.         public function crudShowId(OIDplusObject $parent) {
  80.                 if ($parent instanceof OIDplusOid) {
  81.                         return $this->deltaDotNotation($parent);
  82.                 }
  83.         }
  84.  
  85.         public function jsTreeNodeName(OIDplusObject $parent = null) {
  86.                 if ($parent == null) return $this->objectTypeTitle();
  87.                 if ($parent instanceof OIDplusOid) {
  88.                         return $this->viewGetArcAsn1s($parent);
  89.                 } else {
  90.                         return '';
  91.                 }
  92.         }
  93.  
  94.         public function defaultTitle() {
  95.                 return _L('OID %1',$this->oid);
  96.         }
  97.  
  98.         public function isLeafNode() {
  99.                 return false;
  100.         }
  101.  
  102.         private function getTechInfo() {
  103.                 $tech_info = array();
  104.  
  105.                 $tmp = _L('Dot notation');
  106.                 $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#14" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
  107.                 $tech_info[$tmp] = $this->getDotNotation();
  108.  
  109.                 $tmp = _L('ASN.1 notation');
  110.                 $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#17" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
  111.                 $tech_info[$tmp] = $this->getAsn1Notation();
  112.  
  113.                 $tmp = _L('OID-IRI notation');
  114.                 $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="http://oid-info.com/faq.htm#iri" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
  115.                 $tech_info[$tmp] = $this->getIriNotation();
  116.  
  117.                 $tmp = _L('WEID notation');
  118.                 $tmp = str_replace(explode(' ', $tmp, 2)[0], '<a href="https://weid.info/" target="_blank">'.explode(' ', $tmp, 2)[0].'</a>', $tmp);
  119.                 $tech_info[$tmp] = $this->getWeidNotation();
  120.  
  121.                 return $tech_info;
  122.         }
  123.  
  124.         protected function isClassCWeid() {
  125.                 $dist = oid_distance($this->oid, '1.3.6.1.4.1.37553.8');
  126.                 if ($dist === false) return false;
  127.                 return $dist >= 0;
  128.         }
  129.  
  130.         public function getContentPage(&$title, &$content, &$icon) {
  131.                 if ($this->isClassCWeid()) {
  132.                         // TODO: Also change treeview menu mini-icon?
  133.                         $icon = file_exists(__DIR__.'/img/weid_icon.png') ? OIDplus::webPath(__DIR__,true).'img/weid_icon.png' : '';
  134.                 } else {
  135.                         $icon = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webPath(__DIR__,true).'img/main_icon.png' : '';
  136.                 }
  137.  
  138.                 if ($this->isRoot()) {
  139.                         $title = OIDplusOid::objectTypeTitle();
  140.  
  141.                         $res = OIDplus::db()->query("select id from ###objects where parent = ?", array(self::root()));
  142.                         if ($res->any()) {
  143.                                 $content = _L('Please select an OID in the tree view at the left to show its contents.');
  144.                         } else {
  145.                                 $content = _L('Currently, no OID is registered in the system.');
  146.                         }
  147.  
  148.                         if (!$this->isLeafNode()) {
  149.                                 if (OIDplus::authUtils()->isAdminLoggedIn()) {
  150.                                         $content .= '<h2>'._L('Manage your root OIDs').'</h2>';
  151.                                 } else {
  152.                                         $content .= '<h2>'._L('Root OIDs').'</h2>';
  153.                                 }
  154.                                 $content .= '%%CRUD%%';
  155.                         }
  156.                 } else {
  157.                         $title = $this->getTitle();
  158.  
  159.                         $tech_info = $this->getTechInfo();
  160.                         $tech_info_html = '';
  161.                         if (count($tech_info) > 0) {
  162.                                 $tech_info_html .= '<h2>'._L('Technical information').'</h2>';
  163.                                 $tech_info_html .= '<table border="0">';
  164.                                 foreach ($tech_info as $key => $value) {
  165.                                         $tech_info_html .= '<tr><td>'.$key.': </td><td><code>'.$value.'</code></td></tr>';
  166.                                 }
  167.                                 $tech_info_html .= '</table>';
  168.                         }
  169.  
  170.                         $content = $tech_info_html;
  171.  
  172.                         $content .= '<h2>'._L('Description').'</h2>%%DESC%%'.
  173.                                     '<h2>'._L('Registration Authority').'</h2>%%RA_INFO%%';
  174.  
  175.                         if (!$this->isLeafNode()) {
  176.                                 if ($this->userHasWriteRights()) {
  177.                                         $content .= '<h2>'._L('Create or change subsequent objects').'</h2>';
  178.                                 } else {
  179.                                         $content .= '<h2>'._L('Subsequent objects').'</h2>';
  180.                                 }
  181.                                 $content .= '%%CRUD%%';
  182.                         }
  183.                 }
  184.         }
  185.  
  186.         # ---
  187.  
  188.         // Gets the last arc of an WEID
  189.         public function weidArc() {
  190.                 // Dirty hack: We prepend '0.' in front of the OID to enforce the
  191.                 //             creation of a Class A weid (weid:root:) . Otherwise we could not
  192.                 //             get the hidden arc value "8" from "weid:4" (which is actually "weid:pen:SZ5-8-?"
  193.                 $weid = WeidOidConverter::oid2weid('0.'.$this->getDotNotation());
  194.                 if ($weid === false) return false;
  195.                 $ary = explode(':', $weid);
  196.                 $weid = array_pop($ary); // remove namespace and sub-namespace if existing
  197.                 $x = explode('-', $weid);
  198.                 if (count($x) < 2) return ''; // WEID root arc. Has no name
  199.                 return $x[count($x)-2];
  200.         }
  201.  
  202.         public function getWeidNotation($withAbbr=true) {
  203.                 $weid = WeidOidConverter::oid2weid($this->getDotNotation());
  204.                 if ($withAbbr) {
  205.                         $ary = explode(':', $weid);
  206.                         $weid = array_pop($ary); // remove namespace and sub-namespace if existing
  207.                         $ns = implode(':', $ary).':';
  208.  
  209.                         $weid_arcs = explode('-', $weid);
  210.                         foreach ($weid_arcs as $i => &$weid) {
  211.                                 if ($i == count($weid_arcs)-1) {
  212.                                         $weid = '<abbr title="'._L('weLuhn check digit').'">'.$weid.'</abbr>';
  213.                                 } else {
  214.                                         $oid_arcs = explode('.',$this->oid);
  215.                                         $weid_num = $oid_arcs[(count($oid_arcs)-1)-(count($weid_arcs)-1)+($i+1)];
  216.                                         if ($weid_num != $weid) {
  217.                                                 $weid = '<abbr title="'._L('Numeric value').': '.$weid_num.'">'.$weid.'</abbr>';
  218.                                         }
  219.                                 }
  220.                         }
  221.                         $base_arc = '???';
  222.                         if ($ns === 'weid:')      $base_arc = '1.3.6.1.4.1.37553.8';
  223.                         if ($ns === 'weid:pen:')  $base_arc = '1.3.6.1.4.1';
  224.                         if ($ns === 'weid:root:') $base_arc = _L('OID tree root');
  225.  
  226.                         $weid = '<abbr title="'._L('Base OID').': '.$base_arc.'">' . rtrim($ns,':') . '</abbr>:' . implode('-',$weid_arcs);
  227.                 }
  228.                 return $weid;
  229.         }
  230.  
  231.         public function appendArcs(String $arcs) {
  232.                 $out = new self($this->oid);
  233.  
  234.                 if ($out->isRoot()) {
  235.                         $out->oid .= $arcs;
  236.                 } else {
  237.                         $out->oid .= '.' . $arcs;
  238.                 }
  239.  
  240.                 $bak_oid = $out->oid;
  241.                 $out->oid = sanitizeOID($out->oid);
  242.                 if ($out->oid === false) throw new OIDplusException(_L('%1 is not a valid OID!',$bak_oid));
  243.  
  244.                 $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_ID_LENGTH')-strlen('oid:');
  245.                 if (strlen($out->oid) > $maxlen) {
  246.                         throw new OIDplusException(_L('The resulting OID "%1" is too long (max allowed length: %2).',$out->oid,$maxlen));
  247.                 }
  248.  
  249.                 $depth = 0;
  250.                 foreach (explode('.',$out->oid) as $arc) {
  251.                         if (strlen($arc) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE')) {
  252.                                 $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ARC_SIZE');
  253.                                 throw new OIDplusException(_L('Arc "%1" is too long and therefore cannot be appended to the OID "%2" (max allowed arc size is "%3")',$arc,$this->oid,$maxlen));
  254.                         }
  255.                         $depth++;
  256.                 }
  257.                 if ($depth > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH')) {
  258.                         $maxdepth = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_DEPTH');
  259.                         throw new OIDplusException(_L('OID %1 has too many arcs (current depth %2, max depth %3)',$out->oid,$depth,$maxdepth));
  260.                 }
  261.  
  262.                 return $out;
  263.         }
  264.  
  265.         public function deltaDotNotation(OIDplusOid $parent) {
  266.                 if (!$parent->isRoot()) {
  267.                         if (substr($this->oid, 0, strlen($parent->oid)+1) == $parent->oid.'.') {
  268.                                 return substr($this->oid, strlen($parent->oid)+1);
  269.                         } else {
  270.                                 return false;
  271.                         }
  272.                 } else {
  273.                         return $this->oid;
  274.                 }
  275.         }
  276.  
  277.         public function viewGetArcAsn1s(OIDplusOid $parent=null, $separator = ' | ') {
  278.                 $asn_ids = array();
  279.  
  280.                 if (is_null($parent)) $parent = OIDplusOid::parse('oid:');
  281.  
  282.                 $part = $this->deltaDotNotation($parent);
  283.  
  284.                 if (strpos($part, '.') === false) {
  285.                         $res2 = OIDplus::db()->query("select name from ###asn1id where oid = ? order by lfd", array("oid:".$this->oid));
  286.                         while ($row2 = $res2->fetch_array()) {
  287.                                 $asn_ids[] = $row2['name'].'('.$part.')';
  288.                         }
  289.                 }
  290.  
  291.                 if (count($asn_ids) == 0) $asn_ids = array($part);
  292.                 return implode($separator, $asn_ids);
  293.         }
  294.  
  295.         public function getAsn1Notation($withAbbr=true) {
  296.                 $asn1_notation = '';
  297.                 $arcs = explode('.', $this->oid);
  298.  
  299.                 foreach ($arcs as $arc) {
  300.                         $res = OIDplus::db()->query("select name, standardized from ###asn1id where oid = ? order by lfd", array('oid:'.implode('.',$arcs)));
  301.  
  302.                         $names = array();
  303.                         while ($row = $res->fetch_array()) {
  304.                                 $names[] = $row['name']."(".end($arcs).")";
  305.                                 if ($row['standardized']) {
  306.                                         $names[] = $row['name'];
  307.                                 }
  308.                         }
  309.  
  310.                         $numeric = array_pop($arcs);
  311.                         if (count($names) > 1) {
  312.                                 $first_name = array_shift($names);
  313.                                 $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names);
  314.                                 if ($withAbbr) {
  315.                                         $asn1_notation = '<abbr title="'.$abbr.'">'.$first_name.'</abbr> '.$asn1_notation;
  316.                                 } else {
  317.                                         $asn1_notation = $first_name.' '.$asn1_notation;
  318.                                 }
  319.                         } else if (count($names) == 1) {
  320.                                 $asn1_notation = array_shift($names).' '.$asn1_notation;
  321.                         } else {
  322.                                 $asn1_notation = $numeric.' '.$asn1_notation;
  323.                         }
  324.                 }
  325.  
  326.                 return "{ ".trim($asn1_notation)." }";
  327.         }
  328.  
  329.         public function getIriNotation($withAbbr=true) {
  330.                 $iri_notation = '';
  331.                 $arcs = explode('.', $this->oid);
  332.  
  333.                 foreach ($arcs as $arc) {
  334.                         $res = OIDplus::db()->query("select name, longarc from ###iri where oid = ? order by lfd", array('oid:'.implode('.',$arcs)));
  335.  
  336.                         $is_longarc = false;
  337.                         $names = array();
  338.                         while ($row = $res->fetch_array()) {
  339.                                 $is_longarc = $row['longarc'];
  340.                                 $names[] = $row['name'];
  341.  
  342.                                 if ($is_longarc) {
  343.                                         $names[] = 'Joint-ISO-ITU-T/'.$row['name']; // Long arcs can only be inside root OID 2
  344.                                 }
  345.                         }
  346.  
  347.                         $names[] = array_pop($arcs);
  348.                         if (count($names) > 2) {
  349.                                 $first_name = array_shift($names);
  350.                                 $numeric = array_pop($names);
  351.                                 $abbr = _L('Other identifiers').':&#10;      '.implode('&#10;      ',$names).'&#10;'._L('Numeric value').': '.$numeric;
  352.                                 $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
  353.                         } else if (count($names) > 1) {
  354.                                 $first_name = array_shift($names);
  355.                                 $abbr = _L('Numeric value').': '.array_shift($names);
  356.                                 $iri_notation = $withAbbr ? '<abbr title="'.$abbr.'">'.$first_name.'</abbr>/'.$iri_notation : $first_name.'/'.$iri_notation;
  357.                         } else if (count($names) == 1) {
  358.                                 $iri_notation = array_shift($names) . '/' . $iri_notation;
  359.                         }
  360.  
  361.                         if ($is_longarc) break; // we don't write /ITU-T/ at the beginning, when /ITU-T/xyz is a long arc
  362.                 }
  363.                 $iri_notation = '/' . substr($iri_notation, 0, strlen($iri_notation)-1);
  364.  
  365.                 return $iri_notation;
  366.         }
  367.  
  368.         public function getDotNotation() {
  369.                 return $this->oid;
  370.         }
  371.  
  372.         public function isWellKnown() {
  373.                 $res = OIDplus::db()->query("select oid from ###asn1id where oid = ? and well_known = ?", array("oid:".$this->oid,true));
  374.                 if ($res->any()) return true;
  375.  
  376.                 $res = OIDplus::db()->query("select oid from ###iri where oid = ? and well_known = ?", array("oid:".$this->oid,true));
  377.                 if ($res->any()) return true;
  378.  
  379.                 return false;
  380.         }
  381.  
  382.         public function replaceAsn1Ids($demandedASN1s=array(), $simulate=false) {
  383.                 if ($this->isWellKnown()) {
  384.                         throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
  385.                 }
  386.  
  387.                 // First do a few checks
  388.                 foreach ($demandedASN1s as &$asn1) {
  389.                         $asn1 = trim($asn1);
  390.  
  391.                         if (strlen($asn1) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN')) {
  392.                                 $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_ASN1_ID_LEN');
  393.                                 throw new OIDplusException(_L('ASN.1 alphanumeric identifier "%1" is too long (max allowed length %2)',$asn1,$maxlen));
  394.                         }
  395.  
  396.                         // Validate identifier
  397.                         if (!oid_id_is_valid($asn1)) throw new OIDplusException(_L('"%1" is not a valid ASN.1 identifier!',$asn1));
  398.  
  399.                         // Check if the (real) parent has any conflict
  400.                         // Unlike IRI identifiers, ASN.1 identifiers may be used multiple times (not recommended), except if one of them is standardized
  401.                         $res = OIDplus::db()->query("select oid from ###asn1id where name = ? and standardized = ?", array($asn1,true));
  402.                         while ($row = $res->fetch_array()) {
  403.                                 $check_oid = OIDplusOid::parse($row['oid'])->oid;
  404.                                 if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
  405.                                    ($check_oid !== $this->oid))                    // different OID
  406.                                 {
  407.                                         throw new OIDplusException(_L('ASN.1 identifier "%1" is a standardized identifier belonging to OID %2',$asn1,$check_oid));
  408.                                 }
  409.                         }
  410.                 }
  411.  
  412.                 // Now do the real replacement
  413.                 if (!$simulate) {
  414.                         OIDplus::db()->query("delete from ###asn1id where oid = ?", array("oid:".$this->oid));
  415.                         foreach ($demandedASN1s as &$asn1) {
  416.                                 OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array("oid:".$this->oid, $asn1));
  417.                         }
  418.                 }
  419.         }
  420.  
  421.         public function replaceIris($demandedIris=array(), $simulate=false) {
  422.                 if ($this->isWellKnown()) {
  423.                         throw new OIDplusException(_L('OID "%1" is a "well-known" OID. Its identifiers cannot be changed.',$this->oid));
  424.                 }
  425.  
  426.                 // First do a few checks
  427.                 foreach ($demandedIris as &$iri) {
  428.                         $iri = trim($iri);
  429.  
  430.                         if (strlen($iri) > OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN')) {
  431.                                 $maxlen = OIDplus::baseConfig()->getValue('LIMITS_MAX_OID_UNICODE_LABEL_LEN');
  432.                                 throw new OIDplusException(_L('Unicode label "%1" is too long (max allowed length %2)',$iri,$maxlen));
  433.                         }
  434.  
  435.                         // Validate identifier
  436.                         if (!iri_arc_valid($iri, false)) throw new OIDplusException(_L('"%1" is not a valid IRI!',$iri));
  437.  
  438.                         // Check if the (real) parent has any conflict
  439.                         $res = OIDplus::db()->query("select oid from ###iri where name = ?", array($iri));
  440.                         while ($row = $res->fetch_array()) {
  441.                                 $check_oid = OIDplusOid::parse($row['oid'])->oid;
  442.                                 if ((oid_up($check_oid) === oid_up($this->oid)) && // same parent
  443.                                    ($check_oid !== $this->oid))                    // different OID
  444.                                 {
  445.                                         throw new OIDplusException(_L('IRI "%1" is already used by another OID (%2)',$iri,$check_oid));
  446.                                 }
  447.                         }
  448.                 }
  449.  
  450.                 // Now do the real replacement
  451.                 if (!$simulate) {
  452.                         OIDplus::db()->query("delete from ###iri where oid = ?", array("oid:".$this->oid));
  453.                         foreach ($demandedIris as &$iri) {
  454.                                 OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array("oid:".$this->oid, $iri));
  455.                         }
  456.                 }
  457.         }
  458.  
  459.         public function one_up() {
  460.                 return self::parse(self::ns().':'.oid_up($this->oid));
  461.         }
  462.  
  463.         public function distance($to) {
  464.                 if (!is_object($to)) $to = OIDplusObject::parse($to);
  465.                 if (!($to instanceof $this)) return false;
  466.                 return oid_distance($to->oid, $this->oid);
  467.         }
  468.  
  469.         public function getAltIds() {
  470.                 if ($this->isRoot()) return array();
  471.                 $ids = parent::getAltIds();
  472.                 if ($uuid = oid_to_uuid($this->oid)) {
  473.                         $ids[] = new OIDplusAltId('guid', $uuid, _L('GUID representation of this OID'));
  474.                 }
  475.                 $ids[] = new OIDplusAltId('guid', gen_uuid_md5_namebased(UUID_NAMEBASED_NS_OID, $this->oid), _L('Name based version 3 / MD5 UUID with namespace %1','UUID_NAMEBASED_NS_OID'));
  476.                 $ids[] = new OIDplusAltId('guid', gen_uuid_sha1_namebased(UUID_NAMEBASED_NS_OID, $this->oid), _L('Name based version 5 / SHA1 UUID with namespace %1','UUID_NAMEBASED_NS_OID'));
  477.                 return $ids;
  478.         }
  479.  
  480.         public function getDirectoryName() {
  481.                 if ($this->isRoot()) return $this->ns();
  482.                 $oid = $this->nodeId(false);
  483.                 return $this->ns().'_'.str_replace('.', '_', $oid);
  484.         }
  485.  
  486.         public function rootIconname($mode) {
  487.                 return 'img/'.$mode.'_icon16.png';
  488.         }
  489. }
  490.