Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
635 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
635 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
635 daniel-mar 21
 
1086 daniel-mar 22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
1131 daniel-mar 26
class OIDplusPageAdminOIDInfoExport extends OIDplusPagePluginAdmin
27
        implements INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8 /* getNotifications */
28
{
635 daniel-mar 29
 
1130 daniel-mar 30
        /**
31
         *
32
         */
635 daniel-mar 33
        /*private*/ const QUERY_LIST_OIDINFO_OIDS_V1 = '1.3.6.1.4.1.37476.2.5.2.1.5.1';
1130 daniel-mar 34
 
35
        /**
36
         *
37
         */
635 daniel-mar 38
        /*private*/ const QUERY_GET_OIDINFO_DATA_V1  = '1.3.6.1.4.1.37476.2.5.2.1.6.1';
39
 
1116 daniel-mar 40
        /**
41
         * @param string $actionID
42
         * @param array $params
43
         * @return array|int[]
44
         * @throws OIDplusException
45
         */
46
        public function action(string $actionID, array $params): array {
635 daniel-mar 47
 
48
                if ($actionID == 'import_xml_file') {
49
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
50
                                throw new OIDplusException(_L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')));
51
                        }
52
 
53
                        if (!isset($_FILES['userfile'])) {
54
                                throw new OIDplusException(_L('Please choose a file.'));
55
                        }
56
 
57
                        $xml_contents = file_get_contents($_FILES['userfile']['tmp_name']);
58
 
59
                        $errors = array();
60
                        list($count_imported_oids, $count_already_existing, $count_errors, $count_warnings) = $this->oidinfoImportXML($xml_contents, $errors, $replaceExistingOIDs=false, $orphan_mode=self::ORPHAN_AUTO_DEORPHAN);
61
                        if (count($errors) > 0) {
62
                                // Note: These "errors" can also be warnings (partial success)
63
                                // TODO: since the output can be very long, should we really show it in a JavaScript alert() ?!
64
                                return array(
65
                                        "status" => -1,
66
                                        "count_imported_oids" => $count_imported_oids,
67
                                        "count_already_existing" => $count_already_existing,
68
                                        "count_errors" => $count_errors,
69
                                        "count_warnings" => $count_warnings,
70
                                        "error" => implode("\n",$errors)
71
                                );
72
                        } else {
73
                                return array(
74
                                        "status" => 0,
75
                                        "count_imported_oids" => $count_imported_oids,
76
                                        "count_already_existing" => $count_already_existing,
77
                                        "count_errors" => $count_errors,
78
                                        "count_warnings" => $count_warnings
79
                                );
80
                        }
81
                } else if ($actionID == 'import_oidinfo_oid') {
82
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
83
                                throw new OIDplusException(_L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')));
84
                        }
85
 
86
                        _CheckParamExists($params, 'oid');
87
 
88
                        $oid = $params['oid'];
89
 
90
                        $query = self::QUERY_GET_OIDINFO_DATA_V1;
91
 
92
                        $payload = array(
93
                                "query" => $query, // we must repeat the query because we want to sign it
94
                                "system_id" => OIDplus::getSystemId(false),
95
                                "oid" => $oid
96
                        );
97
 
98
                        $signature = '';
830 daniel-mar 99
                        if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
635 daniel-mar 100
                                if (!OIDplus::getPkiStatus()) {
101
                                        throw new OIDplusException(_L('Error: Your system could not generate a private/public key pair. (OpenSSL is probably missing on your system). Therefore, you cannot register/unregister your OIDplus instance.'));
102
                                } else {
103
                                        throw new OIDplusException(_L('Signature failed'));
104
                                }
105
                        }
106
 
107
                        $data = array(
108
                                "payload" => $payload,
109
                                "signature" => base64_encode($signature)
110
                        );
111
 
112
                        if (!function_exists('curl_init')) {
113
                                throw new OIDplusException(_L('The "%1" PHP extension is not installed at your system. Please enable the PHP extension <code>%2</code>.','CURL','php_curl'));
114
                        }
115
 
699 daniel-mar 116
                        if (OIDplus::getEditionInfo()['vendor'] != 'ViaThinkSoft') {
117
                                // The oid-info.com import functionality is a confidential API between ViaThinkSoft and oid-info.com and cannot be used in forks of OIDplus
118
                                throw new OIDplusException(_L('This feature is only available in the ViaThinkSoft edition of OIDplus'));
119
                        }
120
 
635 daniel-mar 121
                        $ch = curl_init();
122
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
123
                        curl_setopt($ch, CURLOPT_URL, 'https://oidplus.viathinksoft.com/reg2/query.php');
715 daniel-mar 124
                        curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
635 daniel-mar 125
                        curl_setopt($ch, CURLOPT_POST, 1);
126
                        if (function_exists('gzdeflate')) {
127
                                $compressed = "1";
128
                                $data2 = gzdeflate(json_encode($data));
129
                        } else {
130
                                $compressed = "0";
131
                                $data2 = json_encode($data);
132
                        }
133
                        curl_setopt($ch, CURLOPT_POSTFIELDS, "query=".urlencode($query)."&compressed=$compressed&data=".urlencode(base64_encode($data2)));
134
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
135
                        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
136
                        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
977 daniel-mar 137
                        if (!($res_curl = @curl_exec($ch))) {
635 daniel-mar 138
                                throw new OIDplusException(_L('Communication with ViaThinkSoft server failed: %1',curl_error($ch)));
139
                        }
140
                        curl_close($ch);
141
 
977 daniel-mar 142
                        $json = @json_decode($res_curl, true);
635 daniel-mar 143
 
144
                        if (!$json) {
145
                                return array(
146
                                        "status" => -1,
977 daniel-mar 147
                                        "error" => _L('JSON reply from ViaThinkSoft decoding error: %1',$res_curl)
635 daniel-mar 148
                                );
149
                        }
150
 
151
                        if (isset($json['error']) || ($json['status'] < 0)) {
152
                                return array(
153
                                        "status" => -1,
1130 daniel-mar 154
                                        "error" => $json['error'] ?? _L('Received error status code: %1', $json['status'])
635 daniel-mar 155
                                );
156
                        }
157
 
158
                        $errors = array();
159
                        list($count_imported_oids, $count_already_existing, $count_errors, $count_warnings) = $this->oidinfoImportXML('<oid-database>'.$json['xml'].'</oid-database>', $errors, $replaceExistingOIDs=false, $orphan_mode=self::ORPHAN_DISALLOW_ORPHANS);
160
                        if (count($errors) > 0) {
161
                                return array("status" => -1, "error" => implode("\n",$errors));
162
                        } else if ($count_imported_oids <> 1) {
163
                                return array("status" => -1, "error" => _L('Imported %1, but expected to import 1',$count_imported_oids));
164
                        } else {
165
                                return array("status" => 0);
166
                        }
167
                } else {
1116 daniel-mar 168
                        return parent::action($actionID, $params);
635 daniel-mar 169
                }
170
        }
171
 
1116 daniel-mar 172
        /**
173
         * @param bool $html
174
         * @return void
175
         */
176
        public function init(bool $html=true) {
635 daniel-mar 177
                // Nothing
178
        }
179
 
1116 daniel-mar 180
        /**
181
         * @param string $id
182
         * @param array $out
183
         * @param bool $handled
184
         * @return void
185
         * @throws OIDplusException
186
         */
187
        public function gui(string $id, array &$out, bool &$handled) {
635 daniel-mar 188
                $ary = explode('$', $id);
189
                if (isset($ary[1])) {
190
                        $id = $ary[0];
191
                        $tab = $ary[1];
192
                } else {
193
                        $tab = 'export';
194
                }
195
                if ($id === 'oidplus:oidinfo_compare_export') {
196
                        $handled = true;
197
                        $out['title'] = _L('List OIDs in your system which are missing at oid-info.com');
801 daniel-mar 198
                        $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
635 daniel-mar 199
 
200
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
800 daniel-mar 201
                                $out['icon'] = 'img/error.png';
635 daniel-mar 202
                                $out['text'] = '<p>'._L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')).'</p>';
203
                                return;
204
                        }
205
 
206
                        $query = self::QUERY_LIST_OIDINFO_OIDS_V1;
207
 
208
                        $payload = array(
209
                                "query" => $query, // we must repeat the query because we want to sign it
210
                                "system_id" => OIDplus::getSystemId(false),
211
                                "show_all" => 1 // this is required so that the VTS OIDRA gets no false notifications for adding the systems in the directory 1.3.6.1.4.1.37476.30.9
212
                        );
213
 
214
                        $signature = '';
830 daniel-mar 215
                        if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
635 daniel-mar 216
                                if (!OIDplus::getPkiStatus()) {
217
                                        throw new OIDplusException(_L('Error: Your system could not generate a private/public key pair. (OpenSSL is probably missing on your system). Therefore, you cannot register/unregister your OIDplus instance.'));
218
                                } else {
219
                                        throw new OIDplusException(_L('Signature failed'));
220
                                }
221
                        }
222
 
223
                        $data = array(
224
                                "payload" => $payload,
225
                                "signature" => base64_encode($signature)
226
                        );
227
 
228
                        if (!function_exists('curl_init')) {
229
                                throw new OIDplusException(_L('The "%1" PHP extension is not installed at your system. Please enable the PHP extension <code>%2</code>.','CURL','php_curl'));
230
                        }
231
 
699 daniel-mar 232
                        if (OIDplus::getEditionInfo()['vendor'] != 'ViaThinkSoft') {
233
                                // The oid-info.com import functionality is a confidential API between ViaThinkSoft and oid-info.com and cannot be used in forks of OIDplus
234
                                throw new OIDplusException(_L('This feature is only available in the ViaThinkSoft edition of OIDplus'));
235
                        }
236
 
635 daniel-mar 237
                        $ch = curl_init();
238
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
239
                        curl_setopt($ch, CURLOPT_URL, 'https://oidplus.viathinksoft.com/reg2/query.php');
715 daniel-mar 240
                        curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
635 daniel-mar 241
                        curl_setopt($ch, CURLOPT_POST, 1);
242
                        if (function_exists('gzdeflate')) {
243
                                $compressed = "1";
244
                                $data2 = gzdeflate(json_encode($data));
245
                        } else {
246
                                $compressed = "0";
247
                                $data2 = json_encode($data);
248
                        }
249
                        curl_setopt($ch, CURLOPT_POSTFIELDS, "query=".urlencode($query)."&compressed=$compressed&data=".urlencode(base64_encode($data2)));
250
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
251
                        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
252
                        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
977 daniel-mar 253
                        if (!($res_curl = @curl_exec($ch))) {
635 daniel-mar 254
                                throw new OIDplusException(_L('Communication with ViaThinkSoft server failed: %1',curl_error($ch)));
255
                        }
256
                        curl_close($ch);
257
 
258
                        $out['text'] = '<p><a '.OIDplus::gui()->link('oidplus:datatransfer$export').'><img src="img/arrow_back.png" width="16" alt="'._L('Go back').'"> '._L('Go back to data transfer main page').'</a></p>';
259
 
977 daniel-mar 260
                        $json = @json_decode($res_curl, true);
635 daniel-mar 261
 
262
                        if (!$json) {
800 daniel-mar 263
                                $out['icon'] = 'img/error.png';
977 daniel-mar 264
                                $out['text'] .= _L('JSON reply from ViaThinkSoft decoding error: %1',$res_curl);
635 daniel-mar 265
                                return;
266
                        }
267
 
268
                        if (isset($json['error']) || ($json['status'] < 0)) {
800 daniel-mar 269
                                $out['icon'] = 'img/error.png';
635 daniel-mar 270
                                if (isset($json['error'])) {
271
                                        $out['text'] .= _L('Received error: %1',$json['error']);
272
                                } else {
273
                                        $out['text'] .= _L('Received error status code: %1',$json['status']);
274
                                }
275
                                return;
276
                        }
277
 
278
                        if (isset($json['error']) || ($json['status'] < 0)) {
279
                                $out['text'] .= '<p>'._L('Error: %1',htmlentities($json['error'])).'</p>';
280
                        } else {
281
                                // TODO: If roots were created or deleted recently, we must do a re-query of the registration, so that the "roots" information at the directory service gets refreshed
282
                                if (count($json['roots']) == 0) $out['text'] .= '<p>'._L('In order to use this feature, you need to have at least one (root) OID added in your system, and the system needs to report the newly added root to the directory service (the reporting interval is 1 hour).').'</p>';
283
                                foreach ($json['roots'] as $root) {
284
                                        $oid = $root['oid'];
285
                                        $out['text'] .= '<h2>'._L('Root OID %1',$oid).'</h2>';
286
                                        if ($root['verified']) {
287
                                                $count = 0;
288
                                                $out['text'] .= '<div class="container box"><div id="suboid_table" class="table-responsive">';
289
                                                $out['text'] .= '<table class="table table-bordered table-striped">';
290
                                                $out['text'] .= '<tr><th colspan="3">'._L('Actions').'</th><th>'._L('OID').'</th></tr>';
291
 
292
                                                $lookup_nonoid = array();
293
                                                $row_lookup = array();
294
 
295
                                                $all_local_oids_of_root = array();
296
                                                $res = OIDplus::db()->query("select * from ###objects where confidential <> 1");
297
                                                while ($row = $res->fetch_object()) {
298
                                                        $obj = OIDplusObject::parse($row->id);
299
                                                        if (!$obj) continue; // can happen when object type is not enabled
300
                                                        if ($obj->isConfidential()) continue; // This will also exclude OIDs which are descendants of confidential OIDs
301
                                                        if (strpos($row->id, 'oid:') === 0) {
302
                                                                $oid = substr($row->id,strlen('oid:'));
303
                                                                if (strpos($oid.'.', $root['oid']) === 0) {
304
                                                                        $row_lookup[$oid] = $row;
305
                                                                        $all_local_oids_of_root[] = $oid;
306
                                                                }
307
                                                        } else {
308
                                                                $aids = $obj->getAltIds();
309
                                                                foreach ($aids as $aid) {
1052 daniel-mar 310
                                                                        // TODO: Let Object Type plugins decide if they want that their OID representations get published or not (via a Feature OID implementation)
635 daniel-mar 311
                                                                        if ($aid->getNamespace() == 'oid') {
312
                                                                                $oid = $aid->getId();
313
                                                                                if (strpos($oid.'.', $root['oid']) === 0) {
314
                                                                                        $row_lookup[$oid] = $row;
315
                                                                                        $all_local_oids_of_root[] = $oid;
316
                                                                                        $lookup_nonoid[$oid] = $row->id;
317
                                                                                }
318
                                                                        }
319
                                                                }
320
                                                        }
321
                                                }
322
 
323
                                                natsort($all_local_oids_of_root);
324
                                                foreach ($all_local_oids_of_root as $local_oid) {
325
                                                        if (!in_array($local_oid, $root['children'])) {
326
                                                                $count++;
327
 
328
                                                                // Start: Build oid-info.com create URL
329
 
330
                                                                $row = $row_lookup[$local_oid];
331
 
1052 daniel-mar 332
                                                                $url = "https://oid-rep.orange-labs.fr/cgi-bin/manage?f=".oid_up($local_oid)."&a=create";
635 daniel-mar 333
 
334
                                                                $tmp = explode('.',$local_oid);
335
                                                                $url .= "&nb=".urlencode(array_pop($tmp));
336
 
337
                                                                $asn1_ids = array();
977 daniel-mar 338
                                                                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($row->id));
339
                                                                while ($row_asn = $res_asn->fetch_object()) {
340
                                                                        $asn1_ids[] = $row_asn->name; // 'unicode-label' is currently not in the standard format (oid.xsd)
635 daniel-mar 341
                                                                }
342
                                                                $url .= "&id=".array_shift($asn1_ids); // urlencode() is already done (see above)
343
                                                                $url .= "&syn_id=".implode('%0A', $asn1_ids); // urlencode() is already done (see above)
344
 
345
                                                                $iri_ids = array();
977 daniel-mar 346
                                                                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ?", array($row->id));
347
                                                                while ($row_iri = $res_iri->fetch_object()) {
348
                                                                        $iri_ids[] = $row_iri->name;
635 daniel-mar 349
                                                                }
350
                                                                $url .= "&unicode_label_list=".implode('%0A', $iri_ids); // urlencode() is already done (see above)
351
 
352
                                                                if (!empty($row->title)) {
353
                                                                        $tmp_description = $row->title;
354
                                                                        $tmp_information = $row->description;/** @phpstan-ignore-line */
355
                                                                        if (trim($row->title) == trim(strip_tags($row->description))) {/** @phpstan-ignore-line */
356
                                                                                $tmp_information = '';
357
                                                                        }
358
                                                                } else if (isset($asn1_ids[0])) {
359
                                                                        $tmp_description = '"'.$asn1_ids[0].'"';
360
                                                                        $tmp_information = $row->description;
361
                                                                } else if (isset($iri_ids[0])) {
362
                                                                        $tmp_description = '"'.$iri_ids[0].'"';
363
                                                                        $tmp_information = $row->description;
364
                                                                } else if (!empty($row->description)) {
365
                                                                        $tmp_description = $row->description;
366
                                                                        $tmp_information = '';
367
                                                                } else if (!empty($row->comment)) {
368
                                                                        $tmp_description = $row->comment;
369
                                                                        $tmp_information = '';
370
                                                                } else {
371
                                                                        $tmp_description = '<i>No description available</i>'; // do not translate
372
                                                                        $tmp_information = '';
373
                                                                }
374
 
375
                                                                if ($tmp_information != '') {
376
                                                                        $tmp_information .= '<br/><br/>';
377
                                                                }
378
 
801 daniel-mar 379
                                                                $tmp_information .= 'See <a href="'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL).'?goto='.urlencode($id).'">more information</a>.'; // do not translate
635 daniel-mar 380
 
381
                                                                if (explode(':',$id,2)[0] != 'oid') {
382
                                                                        $tmp_information = "Object: $id\n\n" . $tmp_information; // do not translate
383
                                                                }
384
 
385
                                                                $url .= "&description=".urlencode(self::repair_relative_links($tmp_description));
386
                                                                $url .= "&info=".urlencode(self::repair_relative_links($tmp_information));
387
 
388
                                                                $url .= "&current_registrant_email=".urlencode($row->ra_email);
389
 
977 daniel-mar 390
                                                                $res_ra = OIDplus::db()->query("select * from ###ra where email = ?", array($row->ra_email));
391
                                                                if ($res_ra->any()) {
392
                                                                        $row_ra = $res_ra->fetch_object();
635 daniel-mar 393
 
394
                                                                        $tmp = array();
977 daniel-mar 395
                                                                        if (!empty($row_ra->personal_name)) {
396
                                                                                $name_ary = split_firstname_lastname($row_ra->personal_name);
635 daniel-mar 397
                                                                                $tmp_first_name = $name_ary[0];
398
                                                                                $tmp_last_name  = $name_ary[1];
977 daniel-mar 399
                                                                                if (!empty($row_ra->ra_name)       ) $tmp[] = $row_ra->ra_name;
400
                                                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
401
                                                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 402
                                                                        } else {
977 daniel-mar 403
                                                                                $tmp_first_name = $row_ra->ra_name;
635 daniel-mar 404
                                                                                $tmp_last_name  = '';
977 daniel-mar 405
                                                                                if (!empty($row_ra->personal_name) ) $tmp[] = $row_ra->personal_name;
406
                                                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
407
                                                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 408
                                                                        }
409
 
410
                                                                        if (empty($tmp_first_name) || empty($tmp_last_name)) {
411
                                                                                $name = self::split_name($tmp_first_name.' '.$tmp_last_name);
412
                                                                                $tmp_first_name = $name[0];
413
                                                                                $tmp_last_name = $name[1];
414
                                                                        }
415
                                                                        $url .= "&current_registrant_first_name=".urlencode($tmp_first_name);
416
                                                                        $url .= "&current_registrant_last_name=".urlencode($tmp_last_name);
417
 
977 daniel-mar 418
                                                                        if ((count($tmp) > 0) && ($tmp[0] == $row_ra->ra_name)) array_shift($tmp);
635 daniel-mar 419
                                                                        $tmp = array_unique($tmp);
420
 
977 daniel-mar 421
                                                                        if (!$row_ra->privacy) {
422
                                                                                if (!empty($row_ra->street))   $tmp[] = $row_ra->street;
423
                                                                                if (!empty($row_ra->zip_town)) $tmp[] = $row_ra->zip_town;
424
                                                                                if (!empty($row_ra->country))  $tmp[] = $row_ra->country;
425
                                                                                $url .= "&current_registrant_tel=".urlencode(!empty($row_ra->phone) ? $row_ra->phone : $row_ra->mobile);
426
                                                                                $url .= "&current_registrant_fax=".urlencode($row_ra->fax);
635 daniel-mar 427
                                                                        }
977 daniel-mar 428
                                                                        if (empty($row_ra->zip_town) && empty($row_ra->country)) {
635 daniel-mar 429
                                                                                // The address is useless if we do neither know city nor country
430
                                                                                // Ignore it
431
                                                                        } else {
432
                                                                                $tmp = self::split_address_country(implode("<br/>", $tmp));
433
                                                                                $url .= "&current_registrant_address=".urlencode($tmp[0]);
434
                                                                                $url .= "&current_registrant_country=".urlencode($tmp[1]);
435
                                                                        }
436
                                                                }
437
                                                                if (!empty($row->updated)) {
438
                                                                        $tmp = explode('-', self::_formatdate($row->updated));
439
                                                                        $url .= "&modification_year=".urlencode($tmp[0]);
440
                                                                        $url .= "&modification_month=".urlencode($tmp[1]);
441
                                                                        $url .= "&modification_day=".urlencode($tmp[2]);
442
                                                                }
443
 
444
                                                                //$url .= "&submitter_last_name=".urlencode($xml->{'submitter'}->{'last-name'});
445
                                                                //$url .= "&submitter_first_name=".urlencode($xml->{'submitter'}->{'first-name'});
446
                                                                //$url .= "&submitter_email=".urlencode($xml->{'submitter'}->{'email'});
447
 
448
                                                                // End: Build oid-info.com create URL
449
 
450
                                                                // Note: "Actions" is at the left, because it has a fixed width, so the user can continue clicking without the links moving if the OID length changes between lines
451
                                                                $out['text'] .= '<tr id="missing_oid_'.str_replace('.','_',$local_oid).'">'.
1130 daniel-mar 452
                                                                '<td><a '.OIDplus::gui()->link($lookup_nonoid[$local_oid] ?? 'oid:' . $local_oid, true).'>'._L('View local OID').'</a></td>'.
635 daniel-mar 453
                                                                '<td><a href="javascript:OIDplusPageAdminOIDInfoExport.removeMissingOid(\''.$local_oid.'\');">'._L('Ignore for now').'</a></td>'.
454
                                                                '<td><a target="_blank" href="'.$url.'">'._L('Add to oid-info.com manually').'</a></td>'.
455
                                                                '<td>'.$local_oid.'</td>'.
456
                                                                '</tr>';
457
                                                        }
458
                                                }
459
                                                if ($count == 0) {
460
                                                        $out['text'] .= '<tr><td colspan="4">'._L('No missing OIDs found').'</td></tr>';
461
                                                }
462
                                                $out['text'] .= '</table></div></div>';
463
                                        } else {
464
                                                $out['text'] .= '<p>'._L('This root is not validated. Please send an email to %1 in order to request ownership verification of this root OID.',$json['vts_verification_email']).'</p>';
465
                                        }
466
                                }
467
                        }
468
                }
469
 
470
                if ($id === 'oidplus:oidinfo_compare_import') {
471
                        $handled = true;
472
                        $out['title'] = _L('List OIDs at oid-info.com which are missing in your system');
801 daniel-mar 473
                        $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
635 daniel-mar 474
 
475
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
800 daniel-mar 476
                                $out['icon'] = 'img/error.png';
635 daniel-mar 477
                                $out['text'] = '<p>'._L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')).'</p>';
478
                                return;
479
                        }
480
 
481
                        $query = self::QUERY_LIST_OIDINFO_OIDS_V1;
482
 
483
                        $payload = array(
484
                                "query" => $query, // we must repeat the query because we want to sign it
485
                                "system_id" => OIDplus::getSystemId(false),
486
                                "show_all" => 0
487
                        );
488
 
489
                        $signature = '';
830 daniel-mar 490
                        if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
635 daniel-mar 491
                                if (!OIDplus::getPkiStatus()) {
492
                                        throw new OIDplusException(_L('Error: Your system could not generate a private/public key pair. (OpenSSL is probably missing on your system). Therefore, you cannot register/unregister your OIDplus instance.'));
493
                                } else {
494
                                        throw new OIDplusException(_L('Signature failed'));
495
                                }
496
                        }
497
 
498
                        $data = array(
499
                                "payload" => $payload,
500
                                "signature" => base64_encode($signature)
501
                        );
502
 
503
                        if (!function_exists('curl_init')) {
504
                                throw new OIDplusException(_L('The "%1" PHP extension is not installed at your system. Please enable the PHP extension <code>%2</code>.','CURL','php_curl'));
505
                        }
506
 
699 daniel-mar 507
                        if (OIDplus::getEditionInfo()['vendor'] != 'ViaThinkSoft') {
508
                                // The oid-info.com import functionality is a confidential API between ViaThinkSoft and oid-info.com and cannot be used in forks of OIDplus
509
                                throw new OIDplusException(_L('This feature is only available in the ViaThinkSoft edition of OIDplus'));
510
                        }
511
 
635 daniel-mar 512
                        $ch = curl_init();
513
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
514
                        curl_setopt($ch, CURLOPT_URL, 'https://oidplus.viathinksoft.com/reg2/query.php');
715 daniel-mar 515
                        curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
635 daniel-mar 516
                        curl_setopt($ch, CURLOPT_POST, 1);
517
                        if (function_exists('gzdeflate')) {
518
                                $compressed = "1";
519
                                $data2 = gzdeflate(json_encode($data));
520
                        } else {
521
                                $compressed = "0";
522
                                $data2 = json_encode($data);
523
                        }
524
                        curl_setopt($ch, CURLOPT_POSTFIELDS, "query=".urlencode($query)."&compressed=$compressed&data=".urlencode(base64_encode($data2)));
525
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
526
                        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
527
                        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
528
                        if (!($res = @curl_exec($ch))) {
529
                                throw new OIDplusException(_L('Communication with ViaThinkSoft server failed: %1',curl_error($ch)));
530
                        }
531
                        curl_close($ch);
532
 
533
                        $out['text'] = '<p><a '.OIDplus::gui()->link('oidplus:datatransfer$import').'><img src="img/arrow_back.png" width="16" alt="'._L('Go back').'"> '._L('Go back to data transfer main page').'</a></p>';
534
 
535
                        $json = @json_decode($res, true);
536
 
537
                        if (!$json) {
800 daniel-mar 538
                                $out['icon'] = 'img/error.png';
635 daniel-mar 539
                                $out['text'] .= _L('JSON reply from ViaThinkSoft decoding error: %1',$res);
540
                                return;
541
                        }
542
 
543
                        if (isset($json['error']) || ($json['status'] < 0)) {
800 daniel-mar 544
                                $out['icon'] = 'img/error.png';
635 daniel-mar 545
                                if (isset($json['error'])) {
546
                                        $out['text'] .= _L('Received error: %1',$json['error']);
547
                                } else {
548
                                        $out['text'] .= _L('Received error status code: %1',$json['status']);
549
                                }
550
                                return;
551
                        }
552
 
553
                        $all_local_oids = array();
554
                        $res = OIDplus::db()->query("select id from ###objects");
555
                        while ($row = $res->fetch_array()) {
556
                                if (strpos($row['id'], 'oid:') === 0) {
557
                                        $all_local_oids[] = substr($row['id'],strlen('oid:'));
558
                                } else {
559
                                        $obj = OIDplusObject::parse($row['id']);
560
                                        if (!$obj) continue; // can happen when object type is not enabled
561
                                        $aids = $obj->getAltIds();
562
                                        foreach ($aids as $aid) {
1052 daniel-mar 563
                                                // TODO: Let Object Type plugins decide if they want that their OID representations get published or not (via a Feature OID implementation)
635 daniel-mar 564
                                                if ($aid->getNamespace() == 'oid') {
565
                                                        $all_local_oids[] = $aid->getId();
566
                                                }
567
                                        }
568
                                }
569
                        }
570
 
571
                        if (isset($json['error']) || ($json['status'] < 0)) {
572
                                $out['text'] .= '<p>'._L('Error: %1',htmlentities($json['error'])).'</p>';
573
                        } else {
574
                                // TODO: If roots were created or deleted recently, we must do a re-query of the registration, so that the "roots" information at the directory service gets refreshed
575
                                if (count($json['roots']) == 0) $out['text'] .= '<p>'._L('In order to use this feature, you need to have at least one (root) OID added in your system, and the system needs to report the newly added root to the directory service (the reporting interval is 1 hour).').'</p>';
576
                                foreach ($json['roots'] as $root) {
577
                                        $oid = $root['oid'];
578
                                        $out['text'] .= '<h2>'._L('Root OID %1',$oid).'</h2>';
579
                                        // TODO: "Import all" button
580
                                        if ($root['verified']) {
581
                                                $count = 0;
582
                                                $out['text'] .= '<div class="container box"><div id="suboid_table" class="table-responsive">';
583
                                                $out['text'] .= '<table class="table table-bordered table-striped">';
584
                                                $out['text'] .= '<tr><th colspan="4">'._L('Actions').'</th><th>'._L('OID').'</th></tr>';
585
                                                natsort($root['children']);
586
                                                foreach ($root['children'] as $child_oid) {
587
                                                        if (!in_array($child_oid, $all_local_oids)) {
588
                                                                $count++;
589
                                                                // Note: "Actions" is at the left, because it has a fixed width, so the user can continue clicking without the links moving if the OID length changes between lines
590
                                                                $out['text'] .= '<tr id="missing_oid_'.str_replace('.','_',$child_oid).'">'.
1052 daniel-mar 591
                                                                '<td><a target="_blank" href="https://oid-rep.orange-labs.fr/get/'.$child_oid.'">'._L('View OID at oid-info.com').'</a></td>'.
635 daniel-mar 592
                                                                '<td><a href="javascript:OIDplusPageAdminOIDInfoExport.removeMissingOid(\''.$child_oid.'\');">'._L('Ignore for now').'</a></td>'.
593
                                                                '<td><a href="mailto:admin@oid-info.com">'._L('Report illegal OID').'</a></td>'.
594
                                                                (strpos($child_oid,'1.3.6.1.4.1.37476.30.9.') === 0 ? '<td>&nbsp;</td>' : '<td><a href="javascript:OIDplusPageAdminOIDInfoExport.importMissingOid(\''.$child_oid.'\');">'._L('Import OID').'</a></td>').
595
                                                                '<td>'.$child_oid.'</td>'.
596
                                                                '</tr>';
597
                                                        }
598
                                                }
599
                                                if ($count == 0) {
600
                                                        $out['text'] .= '<tr><td colspan="5">'._L('No extra OIDs found').'</td></tr>';
601
                                                }
602
                                                $out['text'] .= '</table></div></div>';
603
                                        } else {
604
                                                $out['text'] .= '<p>'._L('This root is not validated. Please send an email to %1 in order to request ownership verification of this root OID.',$json['vts_verification_email']).'</p>';
605
                                        }
606
                                }
607
                        }
608
                }
609
 
610
                if ($id === 'oidplus:datatransfer') {
611
                        $handled = true;
612
                        $out['title'] = _L('Data Transfer');
801 daniel-mar 613
                        $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
635 daniel-mar 614
 
615
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
800 daniel-mar 616
                                $out['icon'] = 'img/error.png';
635 daniel-mar 617
                                $out['text'] = '<p>'._L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')).'</p>';
618
                                return;
619
                        }
620
 
890 daniel-mar 621
                        $out['text'] = '<noscript>';
622
                        $out['text'] .= '<p>'._L('You need to enable JavaScript to use the login area.').'</p>';
623
                        $out['text'] .= '</noscript>';
635 daniel-mar 624
 
890 daniel-mar 625
                        $out['text'] .= '<br><div id="dataTransferArea" style="visibility: hidden"><div id="dataTransferTab" class="container" style="width:100%;">';
626
 
635 daniel-mar 627
                        // ---------------- Tab control
628
                        $out['text'] .= OIDplus::gui()->tabBarStart();
629
                        $out['text'] .= OIDplus::gui()->tabBarElement('export', _L('Export'), $tab === 'export');
630
                        $out['text'] .= OIDplus::gui()->tabBarElement('import', _L('Import'), $tab === 'import');
631
                        $out['text'] .= OIDplus::gui()->tabBarEnd();
632
                        $out['text'] .= OIDplus::gui()->tabContentStart();
633
                        // ---------------- "Export" tab
634
                        $tabcont  = '<h2>'._L('Generate XML file containing all OIDs').'</h2>';
1052 daniel-mar 635
                        $tabcont .= '<p>'._L('These XML files are following the <a %1>XML schema</a> of <b>oid-info.com</b>. They can be used for various purposes though.','href="https://oid-rep.orange-labs.fr/oid.xsd" target="_blank"').'</p>';
801 daniel-mar 636
                        $tabcont .= '<p><input type="button" onclick="window.open(\''.OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'oidinfo_export.php\',\'_blank\')" value="'._L('Generate XML (all OIDs)').'"></p>';
637
                        $tabcont .= '<p><input type="button" onclick="window.open(\''.OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'oidinfo_export.php?online=1\',\'_blank\')" value="'._L('Generate XML (only OIDs which do not exist at oid-info.com)').'"></p>';
1052 daniel-mar 638
                        $tabcont .= '<p><a href="https://oid-rep.orange-labs.fr/submit.htm" target="_blank">'._L('Upload XML files manually to oid-info.com').'</a></p>';
635 daniel-mar 639
                        $tabcont .= '<br><p>'._L('Attention: Do not use this XML Export/Import to exchange, backup or restore data between OIDplus systems!<br>It will cause various loss of information, e.g. because Non-OIDs like GUIDs are converted in OIDs and can\'t be converted back.').'</p>';
640
                        $tabcont .= '<h2>'._L('Automatic export to oid-info.com').'</h2>';
641
                        $privacy_level = OIDplus::config()->getValue('reg_privacy');
642
                        if ($privacy_level == 0) {
643
                                $tabcont .= '<p>'._L('All your OIDs will automatically submitted to oid-info.com through the remote directory service in regular intervals.').' (<a '.OIDplus::gui()->link('oidplus:srv_registration').'>'._L('Change preference').'</a>)</p>';
644
                        } else {
645
                                $tabcont .= '<p>'._L('If you set the privacy option to "0" (your system is registered), then all your OIDs will be automatically exported to oid-info.com.').' (<a '.OIDplus::gui()->link('oidplus:srv_registration').'>'._L('Change preference').'</a>)</p>';
646
                        }
647
                        $tabcont .= '<h2>'._L('Comparison with oid-info.com').'</h2>';
648
                        $tabcont .= '<p><a '.OIDplus::gui()->link('oidplus:oidinfo_compare_export').'>'._L('List OIDs in your system which are missing at oid-info.com').'</a></p>';
649
                        $out['text'] .= OIDplus::gui()->tabContentPage('export', $tabcont, $tab === 'export');
650
                        // ---------------- "Import" tab
651
                        $tabcont  = '<h2>'._L('Import XML file').'</h2>';
1052 daniel-mar 652
                        $tabcont .= '<p>'._L('These XML files are following the <a %1>XML schema</a> of <b>oid-info.com</b>.','href="https://oid-rep.orange-labs.fr/oid.xsd" target="_blank"').'</p>';
635 daniel-mar 653
                        // TODO: we need a waiting animation!
654
                        $tabcont .= '<form action="javascript:void(0);" onsubmit="return OIDplusPageAdminOIDInfoExport.uploadXmlFileOnSubmit(this);" enctype="multipart/form-data" id="uploadXmlFileForm">';
655
                        $tabcont .= '<div>'._L('Choose XML file here').':<input type="file" name="userfile" value="" id="userfile">';
656
                        $tabcont .= '<br><input type="submit" value="'._L('Import XML').'"></div>';
657
                        $tabcont .= '</form>';
658
                        $tabcont .= '<br><p>'._L('Attention: Do not use this XML Export/Import to exchange, backup or restore data between OIDplus systems!<br>It will cause various loss of information, e.g. because Non-OIDs like GUIDs are converted in OIDs and can\'t be converted back.').'</p>';
659
                        $tabcont .= '<h2>'._L('Comparison with oid-info.com').'</h2>';
660
                        $tabcont .= '<p><a '.OIDplus::gui()->link('oidplus:oidinfo_compare_import').'>'._L('List OIDs at oid-info.com which are missing in your system').'</a></p>';
661
                        $out['text'] .= OIDplus::gui()->tabContentPage('import', $tabcont, $tab === 'import');
662
                        $out['text'] .= OIDplus::gui()->tabContentEnd();
663
                        // ---------------- Tab control END
664
 
665
                        $out['text'] .= '</div></div><script>$("#dataTransferArea")[0].style.visibility = "visible";</script>';
666
                }
667
        }
668
 
1116 daniel-mar 669
        /**
670
         * @param array $json
671
         * @param string|null $ra_email
672
         * @param bool $nonjs
673
         * @param string $req_goto
674
         * @return bool
675
         * @throws OIDplusException
676
         */
677
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 678
                if (!OIDplus::authUtils()->isAdminLoggedIn()) return false;
679
 
800 daniel-mar 680
                if (file_exists(__DIR__.'/img/main_icon16.png')) {
801 daniel-mar 681
                        $tree_icon = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png';
635 daniel-mar 682
                } else {
683
                        $tree_icon = null; // default icon (folder)
684
                }
685
 
686
                $json[] = array(
687
                        'id' => 'oidplus:datatransfer',
688
                        'icon' => $tree_icon,
689
                        'text' => _L('Data Transfer')
690
                );
691
 
692
                return true;
693
        }
694
 
1116 daniel-mar 695
        /**
696
         * @param string $request
697
         * @return array|false
698
         */
699
        public function tree_search(string $request) {
635 daniel-mar 700
                return false;
701
        }
702
 
1116 daniel-mar 703
        /**
1130 daniel-mar 704
         * @param bool $only_non_existing
1116 daniel-mar 705
         * @return string[]
706
         * @throws OIDplusException
707
         * @throws \OIDInfoException
708
         */
1130 daniel-mar 709
        public static function outputXML(bool $only_non_existing): array {
940 daniel-mar 710
                $out_type = null;
711
                $out_content = '';
712
 
1050 daniel-mar 713
                // This file contains class \OIDInfoAPI.
635 daniel-mar 714
                // We cannot include this in init(), because the init
715
                // of the registration plugin (OIDplusPageAdminRegistration) uses
716
                // OIDplusPageAdminOIDInfoExport::outputXML() before
717
                // OIDplusPageAdminOIDInfoExport::init() ,
718
                // because OIDplusPageAdminRegistration::init() comes first sometimes.
719
                require_once __DIR__ . '/oidinfo_api.inc.php';
720
 
1050 daniel-mar 721
                $oa = new \OIDInfoAPI();
724 daniel-mar 722
                if ($only_non_existing) {
723
                        if (!function_exists('socket_create')) {
724
                                throw new OIDplusException(_L('You must install the PHP "sockets" in order to check for non-existing OIDs.'));
725
                        }
726
                        $oa->addSimplePingProvider('viathinksoft.de:49500');
727
                }
635 daniel-mar 728
 
729
                $email = OIDplus::config()->getValue('admin_email');
730
                if (empty($email)) $email = 'unknown@example.com';
731
 
724 daniel-mar 732
                $sys_title = OIDplus::config()->getValue('system_title');
733
                $name1 = !empty($sys_title) ? $sys_title : 'OIDplus 2.0';
1130 daniel-mar 734
                $name2 = $_SERVER['SERVER_NAME'] ?? 'Export interface';
940 daniel-mar 735
                $out_content .= $oa->xmlAddHeader($name1, $name2, $email); // do not translate
635 daniel-mar 736
 
737
                $params['allow_html'] = true;
738
                $params['allow_illegal_email'] = true; // It should be enabled, because the creator could have used some kind of human-readable anti-spam technique
1050 daniel-mar 739
                $params['soft_correct_behavior'] = \OIDInfoAPI::SOFT_CORRECT_BEHAVIOR_NONE;
635 daniel-mar 740
                $params['do_online_check'] = false; // Flag to disable this online check, because it generates a lot of traffic and runtime.
741
                $params['do_illegality_check'] = true;
742
                $params['do_simpleping_check'] = $only_non_existing;
743
                $params['auto_extract_name'] = '';
744
                $params['auto_extract_url'] = '';
745
                $params['always_output_comment'] = false;
746
                $params['creation_allowed_check'] = $only_non_existing;
747
                $params['tolerant_htmlentities'] = true;
748
                $params['ignore_xhtml_light'] = false;
749
 
750
                $nonConfidential = OIDplusObject::getAllNonConfidential();
751
                natsort($nonConfidential);
752
 
753
                foreach ($nonConfidential as $id) {
977 daniel-mar 754
                        $obj = OIDplusObject::parse($id);
755
                        if ($obj) {
635 daniel-mar 756
                                $elements['identifier'] = array();
977 daniel-mar 757
                                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($id));
758
                                while ($row_asn = $res_asn->fetch_object()) {
759
                                        $elements['identifier'][] = $row_asn->name; // 'unicode-label' is currently not in the standard format (oid.xsd)
635 daniel-mar 760
                                }
761
 
762
                                $elements['unicode-label'] = array();
977 daniel-mar 763
                                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ?", array($id));
764
                                while ($row_iri = $res_iri->fetch_object()) {
765
                                        $elements['unicode-label'][] = $row_iri->name;
635 daniel-mar 766
                                }
767
 
977 daniel-mar 768
                                $title = $obj->getTitle();
769
                                $description = $obj->getDescription();
770
                                $comment = $obj->getComment();
771
                                if (!empty($title)) {
772
                                        $elements['description'] = $title;
773
                                        $elements['information'] = $description;
774
                                        if (trim($title) == trim(strip_tags($description))) {
635 daniel-mar 775
                                                $elements['information'] = '';
776
                                        }
777
                                } else if (isset($elements['identifier'][0])) {
778
                                        $elements['description'] = '"'.$elements['identifier'][0].'"';
977 daniel-mar 779
                                        $elements['information'] = $description;
635 daniel-mar 780
                                } else if (isset($elements['unicode-label'][0])) {
781
                                        $elements['description'] = '"'.$elements['unicode-label'][0].'"';
977 daniel-mar 782
                                        $elements['information'] = $description;
783
                                } else if (!empty($description)) {
784
                                        $elements['description'] = $description;
635 daniel-mar 785
                                        $elements['information'] = '';
977 daniel-mar 786
                                } else if (!empty($comment)) {
787
                                        $elements['description'] = $comment;
635 daniel-mar 788
                                        $elements['information'] = '';
789
                                } else {
790
                                        $elements['description'] = '<i>No description available</i>'; // do not translate
791
                                        $elements['information'] = '';
792
                                }
793
 
794
                                if ($elements['information'] != '') {
795
                                        $elements['information'] .= '<br/><br/>';
796
                                }
797
 
801 daniel-mar 798
                                $elements['information'] .= 'See <a href="'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL).'?goto='.urlencode($id).'">more information</a>.'; // do not translate
635 daniel-mar 799
 
800
                                if (explode(':',$id,2)[0] != 'oid') {
801
                                        $elements['information'] = "Object: $id\n\n" . $elements['information']; // do not translate
802
                                }
803
 
804
                                $elements['description'] = self::repair_relative_links($elements['description']);
805
                                $elements['information'] = self::repair_relative_links($elements['information']);
806
 
807
                                $elements['first-registrant']['first-name'] = '';
808
                                $elements['first-registrant']['last-name'] = '';
809
                                $elements['first-registrant']['address'] = '';
810
                                $elements['first-registrant']['email'] = '';
811
                                $elements['first-registrant']['phone'] = '';
812
                                $elements['first-registrant']['fax'] = '';
977 daniel-mar 813
                                $elements['first-registrant']['creation-date'] = self::_formatdate($obj->getCreatedTime());
635 daniel-mar 814
 
815
                                $elements['current-registrant']['first-name'] = '';
816
                                $elements['current-registrant']['last-name'] = '';
977 daniel-mar 817
                                $elements['current-registrant']['email'] = $obj->getRaMail();
635 daniel-mar 818
                                $elements['current-registrant']['phone'] = '';
819
                                $elements['current-registrant']['fax'] = '';
820
                                $elements['current-registrant']['address'] = '';
821
 
977 daniel-mar 822
                                $res_ra = OIDplus::db()->query("select * from ###ra where email = ?", array($obj->getRaMail()));
823
                                if ($res_ra->any()) {
824
                                        $row_ra = $res_ra->fetch_object();
635 daniel-mar 825
 
826
                                        $tmp = array();
977 daniel-mar 827
                                        if (!empty($row_ra->personal_name)) {
828
                                                $name_ary = split_firstname_lastname($row_ra->personal_name);
635 daniel-mar 829
                                                $elements['current-registrant']['first-name'] = $name_ary[0];
830
                                                $elements['current-registrant']['last-name']  = $name_ary[1];
977 daniel-mar 831
                                                if (!empty($row_ra->ra_name)       ) $tmp[] = $row_ra->ra_name;
832
                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
833
                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 834
                                        } else {
977 daniel-mar 835
                                                $elements['current-registrant']['first-name'] = $row_ra->ra_name;
635 daniel-mar 836
                                                $elements['current-registrant']['last-name']  = '';
977 daniel-mar 837
                                                if (!empty($row_ra->personal_name) ) $tmp[] = $row_ra->personal_name;
838
                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
839
                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 840
                                        }
841
 
977 daniel-mar 842
                                        if ((count($tmp) > 0) && ($tmp[0] == $row_ra->ra_name)) array_shift($tmp);
635 daniel-mar 843
                                        $tmp = array_unique($tmp);
844
 
977 daniel-mar 845
                                        if (!$row_ra->privacy) {
846
                                                if (!empty($row_ra->street))   $tmp[] = $row_ra->street;
847
                                                if (!empty($row_ra->zip_town)) $tmp[] = $row_ra->zip_town;
848
                                                if (!empty($row_ra->country))  $tmp[] = $row_ra->country;
849
                                                $elements['current-registrant']['phone'] = !empty($row_ra->phone) ? $row_ra->phone : $row_ra->mobile;
850
                                                $elements['current-registrant']['fax'] = $row_ra->fax;
635 daniel-mar 851
                                        }
977 daniel-mar 852
                                        if (empty($row_ra->zip_town) && empty($row_ra->country)) {
635 daniel-mar 853
                                                // The address is useless if we do neither know city nor country
854
                                                // Ignore it
855
                                                $elements['current-registrant']['address'] = '';
856
                                        } else {
857
                                                $elements['current-registrant']['address'] = implode("<br/>", $tmp);
858
                                        }
859
                                }
977 daniel-mar 860
                                $elements['current-registrant']['modification-date'] = self::_formatdate($obj->getUpdatedTime());
635 daniel-mar 861
 
862
                                // Request from O.D. 20 May 2019: First registrant should not be empty (especially for cases where Creation and Modify Dates are the same)
863
                                // Actually, this is a problem because we don't know the first registrant.
864
                                // However, since oidinfo gets their XML very fast (if using registration), it is likely that the reported RA is still the same...
865
                                // ... and changes at the RA are not reported to oid-info.com anyways - the XML is only for creation
866
 
867
                                $elements['first-registrant']['first-name'] = $elements['current-registrant']['first-name'];
868
                                $elements['first-registrant']['last-name']  = $elements['current-registrant']['last-name'];
869
                                $elements['first-registrant']['address']    = $elements['current-registrant']['address'];
870
                                $elements['first-registrant']['email']      = $elements['current-registrant']['email'];
871
                                $elements['first-registrant']['phone']      = $elements['current-registrant']['phone'];
872
                                $elements['first-registrant']['fax']        = $elements['current-registrant']['fax'];
873
 
874
                                $elements['current-registrant']['first-name'] = '';
875
                                $elements['current-registrant']['last-name'] = '';
876
                                $elements['current-registrant']['address'] = '';
877
                                $elements['current-registrant']['email'] = '';
878
                                $elements['current-registrant']['phone'] = '';
879
                                $elements['current-registrant']['fax'] = '';
880
                                $elements['current-registrant']['modification-date'] = '';
881
 
882
                                // End request O.D. 20 May 2019
883
 
884
                                list($ns,$id) = explode(':',$obj->nodeId());
885
                                if ($ns == 'oid') {
940 daniel-mar 886
                                        $out_content .= $oa->createXMLEntry($id, $elements, $params, $comment=$obj->nodeId());
635 daniel-mar 887
                                } else {
888
                                        $alt_ids = $obj->getAltIds(); // TODO: slow!
889
                                        foreach ($alt_ids as $alt_id) {
890
                                                $ns = $alt_id->getNamespace();
891
                                                $id = $alt_id->getId();
892
                                                $desc = $alt_id->getDescription();
893
                                                if ($ns == 'oid') {
1052 daniel-mar 894
                                                        // TODO: Let Object Type plugins decide if they want that their OID representations get published or not (via a Feature OID implementation)
635 daniel-mar 895
                                                        if (strpos($id, '2.25.') === 0) continue; // don't spam the uuid arc with GUID objects
940 daniel-mar 896
                                                        $out_content .= $oa->createXMLEntry($id, $elements, $params, $comment=$obj->nodeId());
635 daniel-mar 897
                                                }
898
                                        }
899
                                }
900
                        }
901
                }
902
 
940 daniel-mar 903
                $out_content .= $oa->xmlAddFooter();
904
 
905
                $out_type = 'text/xml';
906
                return array($out_content, $out_type);
635 daniel-mar 907
        }
908
 
1116 daniel-mar 909
        /**
1125 daniel-mar 910
         * @param string $str
911
         * @return string
1116 daniel-mar 912
         */
1125 daniel-mar 913
        private static function _formatdate(string $str): string {
635 daniel-mar 914
                $str = explode(' ',$str)[0];
915
                if ($str == '0000-00-00') $str = '';
916
                return $str;
917
        }
918
 
1116 daniel-mar 919
        /**
1130 daniel-mar 920
         * @param string $str
921
         * @return string
1116 daniel-mar 922
         * @throws OIDplusException
923
         */
1130 daniel-mar 924
        private static function repair_relative_links(string $str): string {
925
                return preg_replace_callback('@(href\s*=\s*([\'"]))(.+)(\\2)@ismU', function($treffer) {
635 daniel-mar 926
                        $url = $treffer[3];
927
                        if ((stripos($url,'http:') !== 0) && (stripos($url,'https:') !== 0) && (stripos($url,'ftp:') !== 0)) {
928
                                if (stripos($url,'www.') === 0) {
929
                                        $url .= 'http://' . $url;
930
                                } else {
801 daniel-mar 931
                                        $url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . $url; // TODO: Canonical or not?
635 daniel-mar 932
                                }
933
                        }
934
                        return $treffer[1].$url.$treffer[4];
935
                }, $str);
936
        }
937
 
1116 daniel-mar 938
        /**
1125 daniel-mar 939
         * @param string $address
940
         * @return string[]
1116 daniel-mar 941
         */
1125 daniel-mar 942
        private static function split_address_country(string $address): array {
635 daniel-mar 943
                global $oidinfo_countries;
944
                $ary = explode("\n", $address);
945
                $last_line = array_pop($ary);
946
                $rest = implode("\n", $ary);
947
                if (isset($oidinfo_countries[$last_line])) {
948
                        return array($rest, $oidinfo_countries[$last_line]);
949
                } else {
950
                        return array($rest."\n".$last_line, '');
951
                }
952
        }
953
 
1116 daniel-mar 954
        /**
1130 daniel-mar 955
         * @param string $name
1116 daniel-mar 956
         * @return array
957
         */
1130 daniel-mar 958
        private static function split_name(string $name): array {
635 daniel-mar 959
                // uses regex that accepts any word character or hyphen in last name
960
                // https://stackoverflow.com/questions/13637145/split-text-string-into-first-and-last-name-in-php
961
                $name = trim($name);
962
                $last_name = (strpos($name, ' ') === false) ? '' : preg_replace('#.*\s([\w-]*)$#', '$1', $name);
963
                $first_name = trim( preg_replace('#'.preg_quote($last_name,'#').'#', '', $name ) );
964
                return array($first_name, $last_name);
965
        }
966
 
967
        /*protected*/ const ORPHAN_IGNORE = 0;
968
        /*protected*/ const ORPHAN_AUTO_DEORPHAN = 1;
969
        /*protected*/ const ORPHAN_DISALLOW_ORPHANS = 2;
970
 
1116 daniel-mar 971
        /**
1130 daniel-mar 972
         * @param string $xml_contents
973
         * @param array $errors
974
         * @param bool $replaceExistingOIDs
975
         * @param int $orphan_mode
1125 daniel-mar 976
         * @return int[]
1116 daniel-mar 977
         * @throws OIDplusException
978
         */
1130 daniel-mar 979
        protected function oidinfoImportXML(string $xml_contents, array &$errors, bool $replaceExistingOIDs=false, int $orphan_mode=self::ORPHAN_AUTO_DEORPHAN): array {
635 daniel-mar 980
                // TODO: Implement RA import (let the user decide)
981
                // TODO: Let the user decide about $replaceExistingOIDs
982
                // TODO: Let the user decide if "created=now" should be set (this is good when the XML files is created by the user itself to do bulk-inserts)
983
 
984
                $xml_contents = str_replace('<description>', '<description><![CDATA[', $xml_contents);
985
                $xml_contents = str_replace('</description>', ']]></description>', $xml_contents);
986
 
987
                $xml_contents = str_replace('<information>', '<information><![CDATA[', $xml_contents);
988
                $xml_contents = str_replace('</information>', ']]></information>', $xml_contents);
989
 
990
                $xml = @simplexml_load_string($xml_contents);
991
 
992
                $count_already_existing = 0;
993
                $count_errors = 0;
994
                $count_warnings = 0;
995
 
996
                if (!$xml) {
997
                        $errors[] = _L('Cannot read XML data. The XML file is probably invalid.');
998
                        $count_errors++;
999
                        return array(0, 0, 1, 0);
1000
                }
1001
 
1002
                $ok_oids = array();
1003
 
1004
                foreach ($xml->oid as $xoid) {
1005
 
1006
                        if (isset($xoid->{'dot-notation'})) {
1007
                                $dot_notation = $xoid->{'dot-notation'}->__toString();
1008
                        } else if (isset($xoid->{'asn1-notation'})) {
1009
                                $dot_notation = asn1_to_dot($xoid->{'asn1-notation'}->__toString());
1010
                        } else {
1011
                                $errors[] = _L('Cannot find dot notation because fields asn1-notation and dot-notation are both not existing');
1012
                                $count_errors++;
1013
                                continue;
1014
                        }
1015
 
1016
                        $id = "oid:$dot_notation";
1017
                        $title = isset($xoid->{'description'}) ? $xoid->{'description'}->__toString() : '';
1018
                        $info = isset($xoid->{'description'}) ? $xoid->{'information'}->__toString() : '';
1019
 
991 daniel-mar 1020
                        // For ASN.1 definitions, "Description" is filled with the definition and "Information" is usually empty
1021
                        if (strpos($title,'<br') !== false) {
1022
                                $info = $title . $info;
1023
                                $title = explode(' ',$title)[0];
1024
                        }
1025
 
635 daniel-mar 1026
                        if (isset($xoid->{'current-registrant'}->email)) {
1027
                                $ra = $xoid->{'current-registrant'}->email->__toString();
1028
                        } else if (isset($xoid->{'first-registrant'}->email)) {
1029
                                $ra = $xoid->{'first-registrant'}->email->__toString();
1030
                        } else {
1031
                                $ra = '';
1032
                        }
1033
 
1034
                        if (!oid_valid_dotnotation($dot_notation, false, false)) {
1035
                                $errors[] = _L('Ignored OID %1 because its dot notation is illegal or was not found',$dot_notation);
1036
                                $count_errors++;
1037
                                continue;
1038
                        }
1039
 
1040
                        $parent = 'oid:'.oid_up($dot_notation);
1041
 
1042
                        if ($orphan_mode === self::ORPHAN_DISALLOW_ORPHANS) {
977 daniel-mar 1043
                                if (!OIDplusObject::exists($parent)) {
635 daniel-mar 1044
                                        $errors[] = _L('Cannot import %1, because its parent is not in the database.',$dot_notation);
1045
                                        $count_errors++;
1046
                                        continue;
1047
                                }
1048
                        }
1049
 
978 daniel-mar 1050
                        OIDplus::db()->transaction_begin();
1051
 
977 daniel-mar 1052
                        $obj_test = OIDplusObject::findFitting($id);
1053
                        if ($obj_test) {
635 daniel-mar 1054
                                if ($replaceExistingOIDs) {
978 daniel-mar 1055
                                        OIDplus::db()->query("delete from ###objects where id = ?", array($id));
635 daniel-mar 1056
                                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array($id));
1057
                                        OIDplus::db()->query("delete from ###iri where oid = ?", array($id));
1058
                                } else {
1059
                                        //$errors[] = "Ignore OID '$dot_notation' because it already exists";
1060
                                        //$count_errors++;
1061
                                        $count_already_existing++;
1062
                                        continue;
1063
                                }
1064
                        }
1065
 
991 daniel-mar 1066
                        // TODO: we can probably get the created and modified timestamp from oid-info.com XML
635 daniel-mar 1067
                        OIDplus::db()->query("insert into ###objects (id, parent, title, description, confidential, ra_email) values (?, ?, ?, ?, ?, ?)", array($id, $parent, $title, $info, 0, $ra));
1068
 
978 daniel-mar 1069
                        OIDplus::db()->transaction_commit();
1070
                        OIDplusObject::resetObjectInformationCache();
1071
 
635 daniel-mar 1072
                        $this_oid_has_warnings = false;
1073
 
1074
                        // ---------------------------------------------------------------------
1075
 
1076
                        $asn1ids = array();
1077
                        if (isset($xoid->{'identifier'})) {
1078
                                $asn1ids[] = $xoid->{'identifier'}->__toString();
1079
                        }
1080
                        if (isset($xoid->{'asn1-notation'})) {
1081
                                $last_id = asn1_last_identifier($xoid->{'asn1-notation'}->__toString());
1082
                                if ($last_id) {
1083
                                        $asn1ids[] = $last_id;
1084
                                }
1085
                        }
1086
                        if (isset($xoid->{'synonymous-identifier'})) {
1087
                                foreach ($xoid->{'synonymous-identifier'} as $synid) {
1088
                                        $asn1ids[] = $synid->__toString();
1089
                                }
1090
                        }
1091
                        $asn1ids = array_unique($asn1ids);
1092
                        foreach ($asn1ids as $asn1id) {
1093
                                if (!oid_id_is_valid($asn1id)) {
1094
                                        $errors[] = _L('Warning').' ['._L('OID %1',$dot_notation).']: '._L('Ignored alphanumeric identifier %1, because it is invalid',$asn1id);
1095
                                        $this_oid_has_warnings = true;
1096
                                } else {
1097
                                        OIDplus::db()->query("delete from ###asn1id where oid = ? and name = ?", array($id, $asn1id));
1098
                                        OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array($id, $asn1id));
1099
                                }
1100
                        }
1101
 
1102
                        // ---------------------------------------------------------------------
1103
 
1104
                        if (isset($xoid->{'unicode-label'})) {
1105
                                $iris = array();
1106
                                foreach ($xoid->{'unicode-label'} as $iri) {
1107
                                        $iris[] = $iri->__toString();
1108
                                }
1109
                                $iris = array_unique($iris);
1110
                                foreach ($iris as $iri) {
1111
                                        if (!iri_arc_valid($iri, false)) {
1112
                                                $errors[] = _L('Warning').' ['._L('OID %1',$dot_notation).']: '._L('Ignored Unicode label %1, because it is invalid',$iri);
1113
                                                $this_oid_has_warnings = true;
1114
                                        } else {
1115
                                                OIDplus::db()->query("delete from ###iri where oid = ? and name = ?", array($id, $iri));
1116
                                                OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array($id, $iri));
1117
                                        }
1118
                                }
1119
                        }
1120
 
1121
                        if ($this_oid_has_warnings) $count_warnings++;
1122
                        $ok_oids[] = $id;
1123
                }
1124
 
1125
                // De-orphanize
978 daniel-mar 1126
                //if ($orphan_mode === self::ORPHAN_AUTO_DEORPHAN) {
1127
                //      OIDplus::db()->query("update ###objects set parent = 'oid:' where parent like 'oid:%' and parent not in (select id from ###objects)");
1128
                //      OIDplusObject::resetObjectInformationCache();
1129
                //}
635 daniel-mar 1130
                foreach ($ok_oids as $id) {
1131
                        // De-orphanize if neccessary
1132
                        if ($orphan_mode === self::ORPHAN_AUTO_DEORPHAN) {
1133
                                $res = OIDplus::db()->query("select * from ###objects where id = ? and parent not in (select id from ###objects)", array($id));
790 daniel-mar 1134
                                if ($res->any()) {
635 daniel-mar 1135
                                        $errors[] = _L("%1 was de-orphaned (placed as root OID) because its parent is not existing.",$id);
1136
                                        $count_warnings++;
1137
                                        OIDplus::db()->query("update ###objects set parent = 'oid:' where id = ? and parent not in (select id from ###objects)", array($id));
978 daniel-mar 1138
                                        OIDplusObject::resetObjectInformationCache();
635 daniel-mar 1139
                                }
1140
                        }
1141
 
1142
                        // We do the logging at the end, otherwise SUPOIDRA() might not work correctly if the OIDs were not imported in order or if there were orphans
1143
                        OIDplus::logger()->log("[INFO]OID($id)+[INFO]SUPOID($id)+[INFO]SUPOIDRA($id)!/[INFO]A!", "Object '$id' was automatically created by the XML import tool");
1144
                }
1145
 
1146
                $count_imported_oids = count($ok_oids);
1147
 
1148
                return array($count_imported_oids, $count_already_existing, $count_errors, $count_warnings);
1149
 
1150
        }
1151
 
1116 daniel-mar 1152
        /**
1131 daniel-mar 1153
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
1130 daniel-mar 1154
         * @param string|null $user
1116 daniel-mar 1155
         * @return array
1156
         * @throws OIDplusException
1157
         */
1130 daniel-mar 1158
        public function getNotifications(string $user=null): array {
1000 daniel-mar 1159
                $notifications = array();
1160
                if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
1161
                        if (!function_exists('curl_init')) {
1162
                                $title = _L('OID-Info.com import/export');
1008 daniel-mar 1163
                                $notifications[] = array('ERR', _L('OIDplus plugin "%1" is enabled, but the required PHP extension "%2" is not installed.', '<a '.OIDplus::gui()->link('oidplus:datatransfer').'>'.htmlentities($title).'</a>', 'php_curl'));
1000 daniel-mar 1164
                        }
1165
                }
1166
                return $notifications;
1167
        }
1168
 
635 daniel-mar 1169
}