Subversion Repositories oidplus

Rev

Rev 339 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

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