Subversion Repositories oidplus

Rev

Rev 1142 | Rev 1149 | 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
1143 daniel-mar 43
         * @return array
1116 daniel-mar 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">';
1138 daniel-mar 290
                                                $out['text'] .= '<thead>';
635 daniel-mar 291
                                                $out['text'] .= '<tr><th colspan="3">'._L('Actions').'</th><th>'._L('OID').'</th></tr>';
1138 daniel-mar 292
                                                $out['text'] .= '</thead>';
293
                                                $out['text'] .= '<tbody>';
635 daniel-mar 294
 
295
                                                $lookup_nonoid = array();
296
                                                $row_lookup = array();
297
 
298
                                                $all_local_oids_of_root = array();
299
                                                $res = OIDplus::db()->query("select * from ###objects where confidential <> 1");
300
                                                while ($row = $res->fetch_object()) {
301
                                                        $obj = OIDplusObject::parse($row->id);
302
                                                        if (!$obj) continue; // can happen when object type is not enabled
303
                                                        if ($obj->isConfidential()) continue; // This will also exclude OIDs which are descendants of confidential OIDs
304
                                                        if (strpos($row->id, 'oid:') === 0) {
305
                                                                $oid = substr($row->id,strlen('oid:'));
306
                                                                if (strpos($oid.'.', $root['oid']) === 0) {
307
                                                                        $row_lookup[$oid] = $row;
308
                                                                        $all_local_oids_of_root[] = $oid;
309
                                                                }
310
                                                        } else {
311
                                                                $aids = $obj->getAltIds();
312
                                                                foreach ($aids as $aid) {
1052 daniel-mar 313
                                                                        // 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 314
                                                                        if ($aid->getNamespace() == 'oid') {
315
                                                                                $oid = $aid->getId();
316
                                                                                if (strpos($oid.'.', $root['oid']) === 0) {
317
                                                                                        $row_lookup[$oid] = $row;
318
                                                                                        $all_local_oids_of_root[] = $oid;
319
                                                                                        $lookup_nonoid[$oid] = $row->id;
320
                                                                                }
321
                                                                        }
322
                                                                }
323
                                                        }
324
                                                }
325
 
326
                                                natsort($all_local_oids_of_root);
327
                                                foreach ($all_local_oids_of_root as $local_oid) {
328
                                                        if (!in_array($local_oid, $root['children'])) {
329
                                                                $count++;
330
 
331
                                                                // Start: Build oid-info.com create URL
332
 
333
                                                                $row = $row_lookup[$local_oid];
334
 
1052 daniel-mar 335
                                                                $url = "https://oid-rep.orange-labs.fr/cgi-bin/manage?f=".oid_up($local_oid)."&a=create";
635 daniel-mar 336
 
337
                                                                $tmp = explode('.',$local_oid);
338
                                                                $url .= "&nb=".urlencode(array_pop($tmp));
339
 
340
                                                                $asn1_ids = array();
977 daniel-mar 341
                                                                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($row->id));
342
                                                                while ($row_asn = $res_asn->fetch_object()) {
343
                                                                        $asn1_ids[] = $row_asn->name; // 'unicode-label' is currently not in the standard format (oid.xsd)
635 daniel-mar 344
                                                                }
345
                                                                $url .= "&id=".array_shift($asn1_ids); // urlencode() is already done (see above)
346
                                                                $url .= "&syn_id=".implode('%0A', $asn1_ids); // urlencode() is already done (see above)
347
 
348
                                                                $iri_ids = array();
977 daniel-mar 349
                                                                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ?", array($row->id));
350
                                                                while ($row_iri = $res_iri->fetch_object()) {
351
                                                                        $iri_ids[] = $row_iri->name;
635 daniel-mar 352
                                                                }
353
                                                                $url .= "&unicode_label_list=".implode('%0A', $iri_ids); // urlencode() is already done (see above)
354
 
355
                                                                if (!empty($row->title)) {
356
                                                                        $tmp_description = $row->title;
357
                                                                        $tmp_information = $row->description;/** @phpstan-ignore-line */
358
                                                                        if (trim($row->title) == trim(strip_tags($row->description))) {/** @phpstan-ignore-line */
359
                                                                                $tmp_information = '';
360
                                                                        }
361
                                                                } else if (isset($asn1_ids[0])) {
362
                                                                        $tmp_description = '"'.$asn1_ids[0].'"';
363
                                                                        $tmp_information = $row->description;
364
                                                                } else if (isset($iri_ids[0])) {
365
                                                                        $tmp_description = '"'.$iri_ids[0].'"';
366
                                                                        $tmp_information = $row->description;
367
                                                                } else if (!empty($row->description)) {
368
                                                                        $tmp_description = $row->description;
369
                                                                        $tmp_information = '';
370
                                                                } else if (!empty($row->comment)) {
371
                                                                        $tmp_description = $row->comment;
372
                                                                        $tmp_information = '';
373
                                                                } else {
374
                                                                        $tmp_description = '<i>No description available</i>'; // do not translate
375
                                                                        $tmp_information = '';
376
                                                                }
377
 
378
                                                                if ($tmp_information != '') {
379
                                                                        $tmp_information .= '<br/><br/>';
380
                                                                }
381
 
801 daniel-mar 382
                                                                $tmp_information .= 'See <a href="'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL).'?goto='.urlencode($id).'">more information</a>.'; // do not translate
635 daniel-mar 383
 
384
                                                                if (explode(':',$id,2)[0] != 'oid') {
385
                                                                        $tmp_information = "Object: $id\n\n" . $tmp_information; // do not translate
386
                                                                }
387
 
388
                                                                $url .= "&description=".urlencode(self::repair_relative_links($tmp_description));
389
                                                                $url .= "&info=".urlencode(self::repair_relative_links($tmp_information));
390
 
391
                                                                $url .= "&current_registrant_email=".urlencode($row->ra_email);
392
 
977 daniel-mar 393
                                                                $res_ra = OIDplus::db()->query("select * from ###ra where email = ?", array($row->ra_email));
394
                                                                if ($res_ra->any()) {
395
                                                                        $row_ra = $res_ra->fetch_object();
635 daniel-mar 396
 
397
                                                                        $tmp = array();
977 daniel-mar 398
                                                                        if (!empty($row_ra->personal_name)) {
399
                                                                                $name_ary = split_firstname_lastname($row_ra->personal_name);
635 daniel-mar 400
                                                                                $tmp_first_name = $name_ary[0];
401
                                                                                $tmp_last_name  = $name_ary[1];
977 daniel-mar 402
                                                                                if (!empty($row_ra->ra_name)       ) $tmp[] = $row_ra->ra_name;
403
                                                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
404
                                                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 405
                                                                        } else {
977 daniel-mar 406
                                                                                $tmp_first_name = $row_ra->ra_name;
635 daniel-mar 407
                                                                                $tmp_last_name  = '';
977 daniel-mar 408
                                                                                if (!empty($row_ra->personal_name) ) $tmp[] = $row_ra->personal_name;
409
                                                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
410
                                                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 411
                                                                        }
412
 
413
                                                                        if (empty($tmp_first_name) || empty($tmp_last_name)) {
414
                                                                                $name = self::split_name($tmp_first_name.' '.$tmp_last_name);
415
                                                                                $tmp_first_name = $name[0];
416
                                                                                $tmp_last_name = $name[1];
417
                                                                        }
418
                                                                        $url .= "&current_registrant_first_name=".urlencode($tmp_first_name);
419
                                                                        $url .= "&current_registrant_last_name=".urlencode($tmp_last_name);
420
 
977 daniel-mar 421
                                                                        if ((count($tmp) > 0) && ($tmp[0] == $row_ra->ra_name)) array_shift($tmp);
635 daniel-mar 422
                                                                        $tmp = array_unique($tmp);
423
 
977 daniel-mar 424
                                                                        if (!$row_ra->privacy) {
425
                                                                                if (!empty($row_ra->street))   $tmp[] = $row_ra->street;
426
                                                                                if (!empty($row_ra->zip_town)) $tmp[] = $row_ra->zip_town;
427
                                                                                if (!empty($row_ra->country))  $tmp[] = $row_ra->country;
428
                                                                                $url .= "&current_registrant_tel=".urlencode(!empty($row_ra->phone) ? $row_ra->phone : $row_ra->mobile);
429
                                                                                $url .= "&current_registrant_fax=".urlencode($row_ra->fax);
635 daniel-mar 430
                                                                        }
977 daniel-mar 431
                                                                        if (empty($row_ra->zip_town) && empty($row_ra->country)) {
635 daniel-mar 432
                                                                                // The address is useless if we do neither know city nor country
433
                                                                                // Ignore it
434
                                                                        } else {
435
                                                                                $tmp = self::split_address_country(implode("<br/>", $tmp));
436
                                                                                $url .= "&current_registrant_address=".urlencode($tmp[0]);
437
                                                                                $url .= "&current_registrant_country=".urlencode($tmp[1]);
438
                                                                        }
439
                                                                }
440
                                                                if (!empty($row->updated)) {
441
                                                                        $tmp = explode('-', self::_formatdate($row->updated));
442
                                                                        $url .= "&modification_year=".urlencode($tmp[0]);
443
                                                                        $url .= "&modification_month=".urlencode($tmp[1]);
444
                                                                        $url .= "&modification_day=".urlencode($tmp[2]);
445
                                                                }
446
 
447
                                                                //$url .= "&submitter_last_name=".urlencode($xml->{'submitter'}->{'last-name'});
448
                                                                //$url .= "&submitter_first_name=".urlencode($xml->{'submitter'}->{'first-name'});
449
                                                                //$url .= "&submitter_email=".urlencode($xml->{'submitter'}->{'email'});
450
 
451
                                                                // End: Build oid-info.com create URL
452
 
453
                                                                // 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
454
                                                                $out['text'] .= '<tr id="missing_oid_'.str_replace('.','_',$local_oid).'">'.
1130 daniel-mar 455
                                                                '<td><a '.OIDplus::gui()->link($lookup_nonoid[$local_oid] ?? 'oid:' . $local_oid, true).'>'._L('View local OID').'</a></td>'.
635 daniel-mar 456
                                                                '<td><a href="javascript:OIDplusPageAdminOIDInfoExport.removeMissingOid(\''.$local_oid.'\');">'._L('Ignore for now').'</a></td>'.
457
                                                                '<td><a target="_blank" href="'.$url.'">'._L('Add to oid-info.com manually').'</a></td>'.
458
                                                                '<td>'.$local_oid.'</td>'.
459
                                                                '</tr>';
460
                                                        }
461
                                                }
1138 daniel-mar 462
                                                $out['text'] .= '</tbody>';
635 daniel-mar 463
                                                if ($count == 0) {
1138 daniel-mar 464
                                                        $out['text'] .= '<tfoot>';
635 daniel-mar 465
                                                        $out['text'] .= '<tr><td colspan="4">'._L('No missing OIDs found').'</td></tr>';
1138 daniel-mar 466
                                                        $out['text'] .= '</tfoot>';
635 daniel-mar 467
                                                }
468
                                                $out['text'] .= '</table></div></div>';
469
                                        } else {
470
                                                $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>';
471
                                        }
472
                                }
473
                        }
474
                }
475
 
476
                if ($id === 'oidplus:oidinfo_compare_import') {
477
                        $handled = true;
478
                        $out['title'] = _L('List OIDs at oid-info.com which are missing in your system');
801 daniel-mar 479
                        $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
635 daniel-mar 480
 
481
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
800 daniel-mar 482
                                $out['icon'] = 'img/error.png';
635 daniel-mar 483
                                $out['text'] = '<p>'._L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')).'</p>';
484
                                return;
485
                        }
486
 
487
                        $query = self::QUERY_LIST_OIDINFO_OIDS_V1;
488
 
489
                        $payload = array(
490
                                "query" => $query, // we must repeat the query because we want to sign it
491
                                "system_id" => OIDplus::getSystemId(false),
492
                                "show_all" => 0
493
                        );
494
 
495
                        $signature = '';
830 daniel-mar 496
                        if (!OIDplus::getPkiStatus() || !@openssl_sign(json_encode($payload), $signature, OIDplus::getSystemPrivateKey())) {
635 daniel-mar 497
                                if (!OIDplus::getPkiStatus()) {
498
                                        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.'));
499
                                } else {
500
                                        throw new OIDplusException(_L('Signature failed'));
501
                                }
502
                        }
503
 
504
                        $data = array(
505
                                "payload" => $payload,
506
                                "signature" => base64_encode($signature)
507
                        );
508
 
509
                        if (!function_exists('curl_init')) {
510
                                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'));
511
                        }
512
 
699 daniel-mar 513
                        if (OIDplus::getEditionInfo()['vendor'] != 'ViaThinkSoft') {
514
                                // The oid-info.com import functionality is a confidential API between ViaThinkSoft and oid-info.com and cannot be used in forks of OIDplus
515
                                throw new OIDplusException(_L('This feature is only available in the ViaThinkSoft edition of OIDplus'));
516
                        }
517
 
635 daniel-mar 518
                        $ch = curl_init();
519
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
520
                        curl_setopt($ch, CURLOPT_URL, 'https://oidplus.viathinksoft.com/reg2/query.php');
715 daniel-mar 521
                        curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
635 daniel-mar 522
                        curl_setopt($ch, CURLOPT_POST, 1);
523
                        if (function_exists('gzdeflate')) {
524
                                $compressed = "1";
525
                                $data2 = gzdeflate(json_encode($data));
526
                        } else {
527
                                $compressed = "0";
528
                                $data2 = json_encode($data);
529
                        }
530
                        curl_setopt($ch, CURLOPT_POSTFIELDS, "query=".urlencode($query)."&compressed=$compressed&data=".urlencode(base64_encode($data2)));
531
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
532
                        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
533
                        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
534
                        if (!($res = @curl_exec($ch))) {
535
                                throw new OIDplusException(_L('Communication with ViaThinkSoft server failed: %1',curl_error($ch)));
536
                        }
537
                        curl_close($ch);
538
 
539
                        $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>';
540
 
541
                        $json = @json_decode($res, true);
542
 
543
                        if (!$json) {
800 daniel-mar 544
                                $out['icon'] = 'img/error.png';
635 daniel-mar 545
                                $out['text'] .= _L('JSON reply from ViaThinkSoft decoding error: %1',$res);
546
                                return;
547
                        }
548
 
549
                        if (isset($json['error']) || ($json['status'] < 0)) {
800 daniel-mar 550
                                $out['icon'] = 'img/error.png';
635 daniel-mar 551
                                if (isset($json['error'])) {
552
                                        $out['text'] .= _L('Received error: %1',$json['error']);
553
                                } else {
554
                                        $out['text'] .= _L('Received error status code: %1',$json['status']);
555
                                }
556
                                return;
557
                        }
558
 
559
                        $all_local_oids = array();
560
                        $res = OIDplus::db()->query("select id from ###objects");
561
                        while ($row = $res->fetch_array()) {
562
                                if (strpos($row['id'], 'oid:') === 0) {
563
                                        $all_local_oids[] = substr($row['id'],strlen('oid:'));
564
                                } else {
565
                                        $obj = OIDplusObject::parse($row['id']);
566
                                        if (!$obj) continue; // can happen when object type is not enabled
567
                                        $aids = $obj->getAltIds();
568
                                        foreach ($aids as $aid) {
1052 daniel-mar 569
                                                // 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 570
                                                if ($aid->getNamespace() == 'oid') {
571
                                                        $all_local_oids[] = $aid->getId();
572
                                                }
573
                                        }
574
                                }
575
                        }
576
 
577
                        if (isset($json['error']) || ($json['status'] < 0)) {
578
                                $out['text'] .= '<p>'._L('Error: %1',htmlentities($json['error'])).'</p>';
579
                        } else {
580
                                // 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
581
                                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>';
582
                                foreach ($json['roots'] as $root) {
583
                                        $oid = $root['oid'];
584
                                        $out['text'] .= '<h2>'._L('Root OID %1',$oid).'</h2>';
585
                                        // TODO: "Import all" button
586
                                        if ($root['verified']) {
587
                                                $count = 0;
588
                                                $out['text'] .= '<div class="container box"><div id="suboid_table" class="table-responsive">';
589
                                                $out['text'] .= '<table class="table table-bordered table-striped">';
1138 daniel-mar 590
                                                $out['text'] .= '<thead>';
635 daniel-mar 591
                                                $out['text'] .= '<tr><th colspan="4">'._L('Actions').'</th><th>'._L('OID').'</th></tr>';
1138 daniel-mar 592
                                                $out['text'] .= '</thead>';
593
                                                $out['text'] .= '<tbody>';
635 daniel-mar 594
                                                natsort($root['children']);
595
                                                foreach ($root['children'] as $child_oid) {
596
                                                        if (!in_array($child_oid, $all_local_oids)) {
597
                                                                $count++;
598
                                                                // 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
599
                                                                $out['text'] .= '<tr id="missing_oid_'.str_replace('.','_',$child_oid).'">'.
1052 daniel-mar 600
                                                                '<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 601
                                                                '<td><a href="javascript:OIDplusPageAdminOIDInfoExport.removeMissingOid(\''.$child_oid.'\');">'._L('Ignore for now').'</a></td>'.
602
                                                                '<td><a href="mailto:admin@oid-info.com">'._L('Report illegal OID').'</a></td>'.
603
                                                                (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>').
604
                                                                '<td>'.$child_oid.'</td>'.
605
                                                                '</tr>';
606
                                                        }
607
                                                }
1138 daniel-mar 608
                                                $out['text'] .= '</tbody>';
635 daniel-mar 609
                                                if ($count == 0) {
1138 daniel-mar 610
                                                        $out['text'] .= '<tfoot>';
635 daniel-mar 611
                                                        $out['text'] .= '<tr><td colspan="5">'._L('No extra OIDs found').'</td></tr>';
1138 daniel-mar 612
                                                        $out['text'] .= '</tfoot>';
635 daniel-mar 613
                                                }
614
                                                $out['text'] .= '</table></div></div>';
615
                                        } else {
616
                                                $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>';
617
                                        }
618
                                }
619
                        }
620
                }
621
 
622
                if ($id === 'oidplus:datatransfer') {
623
                        $handled = true;
624
                        $out['title'] = _L('Data Transfer');
801 daniel-mar 625
                        $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
635 daniel-mar 626
 
627
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
800 daniel-mar 628
                                $out['icon'] = 'img/error.png';
635 daniel-mar 629
                                $out['text'] = '<p>'._L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')).'</p>';
630
                                return;
631
                        }
632
 
890 daniel-mar 633
                        $out['text'] = '<noscript>';
634
                        $out['text'] .= '<p>'._L('You need to enable JavaScript to use the login area.').'</p>';
635
                        $out['text'] .= '</noscript>';
635 daniel-mar 636
 
890 daniel-mar 637
                        $out['text'] .= '<br><div id="dataTransferArea" style="visibility: hidden"><div id="dataTransferTab" class="container" style="width:100%;">';
638
 
635 daniel-mar 639
                        // ---------------- Tab control
640
                        $out['text'] .= OIDplus::gui()->tabBarStart();
641
                        $out['text'] .= OIDplus::gui()->tabBarElement('export', _L('Export'), $tab === 'export');
642
                        $out['text'] .= OIDplus::gui()->tabBarElement('import', _L('Import'), $tab === 'import');
643
                        $out['text'] .= OIDplus::gui()->tabBarEnd();
644
                        $out['text'] .= OIDplus::gui()->tabContentStart();
645
                        // ---------------- "Export" tab
646
                        $tabcont  = '<h2>'._L('Generate XML file containing all OIDs').'</h2>';
1052 daniel-mar 647
                        $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 648
                        $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>';
649
                        $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 650
                        $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 651
                        $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>';
652
                        $tabcont .= '<h2>'._L('Automatic export to oid-info.com').'</h2>';
653
                        $privacy_level = OIDplus::config()->getValue('reg_privacy');
654
                        if ($privacy_level == 0) {
655
                                $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>';
656
                        } else {
657
                                $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>';
658
                        }
659
                        $tabcont .= '<h2>'._L('Comparison with oid-info.com').'</h2>';
660
                        $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>';
661
                        $out['text'] .= OIDplus::gui()->tabContentPage('export', $tabcont, $tab === 'export');
662
                        // ---------------- "Import" tab
663
                        $tabcont  = '<h2>'._L('Import XML file').'</h2>';
1052 daniel-mar 664
                        $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 665
                        // TODO: we need a waiting animation!
666
                        $tabcont .= '<form action="javascript:void(0);" onsubmit="return OIDplusPageAdminOIDInfoExport.uploadXmlFileOnSubmit(this);" enctype="multipart/form-data" id="uploadXmlFileForm">';
667
                        $tabcont .= '<div>'._L('Choose XML file here').':<input type="file" name="userfile" value="" id="userfile">';
668
                        $tabcont .= '<br><input type="submit" value="'._L('Import XML').'"></div>';
669
                        $tabcont .= '</form>';
670
                        $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>';
671
                        $tabcont .= '<h2>'._L('Comparison with oid-info.com').'</h2>';
672
                        $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>';
673
                        $out['text'] .= OIDplus::gui()->tabContentPage('import', $tabcont, $tab === 'import');
674
                        $out['text'] .= OIDplus::gui()->tabContentEnd();
675
                        // ---------------- Tab control END
676
 
677
                        $out['text'] .= '</div></div><script>$("#dataTransferArea")[0].style.visibility = "visible";</script>';
678
                }
679
        }
680
 
1116 daniel-mar 681
        /**
682
         * @param array $json
683
         * @param string|null $ra_email
684
         * @param bool $nonjs
685
         * @param string $req_goto
686
         * @return bool
687
         * @throws OIDplusException
688
         */
689
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 690
                if (!OIDplus::authUtils()->isAdminLoggedIn()) return false;
691
 
800 daniel-mar 692
                if (file_exists(__DIR__.'/img/main_icon16.png')) {
801 daniel-mar 693
                        $tree_icon = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png';
635 daniel-mar 694
                } else {
695
                        $tree_icon = null; // default icon (folder)
696
                }
697
 
698
                $json[] = array(
699
                        'id' => 'oidplus:datatransfer',
700
                        'icon' => $tree_icon,
701
                        'text' => _L('Data Transfer')
702
                );
703
 
704
                return true;
705
        }
706
 
1116 daniel-mar 707
        /**
708
         * @param string $request
709
         * @return array|false
710
         */
711
        public function tree_search(string $request) {
635 daniel-mar 712
                return false;
713
        }
714
 
1116 daniel-mar 715
        /**
1130 daniel-mar 716
         * @param bool $only_non_existing
1116 daniel-mar 717
         * @return string[]
718
         * @throws OIDplusException
719
         * @throws \OIDInfoException
720
         */
1130 daniel-mar 721
        public static function outputXML(bool $only_non_existing): array {
940 daniel-mar 722
                $out_type = null;
723
                $out_content = '';
724
 
1050 daniel-mar 725
                // This file contains class \OIDInfoAPI.
635 daniel-mar 726
                // We cannot include this in init(), because the init
727
                // of the registration plugin (OIDplusPageAdminRegistration) uses
728
                // OIDplusPageAdminOIDInfoExport::outputXML() before
729
                // OIDplusPageAdminOIDInfoExport::init() ,
730
                // because OIDplusPageAdminRegistration::init() comes first sometimes.
731
                require_once __DIR__ . '/oidinfo_api.inc.php';
732
 
1050 daniel-mar 733
                $oa = new \OIDInfoAPI();
724 daniel-mar 734
                if ($only_non_existing) {
735
                        if (!function_exists('socket_create')) {
736
                                throw new OIDplusException(_L('You must install the PHP "sockets" in order to check for non-existing OIDs.'));
737
                        }
738
                        $oa->addSimplePingProvider('viathinksoft.de:49500');
739
                }
635 daniel-mar 740
 
741
                $email = OIDplus::config()->getValue('admin_email');
742
                if (empty($email)) $email = 'unknown@example.com';
743
 
724 daniel-mar 744
                $sys_title = OIDplus::config()->getValue('system_title');
745
                $name1 = !empty($sys_title) ? $sys_title : 'OIDplus 2.0';
1130 daniel-mar 746
                $name2 = $_SERVER['SERVER_NAME'] ?? 'Export interface';
940 daniel-mar 747
                $out_content .= $oa->xmlAddHeader($name1, $name2, $email); // do not translate
635 daniel-mar 748
 
749
                $params['allow_html'] = true;
750
                $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 751
                $params['soft_correct_behavior'] = \OIDInfoAPI::SOFT_CORRECT_BEHAVIOR_NONE;
635 daniel-mar 752
                $params['do_online_check'] = false; // Flag to disable this online check, because it generates a lot of traffic and runtime.
753
                $params['do_illegality_check'] = true;
754
                $params['do_simpleping_check'] = $only_non_existing;
755
                $params['auto_extract_name'] = '';
756
                $params['auto_extract_url'] = '';
757
                $params['always_output_comment'] = false;
758
                $params['creation_allowed_check'] = $only_non_existing;
759
                $params['tolerant_htmlentities'] = true;
760
                $params['ignore_xhtml_light'] = false;
761
 
762
                $nonConfidential = OIDplusObject::getAllNonConfidential();
763
                natsort($nonConfidential);
764
 
765
                foreach ($nonConfidential as $id) {
977 daniel-mar 766
                        $obj = OIDplusObject::parse($id);
767
                        if ($obj) {
635 daniel-mar 768
                                $elements['identifier'] = array();
977 daniel-mar 769
                                $res_asn = OIDplus::db()->query("select * from ###asn1id where oid = ?", array($id));
770
                                while ($row_asn = $res_asn->fetch_object()) {
771
                                        $elements['identifier'][] = $row_asn->name; // 'unicode-label' is currently not in the standard format (oid.xsd)
635 daniel-mar 772
                                }
773
 
774
                                $elements['unicode-label'] = array();
977 daniel-mar 775
                                $res_iri = OIDplus::db()->query("select * from ###iri where oid = ?", array($id));
776
                                while ($row_iri = $res_iri->fetch_object()) {
777
                                        $elements['unicode-label'][] = $row_iri->name;
635 daniel-mar 778
                                }
779
 
977 daniel-mar 780
                                $title = $obj->getTitle();
781
                                $description = $obj->getDescription();
782
                                $comment = $obj->getComment();
783
                                if (!empty($title)) {
784
                                        $elements['description'] = $title;
785
                                        $elements['information'] = $description;
786
                                        if (trim($title) == trim(strip_tags($description))) {
635 daniel-mar 787
                                                $elements['information'] = '';
788
                                        }
789
                                } else if (isset($elements['identifier'][0])) {
790
                                        $elements['description'] = '"'.$elements['identifier'][0].'"';
977 daniel-mar 791
                                        $elements['information'] = $description;
635 daniel-mar 792
                                } else if (isset($elements['unicode-label'][0])) {
793
                                        $elements['description'] = '"'.$elements['unicode-label'][0].'"';
977 daniel-mar 794
                                        $elements['information'] = $description;
795
                                } else if (!empty($description)) {
796
                                        $elements['description'] = $description;
635 daniel-mar 797
                                        $elements['information'] = '';
977 daniel-mar 798
                                } else if (!empty($comment)) {
799
                                        $elements['description'] = $comment;
635 daniel-mar 800
                                        $elements['information'] = '';
801
                                } else {
802
                                        $elements['description'] = '<i>No description available</i>'; // do not translate
803
                                        $elements['information'] = '';
804
                                }
805
 
806
                                if ($elements['information'] != '') {
807
                                        $elements['information'] .= '<br/><br/>';
808
                                }
809
 
801 daniel-mar 810
                                $elements['information'] .= 'See <a href="'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL).'?goto='.urlencode($id).'">more information</a>.'; // do not translate
635 daniel-mar 811
 
812
                                if (explode(':',$id,2)[0] != 'oid') {
813
                                        $elements['information'] = "Object: $id\n\n" . $elements['information']; // do not translate
814
                                }
815
 
816
                                $elements['description'] = self::repair_relative_links($elements['description']);
817
                                $elements['information'] = self::repair_relative_links($elements['information']);
818
 
819
                                $elements['first-registrant']['first-name'] = '';
820
                                $elements['first-registrant']['last-name'] = '';
821
                                $elements['first-registrant']['address'] = '';
822
                                $elements['first-registrant']['email'] = '';
823
                                $elements['first-registrant']['phone'] = '';
824
                                $elements['first-registrant']['fax'] = '';
1142 daniel-mar 825
                                $create_ts = $obj->getCreatedTime();
826
                                $elements['first-registrant']['creation-date'] = $create_ts ? self::_formatdate($create_ts) : '';
635 daniel-mar 827
 
828
                                $elements['current-registrant']['first-name'] = '';
829
                                $elements['current-registrant']['last-name'] = '';
977 daniel-mar 830
                                $elements['current-registrant']['email'] = $obj->getRaMail();
635 daniel-mar 831
                                $elements['current-registrant']['phone'] = '';
832
                                $elements['current-registrant']['fax'] = '';
833
                                $elements['current-registrant']['address'] = '';
834
 
977 daniel-mar 835
                                $res_ra = OIDplus::db()->query("select * from ###ra where email = ?", array($obj->getRaMail()));
836
                                if ($res_ra->any()) {
837
                                        $row_ra = $res_ra->fetch_object();
635 daniel-mar 838
 
839
                                        $tmp = array();
977 daniel-mar 840
                                        if (!empty($row_ra->personal_name)) {
841
                                                $name_ary = split_firstname_lastname($row_ra->personal_name);
635 daniel-mar 842
                                                $elements['current-registrant']['first-name'] = $name_ary[0];
843
                                                $elements['current-registrant']['last-name']  = $name_ary[1];
977 daniel-mar 844
                                                if (!empty($row_ra->ra_name)       ) $tmp[] = $row_ra->ra_name;
845
                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
846
                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 847
                                        } else {
977 daniel-mar 848
                                                $elements['current-registrant']['first-name'] = $row_ra->ra_name;
635 daniel-mar 849
                                                $elements['current-registrant']['last-name']  = '';
977 daniel-mar 850
                                                if (!empty($row_ra->personal_name) ) $tmp[] = $row_ra->personal_name;
851
                                                if (!empty($row_ra->office)        ) $tmp[] = $row_ra->office;
852
                                                if (!empty($row_ra->organization)  ) $tmp[] = $row_ra->organization;
635 daniel-mar 853
                                        }
854
 
977 daniel-mar 855
                                        if ((count($tmp) > 0) && ($tmp[0] == $row_ra->ra_name)) array_shift($tmp);
635 daniel-mar 856
                                        $tmp = array_unique($tmp);
857
 
977 daniel-mar 858
                                        if (!$row_ra->privacy) {
859
                                                if (!empty($row_ra->street))   $tmp[] = $row_ra->street;
860
                                                if (!empty($row_ra->zip_town)) $tmp[] = $row_ra->zip_town;
861
                                                if (!empty($row_ra->country))  $tmp[] = $row_ra->country;
862
                                                $elements['current-registrant']['phone'] = !empty($row_ra->phone) ? $row_ra->phone : $row_ra->mobile;
863
                                                $elements['current-registrant']['fax'] = $row_ra->fax;
635 daniel-mar 864
                                        }
977 daniel-mar 865
                                        if (empty($row_ra->zip_town) && empty($row_ra->country)) {
635 daniel-mar 866
                                                // The address is useless if we do neither know city nor country
867
                                                // Ignore it
868
                                                $elements['current-registrant']['address'] = '';
869
                                        } else {
870
                                                $elements['current-registrant']['address'] = implode("<br/>", $tmp);
871
                                        }
872
                                }
1142 daniel-mar 873
                                $update_ts = $obj->getUpdatedTime();
874
                                $elements['current-registrant']['modification-date'] = $update_ts ? self::_formatdate($update_ts) : '';
635 daniel-mar 875
 
876
                                // Request from O.D. 20 May 2019: First registrant should not be empty (especially for cases where Creation and Modify Dates are the same)
877
                                // Actually, this is a problem because we don't know the first registrant.
878
                                // However, since oidinfo gets their XML very fast (if using registration), it is likely that the reported RA is still the same...
879
                                // ... and changes at the RA are not reported to oid-info.com anyways - the XML is only for creation
880
 
881
                                $elements['first-registrant']['first-name'] = $elements['current-registrant']['first-name'];
882
                                $elements['first-registrant']['last-name']  = $elements['current-registrant']['last-name'];
883
                                $elements['first-registrant']['address']    = $elements['current-registrant']['address'];
884
                                $elements['first-registrant']['email']      = $elements['current-registrant']['email'];
885
                                $elements['first-registrant']['phone']      = $elements['current-registrant']['phone'];
886
                                $elements['first-registrant']['fax']        = $elements['current-registrant']['fax'];
887
 
888
                                $elements['current-registrant']['first-name'] = '';
889
                                $elements['current-registrant']['last-name'] = '';
890
                                $elements['current-registrant']['address'] = '';
891
                                $elements['current-registrant']['email'] = '';
892
                                $elements['current-registrant']['phone'] = '';
893
                                $elements['current-registrant']['fax'] = '';
894
                                $elements['current-registrant']['modification-date'] = '';
895
 
896
                                // End request O.D. 20 May 2019
897
 
898
                                list($ns,$id) = explode(':',$obj->nodeId());
899
                                if ($ns == 'oid') {
940 daniel-mar 900
                                        $out_content .= $oa->createXMLEntry($id, $elements, $params, $comment=$obj->nodeId());
635 daniel-mar 901
                                } else {
902
                                        $alt_ids = $obj->getAltIds(); // TODO: slow!
903
                                        foreach ($alt_ids as $alt_id) {
904
                                                $ns = $alt_id->getNamespace();
905
                                                $id = $alt_id->getId();
906
                                                $desc = $alt_id->getDescription();
907
                                                if ($ns == 'oid') {
1052 daniel-mar 908
                                                        // 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 909
                                                        if (strpos($id, '2.25.') === 0) continue; // don't spam the uuid arc with GUID objects
940 daniel-mar 910
                                                        $out_content .= $oa->createXMLEntry($id, $elements, $params, $comment=$obj->nodeId());
635 daniel-mar 911
                                                }
912
                                        }
913
                                }
914
                        }
915
                }
916
 
940 daniel-mar 917
                $out_content .= $oa->xmlAddFooter();
918
 
919
                $out_type = 'text/xml';
920
                return array($out_content, $out_type);
635 daniel-mar 921
        }
922
 
1116 daniel-mar 923
        /**
1125 daniel-mar 924
         * @param string $str
925
         * @return string
1116 daniel-mar 926
         */
1125 daniel-mar 927
        private static function _formatdate(string $str): string {
635 daniel-mar 928
                $str = explode(' ',$str)[0];
929
                if ($str == '0000-00-00') $str = '';
930
                return $str;
931
        }
932
 
1116 daniel-mar 933
        /**
1130 daniel-mar 934
         * @param string $str
935
         * @return string
1116 daniel-mar 936
         * @throws OIDplusException
937
         */
1130 daniel-mar 938
        private static function repair_relative_links(string $str): string {
939
                return preg_replace_callback('@(href\s*=\s*([\'"]))(.+)(\\2)@ismU', function($treffer) {
635 daniel-mar 940
                        $url = $treffer[3];
941
                        if ((stripos($url,'http:') !== 0) && (stripos($url,'https:') !== 0) && (stripos($url,'ftp:') !== 0)) {
942
                                if (stripos($url,'www.') === 0) {
943
                                        $url .= 'http://' . $url;
944
                                } else {
801 daniel-mar 945
                                        $url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . $url; // TODO: Canonical or not?
635 daniel-mar 946
                                }
947
                        }
948
                        return $treffer[1].$url.$treffer[4];
949
                }, $str);
950
        }
951
 
1116 daniel-mar 952
        /**
1125 daniel-mar 953
         * @param string $address
954
         * @return string[]
1116 daniel-mar 955
         */
1125 daniel-mar 956
        private static function split_address_country(string $address): array {
635 daniel-mar 957
                global $oidinfo_countries;
958
                $ary = explode("\n", $address);
959
                $last_line = array_pop($ary);
960
                $rest = implode("\n", $ary);
961
                if (isset($oidinfo_countries[$last_line])) {
962
                        return array($rest, $oidinfo_countries[$last_line]);
963
                } else {
964
                        return array($rest."\n".$last_line, '');
965
                }
966
        }
967
 
1116 daniel-mar 968
        /**
1130 daniel-mar 969
         * @param string $name
1116 daniel-mar 970
         * @return array
971
         */
1130 daniel-mar 972
        private static function split_name(string $name): array {
635 daniel-mar 973
                // uses regex that accepts any word character or hyphen in last name
974
                // https://stackoverflow.com/questions/13637145/split-text-string-into-first-and-last-name-in-php
975
                $name = trim($name);
976
                $last_name = (strpos($name, ' ') === false) ? '' : preg_replace('#.*\s([\w-]*)$#', '$1', $name);
977
                $first_name = trim( preg_replace('#'.preg_quote($last_name,'#').'#', '', $name ) );
978
                return array($first_name, $last_name);
979
        }
980
 
981
        /*protected*/ const ORPHAN_IGNORE = 0;
982
        /*protected*/ const ORPHAN_AUTO_DEORPHAN = 1;
983
        /*protected*/ const ORPHAN_DISALLOW_ORPHANS = 2;
984
 
1116 daniel-mar 985
        /**
1130 daniel-mar 986
         * @param string $xml_contents
987
         * @param array $errors
988
         * @param bool $replaceExistingOIDs
989
         * @param int $orphan_mode
1125 daniel-mar 990
         * @return int[]
1116 daniel-mar 991
         * @throws OIDplusException
992
         */
1130 daniel-mar 993
        protected function oidinfoImportXML(string $xml_contents, array &$errors, bool $replaceExistingOIDs=false, int $orphan_mode=self::ORPHAN_AUTO_DEORPHAN): array {
635 daniel-mar 994
                // TODO: Implement RA import (let the user decide)
995
                // TODO: Let the user decide about $replaceExistingOIDs
996
                // 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)
997
 
998
                $xml_contents = str_replace('<description>', '<description><![CDATA[', $xml_contents);
999
                $xml_contents = str_replace('</description>', ']]></description>', $xml_contents);
1000
 
1001
                $xml_contents = str_replace('<information>', '<information><![CDATA[', $xml_contents);
1002
                $xml_contents = str_replace('</information>', ']]></information>', $xml_contents);
1003
 
1004
                $xml = @simplexml_load_string($xml_contents);
1005
 
1006
                $count_already_existing = 0;
1007
                $count_errors = 0;
1008
                $count_warnings = 0;
1009
 
1010
                if (!$xml) {
1011
                        $errors[] = _L('Cannot read XML data. The XML file is probably invalid.');
1012
                        $count_errors++;
1013
                        return array(0, 0, 1, 0);
1014
                }
1015
 
1016
                $ok_oids = array();
1017
 
1018
                foreach ($xml->oid as $xoid) {
1019
 
1020
                        if (isset($xoid->{'dot-notation'})) {
1021
                                $dot_notation = $xoid->{'dot-notation'}->__toString();
1022
                        } else if (isset($xoid->{'asn1-notation'})) {
1023
                                $dot_notation = asn1_to_dot($xoid->{'asn1-notation'}->__toString());
1024
                        } else {
1025
                                $errors[] = _L('Cannot find dot notation because fields asn1-notation and dot-notation are both not existing');
1026
                                $count_errors++;
1027
                                continue;
1028
                        }
1029
 
1030
                        $id = "oid:$dot_notation";
1031
                        $title = isset($xoid->{'description'}) ? $xoid->{'description'}->__toString() : '';
1032
                        $info = isset($xoid->{'description'}) ? $xoid->{'information'}->__toString() : '';
1033
 
991 daniel-mar 1034
                        // For ASN.1 definitions, "Description" is filled with the definition and "Information" is usually empty
1035
                        if (strpos($title,'<br') !== false) {
1036
                                $info = $title . $info;
1037
                                $title = explode(' ',$title)[0];
1038
                        }
1039
 
635 daniel-mar 1040
                        if (isset($xoid->{'current-registrant'}->email)) {
1041
                                $ra = $xoid->{'current-registrant'}->email->__toString();
1042
                        } else if (isset($xoid->{'first-registrant'}->email)) {
1043
                                $ra = $xoid->{'first-registrant'}->email->__toString();
1044
                        } else {
1045
                                $ra = '';
1046
                        }
1047
 
1048
                        if (!oid_valid_dotnotation($dot_notation, false, false)) {
1049
                                $errors[] = _L('Ignored OID %1 because its dot notation is illegal or was not found',$dot_notation);
1050
                                $count_errors++;
1051
                                continue;
1052
                        }
1053
 
1054
                        $parent = 'oid:'.oid_up($dot_notation);
1055
 
1056
                        if ($orphan_mode === self::ORPHAN_DISALLOW_ORPHANS) {
977 daniel-mar 1057
                                if (!OIDplusObject::exists($parent)) {
635 daniel-mar 1058
                                        $errors[] = _L('Cannot import %1, because its parent is not in the database.',$dot_notation);
1059
                                        $count_errors++;
1060
                                        continue;
1061
                                }
1062
                        }
1063
 
978 daniel-mar 1064
                        OIDplus::db()->transaction_begin();
1065
 
977 daniel-mar 1066
                        $obj_test = OIDplusObject::findFitting($id);
1067
                        if ($obj_test) {
635 daniel-mar 1068
                                if ($replaceExistingOIDs) {
978 daniel-mar 1069
                                        OIDplus::db()->query("delete from ###objects where id = ?", array($id));
635 daniel-mar 1070
                                        OIDplus::db()->query("delete from ###asn1id where oid = ?", array($id));
1071
                                        OIDplus::db()->query("delete from ###iri where oid = ?", array($id));
1072
                                } else {
1073
                                        //$errors[] = "Ignore OID '$dot_notation' because it already exists";
1074
                                        //$count_errors++;
1075
                                        $count_already_existing++;
1076
                                        continue;
1077
                                }
1078
                        }
1079
 
991 daniel-mar 1080
                        // TODO: we can probably get the created and modified timestamp from oid-info.com XML
635 daniel-mar 1081
                        OIDplus::db()->query("insert into ###objects (id, parent, title, description, confidential, ra_email) values (?, ?, ?, ?, ?, ?)", array($id, $parent, $title, $info, 0, $ra));
1082
 
978 daniel-mar 1083
                        OIDplus::db()->transaction_commit();
1084
                        OIDplusObject::resetObjectInformationCache();
1085
 
635 daniel-mar 1086
                        $this_oid_has_warnings = false;
1087
 
1088
                        // ---------------------------------------------------------------------
1089
 
1090
                        $asn1ids = array();
1091
                        if (isset($xoid->{'identifier'})) {
1092
                                $asn1ids[] = $xoid->{'identifier'}->__toString();
1093
                        }
1094
                        if (isset($xoid->{'asn1-notation'})) {
1095
                                $last_id = asn1_last_identifier($xoid->{'asn1-notation'}->__toString());
1096
                                if ($last_id) {
1097
                                        $asn1ids[] = $last_id;
1098
                                }
1099
                        }
1100
                        if (isset($xoid->{'synonymous-identifier'})) {
1101
                                foreach ($xoid->{'synonymous-identifier'} as $synid) {
1102
                                        $asn1ids[] = $synid->__toString();
1103
                                }
1104
                        }
1105
                        $asn1ids = array_unique($asn1ids);
1106
                        foreach ($asn1ids as $asn1id) {
1107
                                if (!oid_id_is_valid($asn1id)) {
1108
                                        $errors[] = _L('Warning').' ['._L('OID %1',$dot_notation).']: '._L('Ignored alphanumeric identifier %1, because it is invalid',$asn1id);
1109
                                        $this_oid_has_warnings = true;
1110
                                } else {
1111
                                        OIDplus::db()->query("delete from ###asn1id where oid = ? and name = ?", array($id, $asn1id));
1112
                                        OIDplus::db()->query("insert into ###asn1id (oid, name) values (?, ?)", array($id, $asn1id));
1113
                                }
1114
                        }
1115
 
1116
                        // ---------------------------------------------------------------------
1117
 
1118
                        if (isset($xoid->{'unicode-label'})) {
1119
                                $iris = array();
1120
                                foreach ($xoid->{'unicode-label'} as $iri) {
1121
                                        $iris[] = $iri->__toString();
1122
                                }
1123
                                $iris = array_unique($iris);
1124
                                foreach ($iris as $iri) {
1125
                                        if (!iri_arc_valid($iri, false)) {
1126
                                                $errors[] = _L('Warning').' ['._L('OID %1',$dot_notation).']: '._L('Ignored Unicode label %1, because it is invalid',$iri);
1127
                                                $this_oid_has_warnings = true;
1128
                                        } else {
1129
                                                OIDplus::db()->query("delete from ###iri where oid = ? and name = ?", array($id, $iri));
1130
                                                OIDplus::db()->query("insert into ###iri (oid, name) values (?, ?)", array($id, $iri));
1131
                                        }
1132
                                }
1133
                        }
1134
 
1135
                        if ($this_oid_has_warnings) $count_warnings++;
1136
                        $ok_oids[] = $id;
1137
                }
1138
 
1139
                // De-orphanize
978 daniel-mar 1140
                //if ($orphan_mode === self::ORPHAN_AUTO_DEORPHAN) {
1141
                //      OIDplus::db()->query("update ###objects set parent = 'oid:' where parent like 'oid:%' and parent not in (select id from ###objects)");
1142
                //      OIDplusObject::resetObjectInformationCache();
1143
                //}
635 daniel-mar 1144
                foreach ($ok_oids as $id) {
1145
                        // De-orphanize if neccessary
1146
                        if ($orphan_mode === self::ORPHAN_AUTO_DEORPHAN) {
1147
                                $res = OIDplus::db()->query("select * from ###objects where id = ? and parent not in (select id from ###objects)", array($id));
790 daniel-mar 1148
                                if ($res->any()) {
635 daniel-mar 1149
                                        $errors[] = _L("%1 was de-orphaned (placed as root OID) because its parent is not existing.",$id);
1150
                                        $count_warnings++;
1151
                                        OIDplus::db()->query("update ###objects set parent = 'oid:' where id = ? and parent not in (select id from ###objects)", array($id));
978 daniel-mar 1152
                                        OIDplusObject::resetObjectInformationCache();
635 daniel-mar 1153
                                }
1154
                        }
1155
 
1156
                        // 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
1157
                        OIDplus::logger()->log("[INFO]OID($id)+[INFO]SUPOID($id)+[INFO]SUPOIDRA($id)!/[INFO]A!", "Object '$id' was automatically created by the XML import tool");
1158
                }
1159
 
1160
                $count_imported_oids = count($ok_oids);
1161
 
1162
                return array($count_imported_oids, $count_already_existing, $count_errors, $count_warnings);
1163
 
1164
        }
1165
 
1116 daniel-mar 1166
        /**
1131 daniel-mar 1167
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
1130 daniel-mar 1168
         * @param string|null $user
1116 daniel-mar 1169
         * @return array
1170
         * @throws OIDplusException
1171
         */
1130 daniel-mar 1172
        public function getNotifications(string $user=null): array {
1000 daniel-mar 1173
                $notifications = array();
1174
                if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
1175
                        if (!function_exists('curl_init')) {
1176
                                $title = _L('OID-Info.com import/export');
1008 daniel-mar 1177
                                $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 1178
                        }
1179
                }
1180
                return $notifications;
1181
        }
1182
 
635 daniel-mar 1183
}