Subversion Repositories oidplus

Rev

Rev 1449 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
990 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1463 daniel-mar 5
 * Copyright 2022 - 2024 Daniel Marschall, ViaThinkSoft / Till Wehowski, Frdlweb
990 daniel-mar 6
 *
7
 * Licensed under the MIT License.
8
 */
9
 
1050 daniel-mar 10
namespace Frdlweb\OIDplus;
990 daniel-mar 11
 
1463 daniel-mar 12
use ViaThinkSoft\OIDplus\INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3;
1131 daniel-mar 13
use ViaThinkSoft\OIDplus\INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_4;
14
use ViaThinkSoft\OIDplus\INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_7;
1463 daniel-mar 15
use ViaThinkSoft\OIDplus\INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8;
1050 daniel-mar 16
use ViaThinkSoft\OIDplus\OIDplus;
17
use ViaThinkSoft\OIDplus\OIDplusObject;
18
use ViaThinkSoft\OIDplus\OIDplusPagePluginPublic;
1463 daniel-mar 19
use ViaThinkSoft\OIDplus\OIDplusNotification;
1050 daniel-mar 20
 
1086 daniel-mar 21
// phpcs:disable PSR1.Files.SideEffects
22
\defined('INSIDE_OIDPLUS') or die;
23
// phpcs:enable PSR1.Files.SideEffects
24
 
1131 daniel-mar 25
class OIDplusPagePublicAltIds extends OIDplusPagePluginPublic
1463 daniel-mar 26
        implements INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_4,  /* whois*Attributes */
27
                   INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_7,  /* getAlternativesForQuery */
28
                   INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8,  /* getNotifications */
29
                   INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3   /* *objects* */
1131 daniel-mar 30
{
990 daniel-mar 31
 
1116 daniel-mar 32
        /**
1463 daniel-mar 33
         * @var bool
1116 daniel-mar 34
         */
1463 daniel-mar 35
        private $db_table_exists;
36
 
37
 
38
        /**
39
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
40
         * @param string $id
41
         * @return void
42
         */
43
        public function beforeObjectDelete(string $id){
44
 
990 daniel-mar 45
        }
46
 
1116 daniel-mar 47
        /**
1463 daniel-mar 48
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
1116 daniel-mar 49
         * @param string $id
50
         * @return void
51
         */
1463 daniel-mar 52
        public function afterObjectDelete(string $id){
53
                if (!$this->db_table_exists) return;
54
                OIDplus::db()->query("DELETE FROM ###altids WHERE origin = ?", [$id]);
55
        }
990 daniel-mar 56
 
1463 daniel-mar 57
        /**
58
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
59
         * @param string $id
60
         * @param array $params
61
         * @return void
62
         */
63
        public function beforeObjectUpdateSuperior(string $id, array &$params){
64
 
990 daniel-mar 65
        }
66
 
1116 daniel-mar 67
        /**
1463 daniel-mar 68
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
69
         * @param string $id
70
         * @param array $params
1116 daniel-mar 71
         * @return void
72
         */
1463 daniel-mar 73
        public function afterObjectUpdateSuperior(string $id, array &$params){
74
                $this->saveAltIdsForQuery($id);
75
        }
990 daniel-mar 76
 
1463 daniel-mar 77
        /**
78
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
79
         * @param string $id
80
         * @param array $params
81
         * @return void
82
         */
83
        public function beforeObjectUpdateSelf(string $id, array &$params){
84
 
990 daniel-mar 85
        }
86
 
1116 daniel-mar 87
        /**
1463 daniel-mar 88
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
89
         * @param string $id
90
         * @param array $params
91
         * @return void
1116 daniel-mar 92
         */
1463 daniel-mar 93
        public function afterObjectUpdateSelf(string $id, array &$params){
94
                $this->saveAltIdsForQuery($id);
990 daniel-mar 95
        }
96
 
1116 daniel-mar 97
        /**
1463 daniel-mar 98
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
99
         * @param string $id
100
         * @param array $params
101
         * @return void
1116 daniel-mar 102
         */
1463 daniel-mar 103
        public function beforeObjectInsert(string $id, array &$params){
104
 
990 daniel-mar 105
        }
106
 
1116 daniel-mar 107
        /**
1463 daniel-mar 108
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_3
109
         * @param string $id
110
         * @param array $params
111
         * @return void
1116 daniel-mar 112
         */
1463 daniel-mar 113
        public function afterObjectInsert(string $id, array &$params){
114
                $this->saveAltIdsForQuery($id);
115
        }
990 daniel-mar 116
 
117
 
1463 daniel-mar 118
        /**
119
         * Adds the required database table if DBMS is known
120
         * @param bool $html
121
         * @return void
122
         * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
123
         * @throws \ViaThinkSoft\OIDplus\OIDplusException
124
         */
125
        public function init(bool $html=true) {
126
                if (!OIDplus::db()->tableExists("###altids")) {
127
                        if (OIDplus::db()->getSlang()->id() == 'mysql') {
128
                                OIDplus::db()->query("CREATE TABLE ###altids ( `origin` varchar(255) NOT NULL, `alternative` varchar(255) NOT NULL, UNIQUE KEY (`origin`, `alternative`)   )");
129
                                $this->db_table_exists = true;
130
                        } else if (OIDplus::db()->getSlang()->id() == 'mssql') {
131
                                // We use nvarchar(225) instead of varchar(255), see https://github.com/frdl/oidplus-plugin-alternate-id-tracking/issues/18
132
                                // Unfortunately, we cannot use nvarchar(255), because we need two of them for the primary key, and an index must not be greater than 900 bytes in SQL Server.
133
                                // Therefore we can only use 225 Unicode characters instead of 255.
134
                                // It is very unlikely that someone has such giant identifiers. But if they do, then saveAltIdsForQuery() will reject the INSERT commands to avoid that an SQL Exception is thrown.
135
                                OIDplus::db()->query("CREATE TABLE ###altids ( [origin] nvarchar(225) NOT NULL, [alternative] nvarchar(225) NOT NULL, CONSTRAINT [PK_###altids] PRIMARY KEY CLUSTERED( [origin] ASC, [alternative] ASC ) )");
136
                                $this->db_table_exists = true;
137
                        } else if (OIDplus::db()->getSlang()->id() == 'oracle') {
138
                                // TODO: Implement Table Creation for this DBMS (see CREATE TABLE syntax at plugins/viathinksoft/sqlSlang/oracle/sql/*.sql)
139
                                $this->db_table_exists = false;
140
                        } else if (OIDplus::db()->getSlang()->id() == 'pgsql') {
141
                                // TODO: Implement Table Creation for this DBMS (see CREATE TABLE syntax at plugins/viathinksoft/sqlSlang/pgsql/sql/*.sql)
142
                                $this->db_table_exists = false;
143
                        } else if (OIDplus::db()->getSlang()->id() == 'access') {
144
                                // TODO: Implement Table Creation for this DBMS (see CREATE TABLE syntax at plugins/viathinksoft/sqlSlang/access/sql/*.sql)
145
                                $this->db_table_exists = false;
146
                        } else if (OIDplus::db()->getSlang()->id() == 'sqlite') {
147
                                // TODO: Implement Table Creation for this DBMS (see CREATE TABLE syntax at plugins/viathinksoft/sqlSlang/sqlite/sql/*.sql)
148
                                $this->db_table_exists = false;
149
                        } else if (OIDplus::db()->getSlang()->id() == 'firebird') {
150
                                // TODO: Implement Table Creation for this DBMS (see CREATE TABLE syntax at plugins/viathinksoft/sqlSlang/firebird/sql/*.sql)
151
                                $this->db_table_exists = false;
152
                        } else {
153
                                // DBMS not supported
154
                                $this->db_table_exists = false;
990 daniel-mar 155
                        }
1463 daniel-mar 156
                } else {
157
                        $this->db_table_exists = true;
990 daniel-mar 158
                }
159
 
1463 daniel-mar 160
                // Whenever a user visits a page, we need to update our cache, so that reverse-lookups are possible later
161
                // TODO! Dirty hack. We need a cleaner solution...
162
                if (isset($_REQUEST['goto'])) $this->saveAltIdsForQuery($_REQUEST['goto']); // => solve using implementing gui()?
163
                if (isset($_REQUEST['query'])) $this->saveAltIdsForQuery($_REQUEST['query']); // for webwhois.php?query=... and rdap.php?query=...
164
                if (isset($_REQUEST['id'])) $this->saveAltIdsForQuery($_REQUEST['id']); // => solve using implementing action()?
165
        }
990 daniel-mar 166
 
1463 daniel-mar 167
        // TODO: call this via cronjob  https://github.com/frdl/oidplus-plugin-alternate-id-tracking/issues/20
168
        public function renewAll() {
169
                if (!$this->db_table_exists) return;
990 daniel-mar 170
 
1463 daniel-mar 171
                OIDplus::db()->query("DELETE FROM ###altids");
172
                $resQ = OIDplus::db()->query("SELECT * FROM ###objects");
173
                while ($row = $resQ->fetch_array()) {
174
                        $this->saveAltIdsForQuery($row['id']);
990 daniel-mar 175
                }
1463 daniel-mar 176
        }
990 daniel-mar 177
 
1463 daniel-mar 178
        protected function saveAltIdsForQuery(string $id){
179
                if (!$this->db_table_exists) return;
990 daniel-mar 180
 
1463 daniel-mar 181
                $obj = OIDplusObject::parse($id);
182
                if (!$obj) return; // e.g. if plugin is disabled
183
                $ary = $obj->getAltIds();
184
                $origin = $obj->nodeId(true);
990 daniel-mar 185
 
1463 daniel-mar 186
                OIDplus::db()->query("DELETE FROM ###altids WHERE origin = ?", [$id]);
990 daniel-mar 187
 
1463 daniel-mar 188
                // Why prefiltering? Consider the following testcase:
189
                // "oid:1.3.6.1.4.1.37553.8.8.2" defines alt ID "mac:63-CF-E4-AE-C5-66" which is NOT canonized (otherwise it would not look good)!
190
                // You must be able to enter "mac:63-CF-E4-AE-C5-66" in the search box, which gets canonized
191
                // to mac:63CFE4AEC566 and must be resolved to "oid:1.3.6.1.4.1.37553.8.8.2" by this plugin.
192
                // Therefore we use self::special_in_array().
193
                // However, it is mandatory, that previously saveAltIdsForQuery("oid:1.3.6.1.4.1.37553.8.8.2") was called once!
194
                // Please also note that the "weid:" to "oid:" converting is handled by prefilterQuery(), but only if the OID plugin is installed.
195
                $origin_prefiltered = OIDplus::prefilterQuery($origin, false);
196
                if($origin_prefiltered !== $origin){
197
                        $ok = true;
198
                        if (OIDplus::db()->getSlang()->id() == 'mssql') {
199
                                // Explanation: See comment in the init() method.
200
                                if ((strlen($origin) > 225) || (strlen($origin_prefiltered) > 225)) $ok = false;
201
                        }
202
                        if ($ok) {
203
                                try {
204
                                        OIDplus::db()->query("INSERT INTO ###altids (origin, alternative) VALUES (?,?);", [$origin, $origin_prefiltered]);
205
                                } catch (\Exception $e) {
206
                                        // There could be a Primary Key collission if this method is called simultaneously at the same moment
207
                                        // Ignore it. The last caller will eventually execute all INSERTs after its call to DELETE.
208
                                }
209
                        }
210
                }
990 daniel-mar 211
 
1463 daniel-mar 212
                foreach ($ary as $a) {
213
                        $alternative = $a->getNamespace() . ':' . $a->getId();
214
                        $ok = true;
215
                        if (OIDplus::db()->getSlang()->id() == 'mssql') {
216
                                // Explanation: See comment in the init() method.
217
                                if ((strlen($origin) > 225) || (strlen($alternative) > 225)) $ok = false;
218
                        }
219
                        if ($ok) {
220
                                try {
221
                                        OIDplus::db()->query("INSERT INTO ###altids (origin, alternative) VALUES (?,?);", [$origin, $alternative]);
222
                                } catch (\Exception $e) {
223
                                        // There could be a Primary Key collission if this method is called simultaneously at the same moment
224
                                        // Ignore it. The last caller will eventually execute all INSERTs after its call to DELETE.
225
                                }
226
                        }
227
 
228
                        $alternative_prefiltered = OIDplus::prefilterQuery($alternative, false);
229
                        if($alternative_prefiltered !== $alternative){
230
                                $ok = true;
231
                                if (OIDplus::db()->getSlang()->id() == 'mssql') {
232
                                        // Explanation: See comment in the init() method.
233
                                        if ((strlen($origin) > 225) || (strlen($alternative_prefiltered) > 225)) $ok = false;
234
                                }
235
                                if ($ok) {
236
                                        try {
237
                                                OIDplus::db()->query("INSERT INTO ###altids (origin, alternative) VALUES (?,?);", [$origin, $alternative_prefiltered]);
238
                                        } catch (\Exception $e) {
239
                                                // There could be a Primary Key collission if this method is called simultaneously at the same moment
240
                                                // Ignore it. The last caller will eventually execute all INSERTs after its call to DELETE.
241
                                        }
242
                                }
243
                        }
1449 daniel-mar 244
                }
245
        }
246
 
247
        /**
1463 daniel-mar 248
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_7
1130 daniel-mar 249
         * @param string $id
1463 daniel-mar 250
         * @return array|string[]
251
         * @throws \ReflectionException
252
         * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
1116 daniel-mar 253
         * @throws \ViaThinkSoft\OIDplus\OIDplusException
254
         */
1463 daniel-mar 255
        public function getAlternativesForQuery(string $id): array {
256
                if (!$this->db_table_exists) return [];
990 daniel-mar 257
 
1463 daniel-mar 258
                $id_prefiltered = OIDplus::prefilterQuery($id, false);
990 daniel-mar 259
 
260
                $res = [
261
                        $id,
1463 daniel-mar 262
                        $id_prefiltered
990 daniel-mar 263
                ];
264
 
1463 daniel-mar 265
                $resQ = OIDplus::db()->query("SELECT origin, alternative FROM ###altids WHERE origin = ? OR alternative = ? OR origin = ? OR alternative = ?", [$res[0],$res[0],$res[1],$res[1]]);
266
                while ($row = $resQ->fetch_array()) {
267
                        if(!in_array($row['origin'], $res)){
268
                                $res[]=$row['origin'];
990 daniel-mar 269
                        }
1463 daniel-mar 270
                        if(!in_array($row['alternative'], $res)){
271
                                $res[]=$row['alternative'];
272
                        }
990 daniel-mar 273
                }
274
 
1463 daniel-mar 275
                return array_unique($res);
276
        }
990 daniel-mar 277
 
1463 daniel-mar 278
        /**
279
         * @param string $id
280
         * @param array $out
281
         * @param bool $handled
282
         * @return void
283
         */
284
        public function gui(string $id, array &$out, bool &$handled) {
285
                // $this->saveAltIdsForQuery($id);
286
        }
990 daniel-mar 287
 
1463 daniel-mar 288
        /**
289
         * @param array $out
290
         * @return void
291
         */
292
        public function publicSitemap(array &$out) {
293
 
990 daniel-mar 294
        }
295
 
1116 daniel-mar 296
        /**
1463 daniel-mar 297
         * @param array $json
298
         * @param string|null $ra_email
299
         * @param bool $nonjs
300
         * @param string $req_goto
301
         * @return bool
302
         */
303
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
304
                return false;
305
        }
306
 
307
        /**
1116 daniel-mar 308
         * @param string $request
309
         * @return array|false
310
         */
311
        public function tree_search(string $request) {
990 daniel-mar 312
                return false;
313
        }
314
 
1116 daniel-mar 315
        /**
1130 daniel-mar 316
         * @param string $id
1116 daniel-mar 317
         * @return false|mixed|string
318
         * @throws \ViaThinkSoft\OIDplus\OIDplusException
319
         */
1130 daniel-mar 320
        public function getCanonical(string $id){
990 daniel-mar 321
                foreach($this->getAlternativesForQuery($id) as $alt){
1008 daniel-mar 322
                        if (strpos($alt,':') !== false) {
323
                                list($ns, $altIdRaw) = explode(':', $alt, 2);
324
                                if($ns === 'oid'){
325
                                        return $alt;
326
                                }
990 daniel-mar 327
                        }
328
                }
329
                return false;
330
        }
331
 
1116 daniel-mar 332
        /**
1131 daniel-mar 333
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_4
1130 daniel-mar 334
         * @param string $id
335
         * @param array $out
1116 daniel-mar 336
         * @return void
337
         * @throws \ViaThinkSoft\OIDplus\OIDplusException
338
         */
1130 daniel-mar 339
        public function whoisObjectAttributes(string $id, array &$out) {
990 daniel-mar 340
                $xmlns = 'oidplus-frdlweb-altids-plugin';
341
                $xmlschema = 'urn:oid:1.3.6.1.4.1.37553.8.1.8.8.53354196964.641310544.1714020422';
1375 daniel-mar 342
                $xmlschemauri = OIDplus::webpath(__DIR__.'/altids.xsd',OIDplus::PATH_ABSOLUTE_CANONICAL);
990 daniel-mar 343
 
344
                $handleShown = false;
345
                $canonicalShown = false;
346
 
1135 daniel-mar 347
                $out1 = array();
348
                $out2 = array();
990 daniel-mar 349
 
1463 daniel-mar 350
                //$tmp = $this->getAlternativesForQuery($id);
351
                $obj = OIDplusObject::parse($id);
352
                $tmp = [
353
                        $this->getCanonical($id),
354
                ];
355
                foreach ($obj->getAltIds() as $altId) {
356
                        $tmp[] = $altId->getNamespace().':'.$altId->getId();
357
                }
358
 
1135 daniel-mar 359
                sort($tmp); // DM 26.03.2023 : Added sorting (intended to sort "alternate-identifier")
360
                foreach($tmp as $alt) {
1040 daniel-mar 361
                        if (strpos($alt,':') === false) continue;
1008 daniel-mar 362
 
990 daniel-mar 363
                        list($ns, $altIdRaw) = explode(':', $alt, 2);
364
 
1445 daniel-mar 365
                        if (($canonicalShown === false) && ($ns === 'oid')) {
990 daniel-mar 366
                                $canonicalShown=true;
367
 
1135 daniel-mar 368
                                $out1[] = [
990 daniel-mar 369
                                        'xmlns' => $xmlns,
370
                                        'xmlschema' => $xmlschema,
371
                                        'xmlschemauri' => $xmlschemauri,
372
                                        'name' => 'canonical-identifier',
373
                                        'value' => $ns.':'.$altIdRaw,
374
                                ];
375
 
376
                        }
377
 
378
                        if (($handleShown === false) && ($alt === $id)) {
379
                                $handleShown=true;
380
 
1135 daniel-mar 381
                                $out1[] = [
990 daniel-mar 382
                                        'xmlns' => $xmlns,
383
                                        'xmlschema' => $xmlschema,
384
                                        'xmlschemauri' => $xmlschemauri,
385
                                        'name' => 'handle-identifier',
386
                                        'value' => $alt,
387
                                ];
388
 
389
                        }
390
 
1135 daniel-mar 391
                        if ($alt !== $id) { // DM 26.03.2023 : Added condition that alternate must not be the id itself
392
                                $out2[] = [
393
                                        'xmlns' => $xmlns,
394
                                        'xmlschema' => $xmlschema,
395
                                        'xmlschemauri' => $xmlschemauri,
396
                                        'name' => 'alternate-identifier',
397
                                        'value' => $ns.':'.$altIdRaw,
398
                                ];
399
                        }
990 daniel-mar 400
 
401
                }
1135 daniel-mar 402
 
403
                // DM 26.03.2023 : Added this
404
                $out = array_merge($out, $out1); // handle-identifier and canonical-identifier
405
                $out = array_merge($out, $out2); // alternate-identifier
406
 
990 daniel-mar 407
        }
408
 
1116 daniel-mar 409
        /**
1131 daniel-mar 410
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_4
1130 daniel-mar 411
         * @param string $email
412
         * @param array $out
1116 daniel-mar 413
         * @return void
414
         */
1130 daniel-mar 415
        public function whoisRaAttributes(string $email, array &$out) {
1116 daniel-mar 416
 
990 daniel-mar 417
        }
418
 
1463 daniel-mar 419
        /**
420
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
421
         * @param string|null $user
422
         * @return array
423
         * @throws \ViaThinkSoft\OIDplus\OIDplusException
424
         */
425
        public function getNotifications(string $user=null): array {
426
                $notifications = array();
427
                if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
428
                        if (!$this->db_table_exists) {
429
                                $title = _L('Alt ID Plugin');
430
                                $notifications[] = new OIDplusNotification('ERR', _L('OIDplus plugin "%1" is enabled, but it does not know how to create its database tables to this DBMS. Therefore the plugin does not work.', htmlentities($title)));
431
                        }
432
                }
433
                return $notifications;
434
        }
435
 
436
}