Subversion Repositories oidplus

Rev

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