Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/oidinfo_api/trunk/oid_utils.inc.phps
Revision: 3
Committed: Thu May 2 10:10:59 2019 UTC (11 months ago) by daniel-marschall
File size: 28484 byte(s)
Log Message:
Fixed symlinks

File Contents

# Content
1 <?php
2
3 /*
4 * OID-Utilities for PHP
5 * Copyright 2011-2019 Daniel Marschall, ViaThinkSoft
6 * Version 2019-03-25
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 // All functions in this library are compatible with leading zeroes (not recommended) and leading dots
22
23 // TODO: change the function names, so that they have a uniform naming schema, and rename "oid identifier" into "asn.1 alphanumeric identifier"
24 // TODO: Function for finding a shared ancestor, e.g. oid_shared_ancestor('2.999.1.2.3', '2.999.4.5') == '2.999'
25
26 define('OID_DOT_FORBIDDEN', 0);
27 define('OID_DOT_OPTIONAL', 1);
28 define('OID_DOT_REQUIRED', 2);
29
30 /**
31 * Checks if an OID has a valid dot notation.
32 * @author Daniel Marschall, ViaThinkSoft
33 * @version 2014-12-09
34 * @param $oid (string)<br />
35 * An OID in dot notation.
36 * @param $allow_leading_zeroes (bool)<br />
37 * true of leading zeroes are allowed or not.
38 * @param $allow_leading_dot (bool)<br />
39 * true of leading dots are allowed or not.
40 * @return (bool) true if the dot notation is valid.
41 **/
42 function oid_valid_dotnotation($oid, $allow_leading_zeroes=true, $allow_leading_dot=false, $min_len=0) {
43 $regex = oid_validation_regex($allow_leading_zeroes, $allow_leading_dot, $min_len);
44
45 return preg_match($regex, $oid, $m) ? true : false;
46 }
47
48 /**
49 * Returns a full regular expression to validate an OID in dot-notation
50 * @author Daniel Marschall, ViaThinkSoft
51 * @version 2014-12-09
52 * @param $allow_leading_zeroes (bool)<br />
53 * true of leading zeroes are allowed or not.
54 * @param $allow_leading_dot (bool)<br />
55 * true of leading dots are allowed or not.
56 * @return (string) The regular expression
57 **/
58 function oid_validation_regex($allow_leading_zeroes=true, $allow_leading_dot=false, $min_len=0) {
59 $leading_dot_policy = $allow_leading_dot ? OID_DOT_OPTIONAL : OID_DOT_FORBIDDEN;
60
61 $part_regex = oid_part_regex($min_len, $allow_leading_zeroes, $leading_dot_policy);
62
63 return '@^'.$part_regex.'$@';
64 }
65
66 /**
67 * Returns a partial regular expression which matches valid OIDs in dot notation.
68 * It can be inserted into regular expressions.
69 * @author Daniel Marschall, ViaThinkSoft
70 * @version 2014-12-09
71 * @param $min_len (int)<br />
72 * 0="." and greater will be recognized, but not ""<br />
73 * 1=".2" and greater will be recognized<br />
74 * 2=".2.999" and greater will be recognized (default)<br />
75 * etc.
76 * @param $allow_leading_zeroes (bool)<br />
77 * true: ".2.0999" will be recognized<br />
78 * false: ".2.0999" won't be recognized (default)
79 * @param $leading_dot_policy (int)<br />
80 * 0 (OID_DOT_FORBIDDEN): forbidden<br />
81 * 1 (OID_DOT_OPTIONAL) : optional (default)<br />
82 * 2 (OID_DOT_REQUIRED) : enforced
83 * @return (string) A regular expression which matches OIDs in dot notation
84 **/
85 function oid_part_regex($min_len=2, $allow_leading_zeroes=false, $leading_dot_policy=OID_DOT_OPTIONAL) {
86 switch ($leading_dot_policy) {
87 case 0: // forbidden
88 $lead_dot = '';
89 break;
90 case 1: // optional
91 $lead_dot = '\\.{0,1}';
92 break;
93 case 2: // enforced
94 $lead_dot = '\\.';
95 break;
96 default:
97 assert(false);
98 break;
99 }
100
101 $lead_zero = $allow_leading_zeroes ? '0*' : '';
102 $zero_till_thirtynine = '(([0-9])|([1-3][0-9]))'; // second arc is limited to 0..39 if root arc is 0..1
103 $singledot_option = ($min_len == 0) && ($leading_dot_policy != OID_DOT_FORBIDDEN) ? '|\\.' : '';
104 $only_root_option = ($min_len <= 1) ? '|('.$lead_dot.$lead_zero.'[0-2])' : '';
105
106 $regex = '
107 (
108 (
109 (
110 ('.$lead_dot.$lead_zero.'[0-1])
111 \\.'.$lead_zero.$zero_till_thirtynine.'
112 (\\.'.$lead_zero.'(0|[1-9][0-9]*)){'.max(0, $min_len-2).',}
113 )|(
114 ('.$lead_dot.$lead_zero.'[2])
115 (\\.'.$lead_zero.'(0|[1-9][0-9]*)){'.max(0, $min_len-1).',}
116 )
117 '.$only_root_option.'
118 '.$singledot_option.'
119 )
120 )';
121
122 // Remove the indentations which are used to maintain this large regular expression in a human friendly way
123 $regex = str_replace("\n", '', $regex);
124 $regex = str_replace("\r", '', $regex);
125 $regex = str_replace("\t", '', $regex);
126 $regex = str_replace(' ', '', $regex);
127
128 return $regex;
129 }
130
131 /**
132 * Searches all OIDs in $text and outputs them as array.
133 * @author Daniel Marschall, ViaThinkSoft
134 * @version 2014-12-09
135 * @param $text (string)<br />
136 * The text to be parsed
137 * @param $min_len (int)<br />
138 * 0="." and greater will be recognized, but not ""<br />
139 * 1=".2" and greater will be recognized<br />
140 * 2=".2.999" and greater will be recognized (default)<br />
141 * etc.
142 * @param $allow_leading_zeroes (bool)<br />
143 * true: ".2.0999" will be recognized<br />
144 * false: ".2.0999" won't be recognized (default)
145 * @param $leading_dot_policy (int)<br />
146 * 0 (OID_DOT_FORBIDDEN): forbidden<br />
147 * 1 (OID_DOT_OPTIONAL) : optional (default)<br />
148 * 2 (OID_DOT_REQUIRED) : enforced
149 * @param $requires_whitespace_delimiters (bool)<br />
150 * true: "2.999" will be recognized, as well as " 2.999 " (default)<br />
151 * false: "2.999!" will be reconigzed, as well as "2.999.c" (this might be used in in documentations with templates)
152 * @return (array<string>) An array of OIDs in dot notation
153 **/
154 function parse_oids($text, $min_len=2, $allow_leading_zeroes=false, $leading_dot_policy=OID_DOT_OPTIONAL, $requires_whitespace_delimiters=true) {
155 $regex = oid_detection_regex($min_len, $allow_leading_zeroes, $leading_dot_policy, $requires_whitespace_delimiters);
156
157 preg_match_all($regex, $text, $matches);
158 return $matches[1];
159 }
160
161 /**
162 * Returns a full regular expression for detecting OIDs in dot notation inside a text.
163 * @author Daniel Marschall, ViaThinkSoft
164 * @version 2014-12-09
165 * @param $min_len (int)<br />
166 * 0="." and greater will be recognized, but not ""<br />
167 * 1=".2" and greater will be recognized<br />
168 * 2=".2.999" and greater will be recognized (default)<br />
169 * etc.
170 * @param $allow_leading_zeroes (bool)<br />
171 * true: ".2.0999" will be recognized<br />
172 * false: ".2.0999" won't be recognized (default)
173 * @param $leading_dot_policy (int)<br />
174 * 0 (OID_DOT_FORBIDDEN): forbidden<br />
175 * 1 (OID_DOT_OPTIONAL) : optional (default)<br />
176 * 2 (OID_DOT_REQUIRED) : enforced
177 * @param $requires_whitespace_delimiters (bool)<br />
178 * true: "2.999" will be recognized, as well as " 2.999 " (default)<br />
179 * false: "2.999!" will be reconigzed, as well as "2.999.c" (this might be used in in documentations with templates)
180 * @return (string) The regular expression
181 **/
182 function oid_detection_regex($min_len=2, $allow_leading_zeroes=false, $leading_dot_policy=OID_DOT_OPTIONAL, $requires_whitespace_delimiters=true) {
183 if ($requires_whitespace_delimiters) {
184 // A fully qualified regular expression which can be used by preg_match()
185 $begin_condition = '(?<=^|\\s)';
186 $end_condition = '(?=\\s|$)';
187 } else {
188 // A partial expression which can be used inside another regular expression
189 $begin_condition = '(?<![\d])';
190 $end_condition = '(?![\d])';
191 }
192
193 $part_regex = oid_part_regex($min_len, $allow_leading_zeroes, $leading_dot_policy);
194
195 return '@'.$begin_condition.$part_regex.$end_condition.'@';
196 }
197
198 /**
199 * Returns the parent of an OID in dot notation or the OID itself, if it is the root.<br />
200 * Leading dots and leading zeroes are tolerated.
201 * @author Daniel Marschall, ViaThinkSoft
202 * @version 2014-12-16
203 * @param $oid (string)<br />
204 * An OID in dot notation.
205 * @return (string) The parent OID in dot notation.
206 **/
207 function oid_up($oid) {
208 $oid = sanitizeOID($oid, 'auto');
209 if ($oid === false) return false;
210
211 $p = strrpos($oid, '.');
212 if ($p === false) return $oid;
213 if ($p == 0) return '.';
214
215 return substr($oid, 0, $p);
216 }
217
218 /**
219 * Outputs the depth of an OID.
220 * @author Daniel Marschall, ViaThinkSoft
221 * @version 2014-12-09
222 * @param $oid (string) An OID in dot notation (with or without leading dot)
223 * @return (int) The depth of the OID, e.g. 2.999 and .2.999 has the length 2.
224 **/
225 function oid_len($oid) {
226 if ($oid == '') return 0;
227 if ($oid[0] == '.') $oid = substr($oid, 1);
228 return substr_count($oid, '.')+1;
229 }
230 function oid_depth($oid) {
231 return oid_len($oid);
232 }
233
234 /**
235 * Lists all parents of an OID.
236 * This function tolerates leading dots. The parent of '.' stays '.'.
237 * The OID will not be checked for validity!
238 * @author Daniel Marschall, ViaThinkSoft
239 * @version 2014-12-17
240 * @param $oid (string)<br />
241 * An OID in dot notation.
242 * @return (array<string>) An array with all parent OIDs.
243 **/
244 function oid_parents($oid) {
245 $parents = array();
246
247 while (oid_len($oid) > 1) {
248 $oid = oid_up($oid);
249 $parents[] = $oid;
250 }
251
252 if (substr($oid, 0, 1) == '.') $parents[] = '.';
253
254 return $parents;
255 }
256
257 /*
258 assert(oid_parents('.1.2.999') == array('.1.2', '.1', '.'));
259 assert(oid_parents('1.2.999') == array('1.2', '1'));
260 assert(oid_parents('.') == array('.'));
261 assert(oid_parents('') == array());
262 */
263
264 /**
265 * Sorts an array containing OIDs in dot notation.
266 * @author Daniel Marschall, ViaThinkSoft
267 * @version 2014-12-09
268 * @param $ary (array<string>)<br />
269 * An array of OIDs in dot notation.<br />
270 * This array will be changed by this method.
271 * @param $output_with_leading_dot (bool)<br />
272 * true: The array will be normalized to OIDs with a leading dot.
273 * false: The array will be normalized to OIDs without a leading dot. (default)
274 * @return Nothing
275 **/
276 function oidSort(&$ary, $output_with_leading_dot=false) {
277 $out = array();
278
279 $none = $output_with_leading_dot ? '.' : '';
280
281 $d = array();
282 foreach ($ary as &$oid) {
283 if (($oid == '') || ($oid == '.')) {
284 $out[] = $none;
285 } else {
286 $oid = sanitizeOID($oid, 'auto'); // strike leading zeroes
287 $bry = explode('.', $oid, 2);
288 $firstarc = $bry[0];
289 $rest = (isset($bry[1])) ? $bry[1] : '';
290 $d[$firstarc][] = $rest;
291 }
292 }
293 unset($oid);
294 ksort($d);
295
296 foreach ($d as $firstarc => &$data) {
297 oidSort($data);
298 foreach ($data as &$rest) {
299 $out[] = ($output_with_leading_dot ? '.' : '')."$firstarc" . (($rest != $none) ? ".$rest" : '');
300 }
301 }
302 unset($data);
303
304 $ary = $out;
305 }
306
307 /**
308 * Removes leading zeroes from an OID in dot notation.
309 * @author Daniel Marschall, ViaThinkSoft
310 * @version 2015-08-17
311 * @param $oid (string)<br />
312 * An OID in dot notation.
313 * @param $leading_dot (bool)<br />
314 * true: The OID is valid, if it contains a leading dot.<br />
315 * false (default): The OID is valid, if it does not contain a leading dot.
316 * 'auto: Allow both
317 * @return (mixed) The OID without leading dots, or <code>false</code> if the OID is syntactically wrong.
318 **/
319 $oid_sanitize_cache = array();
320 function sanitizeOID($oid, $leading_dot=false) {
321 if ($leading_dot) $leading_dot = substr($oid,0,1) == '.';
322
323 // We are using a cache, since this function is used very often by OID+
324 global $oid_sanitize_cache;
325 $v = ($leading_dot ? 'T' : 'F').$oid;
326 if (isset($oid_sanitize_cache[$v])) return $oid_sanitize_cache[$v];
327
328 if ($leading_dot) {
329 if ($oid == '.') return '';
330 } else {
331 if ($oid == '') return '';
332 }
333
334 $out = '';
335 $ary = explode('.', $oid);
336 foreach ($ary as $n => &$a) {
337 if (($leading_dot) && ($n == 0)) {
338 if ($a != '') return false;
339 continue;
340 }
341
342 if (!ctype_digit($a)) return false; // does contain something other than digits
343
344 // strike leading zeroes
345 $a = preg_replace("@^0+@", '', $a);
346 if ($a == '') $a = 0;
347
348 if (($leading_dot) || ($n != 0)) $out .= '.';
349 $out .= $a;
350 }
351 unset($a);
352 unset($ary);
353
354 $oid_sanitize_cache[$v] = $out;
355 return $out;
356 }
357
358 /**
359 * Shows the top arc of an OID.
360 * This function tolerates leading dots.
361 * @author Daniel Marschall, ViaThinkSoft
362 * @version 2014-12-16
363 * @param $oid (string)<br />
364 * An OID in dot notation.
365 * @return (mixed) The top arc of the OID or empty string if it is already the root ('.')
366 **/
367 function oid_toparc($oid) {
368 $leadingdot = substr($oid,0,1) == '.';
369
370 $oid = sanitizeOID($oid, $leadingdot);
371 if ($oid === false) return false;
372
373 if (!$leadingdot) $oid = '.'.$oid;
374
375 $p = strrpos($oid, '.');
376 if ($p === false) return false;
377 $r = substr($oid, $p+1);
378
379 if ($leadingdot) {
380 # if ($r == '') return '.';
381 return $r;
382 } else {
383 return substr($r, 1);
384 }
385 }
386
387 /**
388 * Calculates the distance between two OIDs.
389 * This function tolerates leading dots and leading zeroes.
390 * @author Daniel Marschall, ViaThinkSoft
391 * @version 2014-12-20
392 * @param $a (string)<br />
393 * An OID.
394 * @param $b (string)<br />
395 * An OID.
396 * @return (string) false if both OIDs do not have a child-parent or parent-child relation, e.g. oid_distance('2.999.1.2.3', '2.999.4.5') = false, or if one of the OIDs is syntactially invalid<br />
397 * >0 if $a is more specific than $b , e.g. oid_distance('2.999.1.2', '2.999') = 2<br />
398 * <0 if $a is more common than $b , e.g. oid_distance('2.999', '2.999.1.2') = -2
399 **/
400 function oid_distance($a, $b) {
401 if (substr($a,0,1) == '.') $a = substr($a,1);
402 if (substr($b,0,1) == '.') $b = substr($b,1);
403
404 $a = sanitizeOID($a, false);
405 if ($a === false) return false;
406 $b = sanitizeOID($b, false);
407 if ($b === false) return false;
408
409 $ary = explode('.', $a);
410 $bry = explode('.', $b);
411
412 $min_len = min(count($ary), count($bry));
413
414 for ($i=0; $i<$min_len; $i++) {
415 if ($ary[$i] != $bry[$i]) return false;
416 }
417
418 return count($ary) - count($bry);
419 }
420
421 /*
422 assert(oid_distance('2.999.1.2.3', '2.999.4.5') === false);
423 assert(oid_distance('2.999.1.2', '2.999') === 2);
424 assert(oid_distance('2.999', '2.999.1.2') === -2);
425 */
426
427 /**
428 * Adds a leading dot to an OID.
429 * Leading zeroes are tolerated.
430 * @author Daniel Marschall, ViaThinkSoft
431 * @version 2014-12-20
432 * @param $oid (string)<br />
433 * An OID.
434 * @return (string) The OID with a leading dot or false if the OID is syntactially wrong.
435 **/
436 function oid_add_leading_dot($oid) {
437 $oid = sanitizeOID($oid, 'auto');
438 if ($oid === false) return false;
439
440 if ($oid[0] != '.') $oid = '.'.$oid;
441 return $oid;
442 }
443
444 /**
445 * Removes a leading dot to an OID.
446 * Leading zeroes are tolerated.
447 * @author Daniel Marschall, ViaThinkSoft
448 * @version 2014-12-20
449 * @param $oid (string)<br />
450 * An OID.
451 * @return (string) The OID without a leading dot or false if the OID is syntactially wrong.
452 **/
453 function oid_remove_leading_dot($oid) {
454 $oid = sanitizeOID($oid, 'auto');
455 if ($oid === false) return false;
456
457 if (substr($oid,0,1) == '.') $oid = substr($oid, 1);
458 return $oid;
459 }
460
461
462 # === OID-IRI NOTATION FUNCTIONS ===
463
464 if (!function_exists('mb_ord')) {
465 # http://stackoverflow.com/a/24755772/3544341
466 function mb_ord($char, $encoding = 'UTF-8') {
467 if ($encoding === 'UCS-4BE') {
468 list(, $ord) = (strlen($char) === 4) ? @unpack('N', $char) : @unpack('n', $char);
469 return $ord;
470 } else {
471 return mb_ord(mb_convert_encoding($char, 'UCS-4BE', $encoding), 'UCS-4BE');
472 }
473 }
474 }
475
476 function iri_char_valid($c, $firstchar, $lastchar) {
477 // see Rec. ITU-T X.660, clause 7.5
478
479 if (($firstchar || $lastchar) && ($c == '-')) return false;
480
481 if ($c == '-') return true;
482 if ($c == '.') return true;
483 if ($c == '_') return true;
484 if ($c == '~') return true;
485 if (($c >= '0') && ($c <= '9') && (!$firstchar)) return true;
486 if (($c >= 'A') && ($c <= 'Z')) return true;
487 if (($c >= 'a') && ($c <= 'z')) return true;
488
489 $v = mb_ord($c);
490
491 if (($v >= 0x000000A0) && ($v <= 0x0000DFFE)) return true;
492 if (($v >= 0x0000F900) && ($v <= 0x0000FDCF)) return true;
493 if (($v >= 0x0000FDF0) && ($v <= 0x0000FFEF)) return true;
494 if (($v >= 0x00010000) && ($v <= 0x0001FFFD)) return true;
495 if (($v >= 0x00020000) && ($v <= 0x0002FFFD)) return true;
496 if (($v >= 0x00030000) && ($v <= 0x0003FFFD)) return true;
497 if (($v >= 0x00040000) && ($v <= 0x0004FFFD)) return true;
498 if (($v >= 0x00050000) && ($v <= 0x0005FFFD)) return true;
499 if (($v >= 0x00060000) && ($v <= 0x0006FFFD)) return true;
500 if (($v >= 0x00070000) && ($v <= 0x0007FFFD)) return true;
501 if (($v >= 0x00080000) && ($v <= 0x0008FFFD)) return true;
502 if (($v >= 0x00090000) && ($v <= 0x0009FFFD)) return true;
503 if (($v >= 0x000A0000) && ($v <= 0x000AFFFD)) return true;
504 if (($v >= 0x000B0000) && ($v <= 0x000BFFFD)) return true;
505 if (($v >= 0x000C0000) && ($v <= 0x000CFFFD)) return true;
506 if (($v >= 0x000D0000) && ($v <= 0x000DFFFD)) return true;
507 if (($v >= 0x000E1000) && ($v <= 0x000EFFFD)) return true;
508
509 // Note: Rec. ITU-T X.660, clause 7.5.3 would also forbid ranges which are marked in ISO/IEC 10646 as "(This position shall not be used)"
510 // But tool implementers should be tolerate them, since these limitations can be removed in future.
511
512 return false;
513 }
514
515 function iri_arc_valid($arc, $allow_numeric=true) {
516 if ($arc == '') return false;
517
518 if ($allow_numeric && preg_match('@^(\\d+)$@', $arc, $m)) return true; # numeric arc
519
520 // Question: Should we strip RTL/LTR characters?
521
522 if (mb_substr($arc, 2, 2) == '--') return false; // see Rec. ITU-T X.660, clause 7.5.4
523
524 $array = array();
525 preg_match_all('/./u', $arc, $array, PREG_SET_ORDER);
526 $len = count($array);
527 foreach ($array as $i => $char) {
528 if (!iri_char_valid($char[0], $i==0, $i==$len-1)) return false;
529 }
530
531 return true;
532 }
533
534 /**
535 * Checks if an IRI identifier is valid or not.
536 * @author Daniel Marschall, ViaThinkSoft
537 * @version 2014-12-17
538 * @param $iri (string)<br />
539 * An OID in OID-IRI notation, e.g. /Example/test
540 * @return (bool) true if the IRI identifier is valid.
541 **/
542 function iri_valid($iri) {
543 if ($iri == '/') return true; // OK?
544
545 if (substr($iri, 0, 1) != '/') return false;
546
547 $ary = explode('/', $iri);
548 array_shift($ary);
549 foreach ($ary as $a) {
550 if (!iri_arc_valid($a)) return false;
551 }
552
553 return true;
554 }
555
556 /*
557 assert(iri_arc_valid('ABCDEF'));
558 assert(!iri_arc_valid('-ABCDEF'));
559 assert(!iri_arc_valid('ABCDEF-'));
560 assert(!iri_arc_valid(' ABCDEF'));
561 assert(!iri_arc_valid('2 ABCDEF'));
562 assert(!iri_arc_valid(''));
563
564 assert(!iri_valid(''));
565 assert(iri_valid('/'));
566 assert(iri_valid('/hello/world'));
567 assert(iri_valid('/123/world'));
568 assert(!iri_valid('/hello/0world'));
569 assert(!iri_valid('/hello/xo--test'));
570 assert(!iri_valid('/hello/-super-/sd'));
571 */
572
573 /**
574 * Returns an associative array in the form 'ASN.1' => '/2/1' .
575 * @author Daniel Marschall, ViaThinkSoft
576 * @version 2018-01-05
577 * @see http://itu.int/go/X660
578 * @return (array) An associative array in the form 'ASN.1' => '/2/1' .
579 **/
580 function iri_get_long_arcs() {
581 $iri_long_arcs = array();
582 $iri_long_arcs['ASN.1'] = '/2/1';
583 $iri_long_arcs['Country'] = '/2/16';
584 $iri_long_arcs['International-Organizations'] = '/2/23';
585 $iri_long_arcs['UUID'] = '/2/25';
586 $iri_long_arcs['Tag-Based'] = '/2/27';
587 $iri_long_arcs['BIP'] = '/2/41';
588 $iri_long_arcs['Telebiometrics'] = '/2/42';
589 $iri_long_arcs['Cybersecurity'] = '/2/48';
590 $iri_long_arcs['Alerting'] = '/2/49';
591 $iri_long_arcs['OIDResolutionSystem'] = '/2/50';
592 $iri_long_arcs['GS1'] = '/2/51';
593 $iri_long_arcs['Example'] = '/2/999'; // English
594 $iri_long_arcs['Exemple'] = '/2/999'; // French
595 $iri_long_arcs['Ejemplo'] = '/2/999'; // Spanish
596 $iri_long_arcs["\u{0627}\u{0644}\u{0645}\u{062B}\u{0627}\u{0644}"] = '/2/999'; // Arabic
597 $iri_long_arcs["\u{8303}\u{4F8B}"] = '/2/999'; // Chinese
598 $iri_long_arcs["\u{041F}\u{0440}\u{0438}\u{043C}\u{0435}\u{0440}"] = '/2/999'; // Russian
599 $iri_long_arcs["\u{C608}\u{C81C}"] = '/2/999'; // Korean
600 $iri_long_arcs["\u{4F8B}"] = '/2/999'; // Japanese
601 $iri_long_arcs['Beispiel'] = '/2/999'; // German
602 return $iri_long_arcs;
603 }
604
605 /**
606 * Tries to shorten/simplify an IRI by applying "long arcs", e.g. /2/999/123 -> /Example/123 .
607 * @author Daniel Marschall, ViaThinkSoft
608 * @version 2014-12-28
609 * @param $iri (string)<br />
610 * An OID in OID-IRI notation, e.g. /Example/test
611 * @return (string) The modified IRI.
612 **/
613 function iri_add_longarcs($iri) {
614 $iri_long_arcs = iri_get_long_arcs();
615
616 // TODO: $iri valid?
617
618 $ary = explode('/', $iri);
619
620 $ary_number_iri = $ary;
621 if ($ary_number_iri[1] == 'Joint-ISO-ITU-T') $ary_number_iri[1] = '2';
622 /*
623 if ($ary_number_iri[1] == '2') {
624 // TODO: /2/Example -> /2/999 -> /Example
625 } else {
626 // Currently, only long arcs inside .2 are defined
627 // return $iri;
628 }
629 */
630 $number_iri = implode('/', $ary_number_iri);
631
632 foreach ($iri_long_arcs as $cur_longarc => $cur_iri) {
633 // TODO: $cur_iri valid?
634
635 if (strpos($number_iri.'/', $cur_iri.'/') === 0) {
636 $cnt = substr_count($cur_iri, '/');
637 for ($i=1; $i<$cnt; $i++) {
638 array_shift($ary);
639 }
640 $ary[0] = '';
641 $ary[1] = $cur_longarc;
642 $iri = implode('/', $ary);
643 break;
644 }
645 }
646
647 return $iri;
648 }
649
650 # === FUNCTIONS FOR OIDS IN ASN.1 NOTATION ===
651
652 /**
653 * Checks if an ASN.1 identifier is valid.
654 * @author Daniel Marschall, ViaThinkSoft
655 * @version 2014-12-09
656 * @param $id (string)<br />
657 * An ASN.1 identifier, e.g. "example". Not "example(99)" or "99" and not a path like "{ 2 999 }"
658 * Note: Use asn1_path_valid() for validating a whole ASN.1 notation path.
659 * @return (bool) true, if the identifier is valid: It begins with an lowercase letter and contains only 0-9, a-z, A-Z and "-"
660 **/
661 # TODO: umbenennen in asn1_alpha_id_valid
662 function oid_id_is_valid($id) {
663 return preg_match('/^([a-z][a-zA-Z0-9-]*)$/', $id);
664 }
665
666 /**
667 * Checks if the ASN.1 notation of an OID is valid.
668 * This function does not tolerate leading zeros.
669 * This function will fail (return false) if there are unresolved symbols, e.g. {iso test} is not valid while { iso 123 } is valid.
670 * @author Daniel Marschall, ViaThinkSoft
671 * @version 2014-12-17
672 * @param $asn (string)<br />
673 * An OID in ASN.1 notation.
674 * @return (bools) true if the identifier is valid.
675 **/
676 function asn1_path_valid($asn1) {
677 return asn1_to_dot($asn1) != false;
678 }
679
680 /**
681 * Returns an array of standardized ASN.1 alphanumeric identifiers which do not require a numeric identifier, e.g. { 2 example }
682 * The array has the form '0.0.a' -> '0.0.1'
683 * @author Daniel Marschall, ViaThinkSoft
684 * @version 2019-03-25
685 * @see http://www.oid-info.com/name-forms.htm
686 * @return (array) Associative array of standardized ASN.1 alphanumeric identifiers
687 **/
688 function asn1_get_standardized_array() {
689
690 // Taken from oid-info.com
691 // http://www.oid-info.com/name-forms.htm
692 $standardized = array();
693 $standardized['itu-t'] = '0';
694 $standardized['ccitt'] = '0';
695 $standardized['iso'] = '1';
696 $standardized['joint-iso-itu-t'] = '2';
697 $standardized['joint-iso-ccitt'] = '2';
698 $standardized['0.recommendation'] = '0.0';
699 $standardized['0.0.a'] = '0.0.1';
700 $standardized['0.0.b'] = '0.0.2';
701 $standardized['0.0.c'] = '0.0.3';
702 $standardized['0.0.d'] = '0.0.4';
703 $standardized['0.0.e'] = '0.0.5';
704 $standardized['0.0.f'] = '0.0.6';
705 $standardized['0.0.g'] = '0.0.7';
706 $standardized['0.0.h'] = '0.0.8';
707 $standardized['0.0.i'] = '0.0.9';
708 $standardized['0.0.j'] = '0.0.10';
709 $standardized['0.0.k'] = '0.0.11';
710 $standardized['0.0.l'] = '0.0.12';
711 $standardized['0.0.m'] = '0.0.13';
712 $standardized['0.0.n'] = '0.0.14';
713 $standardized['0.0.o'] = '0.0.15';
714 $standardized['0.0.p'] = '0.0.16';
715 $standardized['0.0.q'] = '0.0.17';
716 $standardized['0.0.r'] = '0.0.18';
717 $standardized['0.0.s'] = '0.0.19';
718 $standardized['0.0.t'] = '0.0.20';
719 $standardized['0.0.u'] = '0.0.21';
720 $standardized['0.0.v'] = '0.0.22';
721 $standardized['0.0.w'] = '0.0.23'; // actually, this OID does not exist
722 $standardized['0.0.x'] = '0.0.24';
723 $standardized['0.0.y'] = '0.0.25';
724 $standardized['0.0.z'] = '0.0.26';
725 $standardized['0.question'] = '0.1';
726 $standardized['0.administration'] = '0.2';
727 $standardized['0.network-operator'] = '0.3';
728 $standardized['0.identified-organization'] = '0.4';
729 $standardized['1.standard'] = '1.0';
730 $standardized['1.registration-authority'] = '1.1';
731 $standardized['1.member-body'] = '1.2';
732 $standardized['1.identified-organization'] = '1.3';
733 return $standardized;
734 }
735
736 /**
737 * Converts an OID in ASN.1 notation into an OID in dot notation and tries to resolve well-known identifiers.<br />
738 * e.g. {joint-iso-itu-t(2) example(999) 1 2 3} --> 2.999.1.2.3<br />
739 * e.g. {iso 3} --> 1.3
740 * This function does not tolerate leading zeros.
741 * This function will fail (return false) if there are unresolved symbols, e.g. {iso test} will not be resolved to 1.test
742 * @author Daniel Marschall, ViaThinkSoft
743 * @version 2014-12-17
744 * @param $asn (string)<br />
745 * An OID in ASN.1 notation.
746 * @return (string) An OID in dot notation without leading dot or false if the path is invalid.
747 **/
748 function asn1_to_dot($asn) {
749 $standardized = asn1_get_standardized_array();
750
751 // Clean up
752 $asn = preg_replace('@^\\{(.+)\\}$@', '\\1', $asn, -1, $count);
753 if ($count == 0) return false; // { and } are required. The asn.1 path will NOT be trimmed by this function
754
755 // If identifier is set, apply it (no check if it overrides a standardized identifier)
756 $asn = preg_replace('|\s*([a-z][a-zA-Z0-9-]*)\s*\((\d+)\)|', ' \\2', $asn);
757 $asn = trim($asn);
758
759 // Set dots
760 $asn = preg_replace('|\s+|', '.', $asn);
761
762 // Apply standardized identifiers (case sensitive)
763 $asn .= '.';
764 foreach ($standardized as $s => $r) {
765 $asn = preg_replace("|^$s|", $r, $asn);
766 }
767 $asn = substr($asn, 0, strlen($asn)-1);
768
769 // Check if all numbers are OK
770 // -> every arc must be resolved
771 // -> numeric arcs must not have a leading zero
772 // -> invalid stuff will be recognized, e.g. a "(1)" without an identifier in front of it
773 $ary = explode('.', $asn);
774 foreach ($ary as $a) {
775 if (!preg_match('@^(0|([1-9]\\d*))$@', $a, $m)) return false;
776 }
777
778 return $asn;
779 }
780
781 /*
782 assert(asn1_to_dot('{2 999 (1)}') == false);
783 assert(asn1_to_dot('{2 999 test}') == false);
784 assert(asn1_to_dot('{2 999 1}') == '2.999.1');
785 assert(asn1_to_dot(' {2 999 1} ') == false);
786 assert(asn1_to_dot('2 999 1') == false);
787 assert(asn1_to_dot('{2 999 01}') == false);
788 assert(asn1_to_dot('{ 0 question 123 }') == '0.1.123');
789 assert(asn1_to_dot('{ iso }') == '1');
790 assert(asn1_to_dot('{ iso(1) }') == '1');
791 assert(asn1_to_dot('{ iso(2) }') == '2');
792 assert(asn1_to_dot('{ iso 3 }') == '1.3');
793 */
794
795 /**
796 * "Soft corrects" an invalid ASN.1 identifier.<br />
797 * Attention, by "soft correcting" the ID, it is not authoritative anymore, and might not be able to be resolved by ORS.
798 * @author Daniel Marschall, ViaThinkSoft
799 * @version 2014-12-09
800 * @param $id (string)<br />
801 * An ASN.1 identifier.
802 * @param $append_id_prefix (bool)<br />
803 * true (default): If the identifier doesn't start with a-Z, the problem will be solved by prepending "id-" to the identifier.<br />
804 * false: If the identifier doesn't start with a-Z, then the problem cannot be solved (method returns empty string).
805 * @return (string) The "soft corrected" ASN.1 identifier.<br />
806 * Invalid characters will be removed.<br />
807 * Uncorrectable start elements (0-9 or "-") will be either removed or solved by prepending "id-" (see <code>$append_id_prefix</code>)<br />
808 * If the identifier begins with an upper case letter, the letter will be converted into lower case.
809 **/
810 function oid_soft_correct_id($id, $append_id_prefix = true) {
811 // Convert "_" to "-"
812 $id = str_replace('_', '-', $id);
813
814 // Remove invalid characters
815 $id = preg_replace('/[^a-zA-Z0-9-]+/', '', $id);
816
817 // Remove uncorrectable start elements (0-9 or "-")
818 if ($append_id_prefix) {
819 $id = preg_replace('/^([^a-zA-Z]+)/', 'id-$1', $id);
820 } else {
821 $id = preg_replace('/^([^a-zA-Z]+)/', '', $id);
822 }
823
824 // "Correct" upper case beginning letter by converting it to lower case
825 if (preg_match('/^[A-Z]/', $id)) {
826 $id = strtolower($id[0]) . substr($id, 1);
827 }
828
829 return $id;
830 }
831