Subversion Repositories oidplus

Rev

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