Subversion Repositories oidinfo_api

Rev

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