Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/oidinfo_api/trunk/oidinfo_api.inc.phps
Revision: 9
Committed: Wed Nov 6 23:26:33 2019 UTC (4 months, 3 weeks ago) by daniel-marschall
File size: 37301 byte(s)

File Contents

# Content
1 <?php
2
3 /*
4 * OID-Info.com API for PHP
5 * Copyright 2019 Daniel Marschall, ViaThinkSoft
6 * Version 2019-11-06
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 error_reporting(E_ALL | E_NOTICE | E_STRICT | E_DEPRECATED);
22
23 if(!defined('STDIN')) define('STDIN', fopen('php://stdin', 'rb'));
24 if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'wb'));
25 if(!defined('STDERR')) define('STDERR', fopen('php://stderr', 'wb'));
26
27 if (file_exists(__DIR__ . '/oid_utils.inc.phps')) require_once __DIR__ . '/oid_utils.inc.phps';
28 if (file_exists(__DIR__ . '/oid_utils.inc.php')) require_once __DIR__ . '/oid_utils.inc.php';
29 if (file_exists(__DIR__ . '/xml_utils.inc.phps')) require_once __DIR__ . '/xml_utils.inc.phps';
30 if (file_exists(__DIR__ . '/xml_utils.inc.php')) require_once __DIR__ . '/xml_utils.inc.php';
31 if (file_exists(__DIR__ . '/../includes/oid_utils.inc.php')) require_once __DIR__ . '/../includes/oid_utils.inc.php';
32 if (file_exists(__DIR__ . '/../includes/xml_utils.inc.php')) require_once __DIR__ . '/../includes/xml_utils.inc.php';
33 if (file_exists(__DIR__ . '/../../includes/oid_utils.inc.php')) require_once __DIR__ . '/../../includes/oid_utils.inc.php';
34 if (file_exists(__DIR__ . '/../../includes/xml_utils.inc.php')) require_once __DIR__ . '/../../includes/xml_utils.inc.php';
35 if (file_exists(__DIR__ . '/../../../includes/oid_utils.inc.php')) require_once __DIR__ . '/../../../includes/oid_utils.inc.php';
36 if (file_exists(__DIR__ . '/../../../includes/xml_utils.inc.php')) require_once __DIR__ . '/../../../includes/xml_utils.inc.php';
37
38 class OIDInfoException extends Exception {
39 }
40
41 class OIDInfoAPI {
42
43 # --- PART 0: Constants
44
45 // First digit of the ping result
46 // "-" = error
47 // "0" = OID does not exist
48 // "1" = OID does exist, but is not approved yet
49 // "2" = OID does exist and is accessible
50 /*private*/ const PING_IDX_EXISTS = 0;
51
52 // Second digit of the ping result
53 // "-" = error
54 // "0" = The OID may not be created
55 // "1" = OID is not an illegal OID, and none of its ascendant is a leaf and its parent OID is not frozen
56 /*private*/ const PING_IDX_MAY_CREATE = 1;
57
58 /*private*/ const SOFT_CORRECT_BEHAVIOR_NONE = 0;
59 /*private*/ const SOFT_CORRECT_BEHAVIOR_LOWERCASE_BEGINNING = 1;
60 /*private*/ const SOFT_CORRECT_BEHAVIOR_ALL_POSSIBLE = 2;
61
62 /*public*/ const DEFAULT_ILLEGALITY_RULE_FILE = __DIR__ . '/oid_illegality_rules';
63
64 # --- Part 1: "Ping API" for checking if OIDs are available or allowed to create
65
66 public $verbosePingProviders = array('https://misc.daniel-marschall.de/oid-repository/ping_oid.php?oid={OID}');
67
68 private $pingCache = array();
69
70 public $pingCacheMaxAge = 3600;
71
72 public function clearPingCache() {
73 $this->pingCache = array();
74 }
75
76 public function checkOnlineExists($oid) {
77 if (!self::strictCheckSyntax($oid)) return false;
78
79 $pingResult = $this->pingOID($oid);
80 $ret = $pingResult[self::PING_IDX_EXISTS] >= 1;
81 return $ret;
82 }
83
84 public function checkOnlineAvailable($oid) {
85 if (!self::strictCheckSyntax($oid)) return false;
86
87 $pingResult = $this->pingOID($oid);
88 $ret = $pingResult[self::PING_IDX_EXISTS] == 2;
89 return $ret;
90 }
91
92 public function checkOnlineAllowed($oid) {
93 if (!self::strictCheckSyntax($oid)) return false;
94
95 $pingResult = $this->pingOID($oid);
96 return $pingResult[self::PING_IDX_MAY_CREATE] == 1;
97 }
98
99 public function checkOnlineMayCreate($oid) {
100 if (!self::strictCheckSyntax($oid)) return false;
101
102 $pingResult = $this->pingOID($oid);
103
104 // OID is either illegal, or one of their parents are leaf or frozen
105 # if (!checkOnlineAllowed($oid)) return false;
106 if ($pingResult[self::PING_IDX_MAY_CREATE] == 0) return false;
107
108 // The OID exists already, so we don't need to create it again
109 # if ($this->checkOnlineExists($oid)) return false;
110 if ($pingResult[self::PING_IDX_EXISTS] >= 1) return false;
111
112 return true;
113 }
114
115 protected function pingOID($oid) {
116 if (isset($this->pingCache[$oid])) {
117 $cacheAge = $this->pingCache[$oid][0] - time();
118 if ($cacheAge <= $this->pingCacheMaxAge) {
119 return $this->pingCache[$oid][1];
120 }
121 }
122
123 if (count($this->verbosePingProviders) == 0) {
124 throw new OIDInfoException("No verbose ping provider available!");
125 }
126
127 $res = false;
128 foreach ($this->verbosePingProviders as $url) {
129 $url = str_replace('{OID}', $oid, $url);
130 $cn = @file_get_contents($url);
131 if ($cn === false) continue;
132 $loc_res = trim($cn);
133 if (strpos($loc_res, '-') === false) {
134 $res = $loc_res;
135 break;
136 }
137 }
138 if ($res === false) {
139 throw new OIDInfoException("Could not ping OID $oid status!");
140 }
141
142 // if ($this->pingCacheMaxAge >= 0) {
143 $this->pingCache[$oid] = array(time(), $res);
144 //}
145
146 return $res;
147 }
148
149 # --- PART 2: Syntax checking
150
151 public static function strictCheckSyntax($oid) {
152 return oid_valid_dotnotation($oid, false, false, 1);
153 }
154
155 // Returns false if $oid has wrong syntax
156 // Return an OID without leading dot or zeroes, if the syntax is acceptable
157 public static function trySanitizeOID($oid) {
158 // Allow leading dots and leading zeroes, but remove then afterwards
159 $ok = oid_valid_dotnotation($oid, true, true, 1);
160 if ($ok === false) return false;
161
162 return sanitizeOID($oid, $oid[0] == '.');
163 }
164
165 # --- PART 3: XML file creation
166
167 protected static function eMailValid($email) {
168 # TODO: use isemail project
169
170 if (empty($email)) return false;
171
172 if (strpos($email, '@') === false) return false;
173
174 $ary = explode('@', $email, 2);
175 if (!isset($ary[1])) return false;
176 if (strpos($ary[1], '.') === false) return false;
177
178 return true;
179 }
180
181 public function softCorrectEMail($email, $params) {
182 $email = str_replace(' ', '', $email);
183 $email = str_replace('&', '@', $email);
184 $email = str_replace('(at)', '@', $email);
185 $email = str_replace('[at]', '@', $email);
186 $email = str_replace('(dot)', '.', $email);
187 $email = str_replace('[dot]', '.', $email);
188 $email = trim($email);
189
190 if (!$params['allow_illegal_email'] && !self::eMailValid($email)) {
191 return '';
192 }
193
194 return $email;
195 }
196
197 public function softCorrectPhone($phone, $params) {
198 // TODO: if no "+", add "+1" , but only if address is in USA
199 // TODO: or use param to fixate country if it is not known
200 /*
201 NOTE: with german phone numbers, this will cause trouble, even if we assume "+49"
202 06223 / 1234
203 shall be
204 +49 6223 1234
205 and not
206 +49 06223 1234
207 */
208
209 $phone = str_replace('-', ' ', $phone);
210 $phone = str_replace('.', ' ', $phone);
211 $phone = str_replace('/', ' ', $phone);
212 $phone = str_replace('(', ' ', $phone);
213 $phone = str_replace(')', ' ', $phone);
214
215 // HL7 registry has included this accidently
216 $phone = str_replace('&quot;', '', $phone);
217
218 $phone = trim($phone);
219
220 return $phone;
221 }
222
223 private static function strip_to_xhtml_light($str, $allow_strong_text=false) {
224 // <strong> is allowed in the XSD, but not <b>
225 $str = str_ireplace('<b>', '<strong>', $str);
226 $str = str_ireplace('</b>', '</strong>', $str);
227
228 if (!$allow_strong_text) {
229 // <strong> is only used for very important things like the word "deprecated". It should therefore not used for anything else
230 $str = str_ireplace('<strong>', '', $str);
231 $str = str_ireplace('</strong>', '', $str);
232 }
233
234 $str = preg_replace('@<\s*script.+<\s*/script.*>@isU', '', $str);
235 $str = preg_replace('@<\s*style.+<\s*/style.*>@isU', '', $str);
236
237 $str = preg_replace_callback(
238 '@<(\s*/{0,1}\d*)([^\s/>]+)(\s*[^>]*)>@i',
239 function ($treffer) {
240 // see http://oid-info.com/xhtml-light.xsd
241 $whitelist = array('a', 'br', 'code', 'em', 'font', 'img', 'li', 'strong', 'sub', 'sup', 'ul');
242
243 $pre = $treffer[1];
244 $tag = $treffer[2];
245 $attrib = $treffer[3];
246 if (in_array($tag, $whitelist)) {
247 return '<'.$pre.$tag.$attrib.'>';
248 } else {
249 return '';
250 }
251 }, $str);
252
253 return $str;
254 }
255
256 const OIDINFO_CORRECT_DESC_OPTIONAL_ENDING_DOT = 0;
257 const OIDINFO_CORRECT_DESC_ENFORCE_ENDING_DOT = 1;
258 const OIDINFO_CORRECT_DESC_DISALLOW_ENDING_DOT = 2;
259
260 public function correctDesc($desc, $params, $ending_dot_policy=self::OIDINFO_CORRECT_DESC_OPTIONAL_ENDING_DOT, $enforce_xhtml_light=false) {
261 $desc = trim($desc);
262
263 $desc = preg_replace('@<!\\[CDATA\\[(.+)\\]\\]>@ismU', '\\1', $desc);
264
265 if (substr_count($desc, '>') != substr_count($desc, '<')) {
266 $params['allow_html'] = false;
267 }
268
269 $desc = str_replace("\r", '', $desc);
270
271 if (!$params['allow_html']) {
272 // htmlentities_numeric() does this for us
273 /*
274 $desc = str_replace('&', '&amp;', $desc);
275 $desc = str_replace('<', '&lt;', $desc);
276 $desc = str_replace('>', '&gt;', $desc);
277 $desc = str_replace('"', '&quot;', $desc);
278 $desc = str_replace("'", '&#39;', $desc); // &apos; is not HTML. It is XML
279 */
280
281 $desc = str_replace("\n", '<br />', $desc);
282 } else {
283 // Some problems we had with HL7 registry
284 $desc = preg_replace('@&lt;(/{0,1}(p|i|b|u|ul|li))&gt;@ismU', '<\\1>', $desc);
285 # preg_match_all('@&lt;[^ :\\@]+&gt;@ismU', $desc, $m);
286 # if (count($m[0]) > 0) print_r($m);
287
288 $desc = preg_replace('@<i>(.+)&lt;i/&gt;@ismU', '<i>\\1</i>', $desc);
289 $desc = str_replace('<p><p>', '</p><p>', $desc);
290
291 // <p> are not supported by oid-info.com
292 $desc = str_replace('<p>', '<br /><br />', $desc);
293 $desc = str_replace('</p>', '', $desc);
294 }
295
296 // Escape unicode characters as numeric &#...;
297 // The XML 1.0 standard does only has a few entities, but nothing like e.g. &euro; , so we prefer numeric
298
299 //$desc = htmlentities_numeric($desc, $params['allow_html']);
300 if (!$params['allow_html']) $desc = htmlentities($desc);
301 $desc = html_named_to_numeric_entities($desc);
302
303 // Remove HTML tags which are not allowed
304 if ($params['allow_html'] && (!$params['ignore_xhtml_light']) && $enforce_xhtml_light) {
305 // oid-info.com does only allow a few HTML tags
306 // see http://oid-info.com/xhtml-light.xsd
307 $desc = self::strip_to_xhtml_light($desc);
308 }
309
310 // Solve some XML problems...
311 $desc = preg_replace('@<\s*br\s*>@ismU', '<br/>', $desc); // auto close <br>
312 $desc = preg_replace('@(href\s*=\s*)(["\'])(.*)&([^#].*)(\2)@ismU', '\1\2\3&amp;\4\5', $desc); // fix "&" inside href-URLs to &amp;
313 // TODO: what do we do if there are more XHTML errors (e.g. additional open tags) which would make the XML invalid?
314
315 // "Trim" <br/>
316 do { $desc = preg_replace('@^\s*<\s*br\s*/{0,1}\s*>@isU', '', $desc, -1, $count); } while ($count > 0); // left trim
317 do { $desc = preg_replace('@<\s*br\s*/{0,1}\s*>\s*$@isU', '', $desc, -1, $count); } while ($count > 0); // right trim
318
319 // Correct double-encoded stuff
320 if (!isset($params['tolerant_htmlentities']) || $params['tolerant_htmlentities']) {
321 do {
322 $old_desc = $desc;
323 # Full list of entities: https://www.freeformatter.com/html-entities.html
324 # Max: 8 chars ( &thetasym; )
325 # Min: 2 chars ( lt,gt,ni,or,ne,le,ge,Mu,Nu,Xi,Pi,mu,nu,xi,pi )
326 $desc = preg_replace('@(&|&amp;)(#|&#35;)(\d+);@ismU', '&#\3;', $desc);
327 $desc = preg_replace('@(&|&amp;)([a-zA-Z0-9]{2,8});@ismU', '&\2;', $desc);
328 } while ($old_desc != $desc);
329 }
330
331 // TODO: use the complete list of oid-info.com
332 // TODO: Make this step optional using $params
333 /*
334 Array
335 (
336 [0] => Root OID for
337 [1] => OID for
338 [2] => OID identifying
339 [3] => Top arc for
340 [4] => Arc for
341 [5] => arc root
342 [6] => Node for
343 [7] => Leaf node for
344 [8] => This OID describes
345 [9] => [tT]his oid
346 [10] => This arc describes
347 [11] => This identifies
348 [12] => Identifies a
349 [13] => [Oo]bject [Ii]dentifier
350 [14] => Identifier for
351 [15] => This [Ii]dentifier is for
352 [16] => Identifiers used by
353 [17] => identifier$
354 [18] => This branch
355 [19] => Branch for
356 [20] => Child tree for
357 [21] => Child for
358 [22] => Subtree for
359 [23] => Sub-OID
360 [24] => Tree for
361 [25] => Child object
362 [26] => Parent OID
363 [27] => root for
364 [28] => Assigned for
365 [29] => Used to identify
366 [30] => Used in
367 [31] => Used for
368 [32] => For use by
369 [33] => Entry for
370 [34] => This is for
371 [35] => ["]?OID["]?
372 [36] => ^OID
373 [37] => OID$
374 [38] => oid
375 [39] => oid$
376 [40] => OIDs
377 )
378 $x = 'Root OID for ; OID for ; OID identifying ; Top arc for ; Arc for ; arc root; Node for ; Leaf node for ; This OID describes ; [tT]his oid ; This arc describes ; This identifies ; Identifies a ; [Oo]bject [Ii]dentifier; Identifier for ; This [Ii]dentifier is for ; Identifiers used by ; identifier$; This branch ; Branch for ; Child tree for ; Child for ; Subtree for ; Sub-OID; Tree for ; Child object; Parent OID; root for ; Assigned for ; Used to identify ; Used in ; Used for ; For use by ; Entry for ; This is for ; ["]?OID["]? ; ^OID ; OID$; oid ; oid$; OIDs';
379 $ary = explode('; ', $x);
380 print_r($ary);
381 */
382 $desc = preg_replace("@^Root OID for the @i", '', $desc);
383 $desc = preg_replace("@^Root OID for @i", '', $desc);
384 $desc = preg_replace("@^OID root for the @i", '', $desc);
385 $desc = preg_replace("@^OID root for @i", '', $desc);
386 $desc = preg_replace("@^This OID will be used for @i", '', $desc);
387 $desc = preg_replace("@^This will be a generic OID for the @i", '', $desc);
388 $desc = preg_replace("@^OID for @i", '', $desc);
389 $desc = preg_replace("@ Root OID$@i", '', $desc);
390 $desc = preg_replace("@ OID$@i", '', $desc);
391 $desc = preg_replace("@ OID Namespace$@i", '', $desc);
392 $desc = preg_replace("@^OID for @i", '', $desc);
393
394 $desc = rtrim($desc);
395 if ($ending_dot_policy == self::OIDINFO_CORRECT_DESC_ENFORCE_ENDING_DOT) {
396 if (($desc != '') && (substr($desc, -1)) != '.') $desc .= '.';
397 } else if ($ending_dot_policy == self::OIDINFO_CORRECT_DESC_DISALLOW_ENDING_DOT) {
398 $desc = preg_replace('@\\.$@', '', $desc);
399 }
400
401 return $desc;
402 }
403
404 public function xmlAddHeader($firstName, $lastName, $email) {
405 // TODO: encode
406
407 $firstName = htmlentities_numeric($firstName);
408 if (empty($firstName)) {
409 throw new OIDInfoException("Please supply a first name");
410 }
411
412 $lastName = htmlentities_numeric($lastName);
413 if (empty($lastName)) {
414 throw new OIDInfoException("Please supply a last name");
415 }
416
417 $email = htmlentities_numeric($email);
418 if (empty($email)) {
419 throw new OIDInfoException("Please supply an email address");
420 }
421
422 // $out = "<!DOCTYPE oid-database>\n\n";
423 $out = '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
424 $out .= '<oid-database xmlns="http://oid-info.com"'."\n";
425 $out .= ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
426 $out .= ' xsi:schemaLocation="http://oid-info.com '."\n";
427 $out .= ' http://oid-info.com/oid.xsd">'."\n";
428 $out .= "\t<submitter>\n";
429 $out .= "\t\t<first-name>$firstName</first-name>\n";
430 $out .= "\t\t<last-name>$lastName</last-name>\n";
431 $out .= "\t\t<email>$email</email>\n";
432 $out .= "\t</submitter>\n";
433
434 if (!self::eMailValid($email)) {
435 throw new OIDInfoException("eMail address '$email' is invalid");
436 }
437
438 return $out;
439 }
440
441 public function xmlAddFooter() {
442 return "</oid-database>\n";
443 }
444
445 /*
446 -- CODE TEMPLATE --
447
448 $params['allow_html'] = false; // Allow HTML in <description> and <information>
449 $params['allow_illegal_email'] = true; // We should allow it, because we don't know if the user has some kind of human-readable anti-spam technique
450 $params['soft_correct_behavior'] = OIDInfoAPI::SOFT_CORRECT_BEHAVIOR_NONE;
451 $params['do_online_check'] = false; // Flag to disable this online check, because it generates a lot of traffic and runtime.
452 $params['do_illegality_check'] = true;
453 $params['do_simpleping_check'] = true;
454 $params['auto_extract_name'] = '';
455 $params['auto_extract_url'] = '';
456 $params['always_output_comment'] = false; // Also output comment if there was an error (e.g. OID already existing)
457 $params['creation_allowed_check'] = true;
458 $params['tolerant_htmlentities'] = true;
459 $params['ignore_xhtml_light'] = false;
460
461 $elements['synonymous-identifier'] = ''; // string or array
462 $elements['description'] = '';
463 $elements['information'] = '';
464
465 $elements['first-registrant']['first-name'] = '';
466 $elements['first-registrant']['last-name'] = '';
467 $elements['first-registrant']['address'] = '';
468 $elements['first-registrant']['email'] = '';
469 $elements['first-registrant']['phone'] = '';
470 $elements['first-registrant']['fax'] = '';
471 $elements['first-registrant']['creation-date'] = '';
472
473 $elements['current-registrant']['first-name'] = '';
474 $elements['current-registrant']['last-name'] = '';
475 $elements['current-registrant']['address'] = '';
476 $elements['current-registrant']['email'] = '';
477 $elements['current-registrant']['phone'] = '';
478 $elements['current-registrant']['fax'] = '';
479 $elements['current-registrant']['modification-date'] = '';
480
481 $oid = '1.2.3';
482
483 $comment = 'test';
484
485 echo $oa->createXMLEntry($oid, $elements, $params, $comment);
486 */
487 public function createXMLEntry($oid, $elements, $params, $comment='') {
488 // Backward compatibility
489 if (!isset($params['do_csv_check'])) $params['do_simpleping_check'] = true;
490
491 // Set default behavior
492 if (!isset($params['allow_html'])) $params['allow_html'] = false; // Allow HTML in <description> and <information>
493 if (!isset($params['allow_illegal_email'])) $params['allow_illegal_email'] = true; // We should allow it, because we don't know if the user has some kind of human-readable anti-spam technique
494 if (!isset($params['soft_correct_behavior'])) $params['soft_correct_behavior'] = self::SOFT_CORRECT_BEHAVIOR_NONE;
495 if (!isset($params['do_online_check'])) $params['do_online_check'] = false; // Flag to disable this online check, because it generates a lot of traffic and runtime.
496 if (!isset($params['do_illegality_check'])) $params['do_illegality_check'] = true;
497 if (!isset($params['do_simpleping_check'])) $params['do_simpleping_check'] = true;
498 if (!isset($params['auto_extract_name'])) $params['auto_extract_name'] = '';
499 if (!isset($params['auto_extract_url'])) $params['auto_extract_url'] = '';
500 if (!isset($params['always_output_comment'])) $params['always_output_comment'] = false; // Also output comment if there was an error (e.g. OID already existing)
501 if (!isset($params['creation_allowed_check'])) $params['creation_allowed_check'] = true;
502 if (!isset($params['tolerant_htmlentities'])) $params['tolerant_htmlentities'] = true;
503 if (!isset($params['ignore_xhtml_light'])) $params['ignore_xhtml_light'] = false;
504
505 $out = '';
506 if (!empty($comment)) $out .= "\t\t<!-- $comment -->\n";
507
508 if ($params['always_output_comment']) {
509 $err = $out;
510 } else {
511 $err = false;
512 }
513
514 if (isset($elements['dotted_oid'])) {
515 throw new OIDInfoException("'dotted_oid' in the \$elements array is not supported. Please use the \$oid argument.");
516 }
517 if (isset($elements['value'])) {
518 // TODO: WHAT SHOULD WE DO WITH THAT?
519 throw new OIDInfoException("'value' in the \$elements array is currently not supported.");
520 }
521
522 $bak_oid = $oid;
523 $oid = self::trySanitizeOID($oid);
524 if ($oid === false) {
525 fwrite(STDOUT/*STDERR*/,"<!-- ERROR: Ignored '$bak_oid', because it is not a valid OID -->\n");
526 return $err;
527 }
528
529 if ($params['creation_allowed_check']) {
530 if (!$this->oidMayCreate($oid, $params['do_online_check'], $params['do_simpleping_check'], $params['do_illegality_check'])) {
531 fwrite(STDOUT/*STDERR*/,"<!-- ERROR: Creation of $oid disallowed -->\n");
532 return $err;
533 }
534 } else {
535 if ($params['do_illegality_check'] && ($this->illegalOid($oid))) {
536 fwrite(STDOUT/*STDERR*/,"<!-- ERROR: Creation of $oid disallowed -->\n");
537 return $err;
538 }
539 }
540
541 $elements['description'] = $this->correctDesc($elements['description'], $params, self::OIDINFO_CORRECT_DESC_DISALLOW_ENDING_DOT, true);
542 $elements['information'] = $this->correctDesc($elements['information'], $params, self::OIDINFO_CORRECT_DESC_ENFORCE_ENDING_DOT, true);
543
544 // Request by O.D. 26 August 2019
545 $elements['description'] = trim($elements['description']);
546 if (preg_match('@^[a-z]@', $elements['description'], $m)) {
547 $ending_dot_policy = self::OIDINFO_CORRECT_DESC_DISALLOW_ENDING_DOT; // for description
548 if (($ending_dot_policy != self::OIDINFO_CORRECT_DESC_ENFORCE_ENDING_DOT) && (strpos($elements['description'], ' ') === false)) { // <-- added by DM
549 $elements['description'] = '"' . $elements['description'] . '"';
550 }
551 }
552 // End request by O.D. 26. August 2019
553
554 if ($params['auto_extract_name'] || $params['auto_extract_url']) {
555 if (!empty($elements['information'])) $elements['information'] .= '<br /><br />';
556 if ($params['auto_extract_name'] || $params['auto_extract_url']) {
557 $elements['information'] .= 'Automatically extracted from <a href="'.$params['auto_extract_url'].'">'.$params['auto_extract_name'].'</a>.';
558 } else if ($params['auto_extract_name']) {
559 $elements['information'] .= 'Automatically extracted from '.$params['auto_extract_name'];
560 } else if ($params['auto_extract_url']) {
561 $hr_url = $params['auto_extract_url'];
562 // $hr_url = preg_replace('@^https{0,1}://@ismU', '', $hr_url);
563 $hr_url = preg_replace('@^http://@ismU', '', $hr_url);
564 $elements['information'] .= 'Automatically extracted from <a href="'.$params['auto_extract_url'].'">'.$hr_url.'</a>.';
565 }
566 }
567
568 // Validate ASN.1 ID
569 if (isset($elements['synonymous-identifier'])) {
570 if (!is_array($elements['synonymous-identifier'])) {
571 $elements['synonymous-identifier'] = array($elements['synonymous-identifier']);
572 }
573 foreach ($elements['synonymous-identifier'] as &$synid) {
574 if ($synid == '') {
575 $synid = null;
576 continue;
577 }
578
579 $behavior = $params['soft_correct_behavior'];
580
581 if ($behavior == self::SOFT_CORRECT_BEHAVIOR_NONE) {
582 if (!oid_id_is_valid($synid)) $synid = null;
583 } else if ($behavior == self::SOFT_CORRECT_BEHAVIOR_LOWERCASE_BEGINNING) {
584 $synid[0] = strtolower($synid[0]);
585 if (!oid_id_is_valid($synid)) $synid = null;
586 } else if ($behavior == self::SOFT_CORRECT_BEHAVIOR_ALL_POSSIBLE) {
587 $synid = oid_soft_correct_id($synid);
588 // if (!oid_id_is_valid($synid)) $synid = null;
589 } else {
590 throw new OIDInfoException("Unexpected soft-correction behavior for ASN.1 IDs");
591 }
592 }
593 }
594
595 // ATTENTION: the XML-generator will always add <dotted-oid> , but what will happen if additionally an
596 // asn1-path (<value>) is given? (the resulting OIDs might be inconsistent/mismatch)
597 if (isset($elements['value']) && (!asn1_path_valid($elements['value']))) {
598 unset($elements['value']);
599 }
600
601 // Validate IRI (currently not supported by oid-info.com, but the tag name is reserved)
602 if (isset($elements['iri'])) {
603 if (!is_array($elements['iri'])) {
604 $elements['iri'] = array($elements['iri']);
605 }
606 foreach ($elements['iri'] as &$iri) {
607 // Numeric-only nicht erlauben. Das wäre ja nur in einem IRI-Pfad gültig, aber nicht als einzelner Identifier
608 if (!iri_arc_valid($iri, false)) $iri = null;
609 }
610 }
611
612 if (isset($elements['first-registrant']['phone']))
613 $elements['first-registrant']['phone'] = $this->softCorrectPhone($elements['first-registrant']['phone'], $params);
614
615 if (isset($elements['current-registrant']['phone']))
616 $elements['current-registrant']['phone'] = $this->softCorrectPhone($elements['current-registrant']['phone'], $params);
617
618 if (isset($elements['first-registrant']['fax']))
619 $elements['first-registrant']['fax'] = $this->softCorrectPhone($elements['first-registrant']['fax'], $params);
620
621 if (isset($elements['current-registrant']['fax']))
622 $elements['current-registrant']['fax'] = $this->softCorrectPhone($elements['current-registrant']['fax'], $params);
623
624 if (isset($elements['first-registrant']['email']))
625 $elements['first-registrant']['email'] = $this->softCorrectEMail($elements['first-registrant']['email'], $params);
626
627 if (isset($elements['current-registrant']['email']))
628 $elements['current-registrant']['email'] = $this->softCorrectEMail($elements['current-registrant']['email'], $params);
629
630 // TODO: if name is empty, but address has 1 line, take it as firstname (but remove hyperlink)
631
632 $out_loc = '';
633 foreach ($elements as $name => $val) {
634 if (($name == 'first-registrant') || ($name == 'current-registrant')) {
635 $out_loc2 = '';
636 foreach ($val as $name2 => $val2) {
637 if (is_null($val2)) continue;
638 if (empty($val2)) continue;
639
640 if (!is_array($val2)) $val2 = array($val2);
641
642 foreach ($val2 as $val3) {
643 // if (is_null($val3)) continue;
644 if (empty($val3)) continue;
645
646 if ($name2 == 'address') {
647 // $val3 = htmlentities_numeric($val3);
648 $val3 = $this->correctDesc($val3, $params, self::OIDINFO_CORRECT_DESC_DISALLOW_ENDING_DOT, true);
649 } else {
650 // $val3 = htmlentities_numeric($val3);
651 $val3 = $this->correctDesc($val3, $params, self::OIDINFO_CORRECT_DESC_DISALLOW_ENDING_DOT, false);
652 }
653 $out_loc2 .= "\t\t\t<$name2>".$val3."</$name2>\n";
654 }
655 }
656
657 if (!empty($out_loc2)) {
658 $out_loc .= "\t\t<$name>\n";
659 $out_loc .= $out_loc2;
660 $out_loc .= "\t\t</$name>\n";
661 }
662 } else {
663 // if (is_null($val)) continue;
664 if (empty($val) && ($name != 'description')) continue; // description is mandatory, according to http://oid-info.com/oid.xsd
665
666 if (!is_array($val)) $val = array($val);
667
668 foreach ($val as $val2) {
669 // if (is_null($val2)) continue;
670 if (empty($val2) && ($name != 'description')) continue; // description is mandatory, according to http://oid-info.com/oid.xsd
671
672 if (($name != 'description') && ($name != 'information')) { // don't correctDesc description/information, because we already did it above.
673 // $val2 = htmlentities_numeric($val2);
674 $val2 = $this->correctDesc($val2, $params, self::OIDINFO_CORRECT_DESC_OPTIONAL_ENDING_DOT, false);
675 }
676 $out_loc .= "\t\t<$name>".$val2."</$name>\n";
677 }
678 }
679 }
680
681 if (!empty($out)) {
682 $out = "\t<oid>\n"."\t\t".trim($out)."\n";
683 } else {
684 $out = "\t<oid>\n";
685 }
686 $out .= "\t\t<dot-notation>$oid</dot-notation>\n";
687 $out .= $out_loc;
688 $out .= "\t</oid>\n";
689
690 return $out;
691 }
692
693 # --- PART 4: Offline check if OIDs are illegal
694
695 protected $illegality_rules = array();
696
697 public function clearIllegalityRules() {
698 $this->illegality_rules = array();
699 }
700
701 public function loadIllegalityRuleFile($file) {
702 if (!file_exists($file)) {
703 throw new OIDInfoException("Error: File '$file' does not exist");
704 }
705
706 $lines = file($file);
707
708 if ($lines === false) {
709 throw new OIDInfoException("Error: Could not load '$file'");
710 }
711
712 $signature = trim(array_shift($lines));
713 if (($signature != '[1.3.6.1.4.1.37476.3.1.5.1]') && ($signature != '[1.3.6.1.4.1.37476.3.1.5.2]')) {
714 throw new OIDInfoException("'$file' does not seem to a valid illegality rule file (file format OID does not match. Signature $signature unexpected)");
715 }
716
717 foreach ($lines as $line) {
718 // Remove comments
719 $ary = explode('--', $line);
720 $rule = trim($ary[0]);
721
722 if ($rule !== '') $this->addIllegalityRule($rule);
723 }
724 }
725
726 public function addIllegalityRule($rule) {
727 $test = $rule;
728 $test = preg_replace('@\\.\\(!\\d+\\)@ismU', '.0', $test); // added in ver 2
729 $test = preg_replace('@\\.\\(\\d+\\+\\)@ismU', '.0', $test);
730 $test = preg_replace('@\\.\\(\\d+\\-\\)@ismU', '.0', $test);
731 $test = preg_replace('@\\.\\(\\d+\\-\\d+\\)@ismU', '.0', $test);
732 $test = preg_replace('@\\.\\*@ismU', '.0', $test);
733
734 if (!oid_valid_dotnotation($test, false, false, 1)) {
735 throw new OIDInfoException("Illegal illegality rule '$rule'.");
736 }
737
738 $this->illegality_rules[] = $rule;
739 }
740
741 private static function bigint_cmp($a, $b) {
742 if (function_exists('bccomp')) {
743 return bccomp($a, $b);
744 }
745
746 if (function_exists('gmp_cmp')) {
747 return gmp_cmp($a, $b);
748 }
749
750 if ($a > $b) return 1;
751 if ($a < $b) return -1;
752 return 0;
753 }
754
755 public function illegalOID($oid, &$illegal_root='') {
756 $bak = $oid;
757 $oid = self::trySanitizeOID($oid);
758 if ($oid === false) {
759 $illegal_root = $bak;
760 return true; // is illegal
761 }
762
763 $rules = $this->illegality_rules;
764
765 foreach ($rules as $rule) {
766 $rule = str_replace(array('(', ')'), '', $rule);
767
768 $oarr = explode('.', $oid);
769 $rarr = explode('.', $rule);
770
771 if (count($oarr) < count($rarr)) continue;
772
773 $rulefit = true;
774
775 $illrootary = array();
776
777 $vararcs = 0;
778 $varsfit = 0;
779 for ($i=0; $i<count($rarr); $i++) {
780 $oelem = $oarr[$i];
781 $relem = $rarr[$i];
782
783 $illrootary[] = $oelem;
784
785 if ($relem == '*') $relem = '0+';
786
787 $startchar = substr($relem, 0, 1);
788 $endchar = substr($relem, -1, 1);
789 if ($startchar == '!') { // added in ver 2
790 $vararcs++;
791 $relem = substr($relem, 1, strlen($relem)-1); // cut away first char
792 $cmp = self::bigint_cmp($oelem, $relem) != 0;
793 if ($cmp) $varsfit++;
794 } else if ($endchar == '+') {
795 $vararcs++;
796 $relem = substr($relem, 0, strlen($relem)-1); // cut away last char
797 $cmp = self::bigint_cmp($oelem, $relem) >= 0;
798 if ($cmp) $varsfit++;
799 } else if ($endchar == '-') {
800 $vararcs++;
801 $relem = substr($relem, 0, strlen($relem)-1); // cut away last char
802 $cmp = self::bigint_cmp($oelem, $relem) <= 0;
803 if ($cmp) $varsfit++;
804 } else if (strpos($relem, '-') !== false) {
805 $vararcs++;
806 $limarr = explode('-', $relem);
807 $limmin = $limarr[0];
808 $limmax = $limarr[1];
809 $cmp_min = self::bigint_cmp($oelem, $limmin) >= 0;
810 $cmp_max = self::bigint_cmp($oelem, $limmax) <= 0;
811 if ($cmp_min && $cmp_max) $varsfit++;
812 } else {
813 if ($relem != $oelem) {
814 $rulefit = false;
815 break;
816 }
817 }
818 }
819
820 if ($rulefit && ($vararcs == $varsfit)) {
821 $illegal_root = implode('.', $illrootary);
822 return true; // is illegal
823 }
824 }
825
826 $illegal_root = '';
827 return false; // not illegal
828 }
829
830 # --- PART 5: Misc functions
831
832 function __construct() {
833 if (file_exists(self::DEFAULT_ILLEGALITY_RULE_FILE)) {
834 $this->loadIllegalityRuleFile(self::DEFAULT_ILLEGALITY_RULE_FILE);
835 }
836 }
837
838 public static function getPublicURL($oid) {
839 return "http://oid-info.com/get/$oid";
840 }
841
842 public function oidExisting($oid, $onlineCheck=true, $useSimplePingProvider=true) {
843 $bak_oid = $oid;
844 $oid = self::trySanitizeOID($oid);
845 if ($oid === false) {
846 throw new OIDInfoException("'$bak_oid' is not a valid OID");
847 }
848
849 $canuseSimplePingProvider = $useSimplePingProvider && $this->simplePingProviderAvailable();
850 if ($canuseSimplePingProvider) {
851 if ($this->simplePingProviderCheckOID($oid)) return true;
852 }
853 if ($onlineCheck) {
854 return $this->checkOnlineExists($oid);
855 }
856 if ((!$canuseSimplePingProvider) && (!$onlineCheck)) {
857 throw new OIDInfoException("No simple or verbose checking method chosen/available");
858 }
859 return false;
860 }
861
862 public function oidMayCreate($oid, $onlineCheck=true, $useSimplePingProvider=true, $illegalityCheck=true) {
863 $bak_oid = $oid;
864 $oid = self::trySanitizeOID($oid);
865 if ($oid === false) {
866 throw new OIDInfoException("'$bak_oid' is not a valid OID");
867 }
868
869 if ($illegalityCheck && $this->illegalOID($oid)) return false;
870
871 $canuseSimplePingProvider = $useSimplePingProvider && $this->simplePingProviderAvailable();
872 if ($canuseSimplePingProvider) {
873 if ($this->simplePingProviderCheckOID($oid)) return false;
874 }
875 if ($onlineCheck) {
876 return $this->checkOnlineMayCreate($oid);
877 }
878 if ((!$canuseSimplePingProvider) && (!$onlineCheck)) {
879 throw new OIDInfoException("No simple or verbose checking method chosen/available");
880 }
881 return true;
882 }
883
884 # --- PART 6: Simple Ping Providers
885 # TODO: Question ... can't these provider concepts (SPP and VPP) not somehow be combined?
886
887 protected $simplePingProviders = array();
888
889 public function addSimplePingProvider($addr) {
890 if (!isset($this->simplePingProviders[$addr])) {
891 if (strtolower(substr($addr, -4, 4)) == '.csv') {
892 $this->simplePingProviders[$addr] = new CSVSimplePingProvider($addr);
893 } else {
894 $this->simplePingProviders[$addr] = new OIDSimplePingProvider($addr);
895 // $this->simplePingProviders[$addr]->connect();
896 }
897 }
898 return $this->simplePingProviders[$addr];
899 }
900
901 public function removeSimplePingProvider($addr) {
902 $this->simplePingProviders[$addr]->disconnect();
903 unset($this->simplePingProviders[$addr]);
904 }
905
906 public function removeAllSimplePingProviders() {
907 foreach ($this->simplePingProviders as $addr => $obj) {
908 $this->removeSimplePingProvider($addr);
909 }
910 }
911
912 public function listSimplePingProviders() {
913 $out = array();
914 foreach ($this->simplePingProviders as $addr => $obj) {
915 $out[] = $addr;
916 }
917 return $out;
918 }
919
920 public function simplePingProviderCheckOID($oid) {
921 if (!$this->simplePingProviderAvailable()) {
922 throw new OIDInfoException("No simple ping providers available.");
923 }
924
925 $one_null = false;
926 foreach ($this->simplePingProviders as $addr => $obj) {
927 $res = $obj->queryOID($oid);
928 if ($res) return true;
929 if ($res !== false) $one_null = true;
930 }
931
932 return $one_null ? null : false;
933 }
934
935 public function simplePingProviderAvailable() {
936 return count($this->simplePingProviders) >= 1;
937 }
938
939 }
940
941 interface IOIDSimplePingProvider {
942 public function queryOID($oid);
943 public function disconnect();
944 public function connect();
945 }
946
947 class CSVSimplePingProvider implements IOIDSimplePingProvider {
948 protected $csvfile = '';
949 protected $lines = array();
950 protected $filemtime = 0;
951
952 public function queryOID($oid) {
953 $this->reloadCSV();
954 return in_array($oid, $this->lines);
955 }
956
957 public function disconnect() {
958 // Nothing
959 }
960
961 public function connect() {
962 // Nothing
963 }
964
965 // TODO: This cannot handle big CSVs. We need to introduce the old code of "2016-09-02_old_oidinfo_api_with_csv_reader.zip" here.
966 protected function reloadCSV() {
967 if (!file_exists($this->csvfile)) {
968 throw new OIDInfoException("File '".$this->csvfile."' does not exist");
969 }
970 $filemtime = filemtime($this->csvfile);
971 if ($filemtime != $this->filemtime) {
972 $this->lines = file($csvfile);
973 $this->filemtime = $filemtime;
974 }
975 }
976
977 function __construct($csvfile) {
978 $this->csvfile = $csvfile;
979 $this->reloadCSV();
980 }
981 }
982
983
984 class OIDSimplePingProvider implements IOIDSimplePingProvider {
985 protected $addr = '';
986 protected $connected = false;
987 protected $socket = null;
988
989 const SPP_MAX_CONNECTION_ATTEMPTS = 3; // TODO: Auslagern in OIDInfoAPI Klasse...?
990
991 const DEFAULT_PORT = 49500;
992
993 protected function spp_reader_init() {
994 $this->spp_reader_uninit();
995
996 $ary = explode(':', $this->addr);
997 $host = $ary[0];
998 $service_port = isset($ary[1]) ? $ary[1] : self::DEFAULT_PORT;
999 $address = @gethostbyname($host);
1000 if ($address === false) {
1001 echo "gethostbyname() failed.\n"; // TODO: exceptions? (Auch alle "echos" darunter)
1002 return false;
1003 }
1004 $this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
1005 if ($this->socket === false) {
1006 echo "socket_create() failed: " . socket_strerror(socket_last_error()) . "\n";
1007 return false;
1008 }
1009 $result = @socket_connect($this->socket, $address, $service_port);
1010 if ($result === false) {
1011 echo "socket_connect() failed: " . socket_strerror(socket_last_error($this->socket)) . "\n";
1012 return false;
1013 }
1014
1015 $this->connected = true;
1016 }
1017
1018 protected function spp_reader_avail($oid, $failcount=0) {
1019 $in = "${oid}\n\0"; // PHP's socket_send() does not send a trailing \n . There needs to be something after the \n ... :(
1020
1021 if ($failcount >= self::SPP_MAX_CONNECTION_ATTEMPTS) {
1022 echo "Query $oid: CONNECTION FAILED!\n";
1023 return null;
1024 }
1025
1026 if (!$this->connected) {
1027 $this->spp_reader_init();
1028 }
1029
1030 $s = @socket_send($this->socket, $in, strlen($in), 0);
1031 if ($s != strlen($in)) {
1032 // echo "Query $oid: Sending failed\n";
1033 $this->spp_reader_init();
1034 if (!$this->socket) return null;
1035 return $this->spp_reader_avail($oid, $failcount+1);
1036 }
1037
1038 $out = @socket_read($this->socket, 2048);
1039 if (trim($out) == '1') {
1040 return true;
1041 } else if (trim($out) == '0') {
1042 return false;
1043 } else {
1044 // echo "Query $oid: Receiving failed\n";
1045 $this->spp_reader_init();
1046 if (!$this->socket) return null;
1047 return $this->spp_reader_avail($oid, $failcount+1);
1048 }
1049 }
1050
1051 protected function spp_reader_uninit() {
1052 if (!$this->connected) return;
1053 @socket_close($this->socket);
1054 $this->connected = false;
1055 }
1056
1057 public function queryOID($oid) {
1058 if (trim($oid) === 'bye') return null;
1059 return $this->spp_reader_avail($oid);
1060 }
1061
1062 public function disconnect() {
1063 return $this->spp_reader_uninit();
1064 }
1065
1066 public function connect() {
1067 return $this->spp_reader_init();
1068 }
1069
1070 function __construct($addr='localhost:49500') {
1071 $this->addr = $addr;
1072 }
1073
1074 }