Subversion Repositories oidinfo_api

Rev

Rev 6 | Rev 8 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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