Subversion Repositories oidinfo_api

Rev

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