Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
2 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;
511 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
 
730 daniel-mar 26
abstract class OIDplusObject extends OIDplusBaseClass {
1130 daniel-mar 27
 
28
        /**
29
         *
30
         */
1323 daniel-mar 31
        //const UUID_NAMEBASED_NS_OidPlusMisc = 'ad1654e6-7e15-11e4-9ef6-78e3b5fc7f22';
150 daniel-mar 32
 
1116 daniel-mar 33
        /**
34
         * Please overwrite this function!
35
         * @param string $node_id
36
         * @return OIDplusObject|null
37
         */
38
        public static function parse(string $node_id)/*: ?OIDplusObject*/ {
227 daniel-mar 39
                foreach (OIDplus::getEnabledObjectTypes() as $ot) {
954 daniel-mar 40
                        try {
956 daniel-mar 41
                                $good = false;
1050 daniel-mar 42
                                if (get_parent_class($ot) == OIDplusObject::class) {
956 daniel-mar 43
                                        $reflector = new \ReflectionMethod($ot, 'parse');
44
                                        $isImplemented = ($reflector->getDeclaringClass()->getName() === $ot);
45
                                        if ($isImplemented) { // avoid endless loop if parse is not overriden
46
                                                $good = true;
47
                                        }
48
                                }
49
                                // We need to do the workaround with "$good", otherwise PHPstan shows
50
                                // "Call to an undefined static method object::parse()"
51
                                if ($good && $obj = $ot::parse($node_id)) return $obj;
1050 daniel-mar 52
                        } catch (\Exception $e) {}
2 daniel-mar 53
                }
54
                return null;
55
        }
56
 
1116 daniel-mar 57
        /**
58
         * @return OIDplusAltId[]
59
         * @throws OIDplusException
60
         */
61
        public function getAltIds(): array {
193 daniel-mar 62
                if ($this->isRoot()) return array();
63
 
64
                $ids = array();
1078 daniel-mar 65
 
1327 daniel-mar 66
                // Create Information Object OID/AID/UUID
67
                // ... but not for OIDs below oid:1.3.6.1.4.1.37476.30.9, because these are the definition of these Information Object AID/OID/UUID (which will be decoded in the OID object type plugin)
68
                if (!str_starts_with($this->nodeId(true), 'oid:1.3.6.1.4.1.37476.30.9.')) {
69
                        // Creates an OIDplus-Hash-OID
70
                        // ... exclude OIDs, because an OID is already an OID
71
                        if ($this->ns() != 'oid') {
72
                                $sid = OIDplus::getSystemId(true);
73
                                if (!empty($sid)) {
74
                                        $ns_oid = $this->getPlugin()->getManifest()->getOid();
75
                                        $hash_payload = $ns_oid.':'.$this->nodeId(false);
76
                                        $oid = $sid . '.' . smallhash($hash_payload);
77
                                        $ids[] = new OIDplusAltId('oid', $oid, _L('OIDplus Information Object OID'));
78
                                }
193 daniel-mar 79
                        }
929 daniel-mar 80
 
1327 daniel-mar 81
                        // Make a OIDplus-UUID, but...
82
                        // ... exclude GUID, because a GUID is already a GUID
83
                        // ... exclude OIDs which are 2.25, because these are basically GUIDs (but 2nd, 3rd, 4th, ... level is OK)
84
                        // Previously, we excluded OID, because an OID already has a record UUID_NAMEBASED_NS_OID (defined by IETF) set by class OIDplusOid
85
                        if (($this->ns() != 'guid') && ($this->ns() != 'oid' || $this->one_up()->nodeId(true) != 'oid:2.25') /*&& ($this->ns() != 'oid')*/) {
86
                                // Obsolete custom namespace for UUIDv3 and UUIDv5:
87
                                //$ids[] = new OIDplusAltId('guid', gen_uuid_md5_namebased(self::UUID_NAMEBASED_NS_OidPlusMisc, $this->nodeId()), _L('Name based version 3 / MD5 UUID with namespace %1','UUID_NAMEBASED_NS_OidPlusMisc'));
88
                                //$ids[] = new OIDplusAltId('guid', gen_uuid_sha1_namebased(self::UUID_NAMEBASED_NS_OidPlusMisc, $this->nodeId()), _L('Name based version 5 / SHA1 UUID with namespace %1','UUID_NAMEBASED_NS_OidPlusMisc'));
89
                                // New custom UUIDv8:
90
                                $sysid = OIDplus::getSystemId(false);
91
                                $sysid_int = $sysid ? $sysid : 0;
92
                                $unix_ts = $this->getCreatedTime() ? strtotime($this->getCreatedTime()) : 0;
1078 daniel-mar 93
                                $ns_oid = $this->getPlugin()->getManifest()->getOid();
1327 daniel-mar 94
                                $obj_name = $this->nodeId(false);
95
                                $ids[] = new OIDplusAltId('guid',
96
                                        gen_uuid_v8(
97
                                                dechex($sysid_int),
1350 daniel-mar 98
                                                dechex((int)round($unix_ts/60/60/24)),
1327 daniel-mar 99
                                                dechex(0),
100
                                                sha1($ns_oid), // Note: No 14bit collission between 1.3.6.1.4.1.37476.2.5.2.4.8.[0-185]
101
                                                sha1($obj_name)
102
                                        ),
1329 daniel-mar 103
                                        _L('OIDplus Information Object Custom UUID (RFC4122bis)'),
104
                                        '',
105
                                        'https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md'
1327 daniel-mar 106
                                        );
929 daniel-mar 107
                        }
1327 daniel-mar 108
 
109
                        // Make a AID based on ViaThinkSoft schema
110
                        // ... exclude AIDs, because an AID is already an AID
111
                        if ($this->ns() != 'aid') {
112
                                $sid = OIDplus::getSystemId(false);
113
                                if ($sid !== false) {
114
                                        $ns_oid = $this->getPlugin()->getManifest()->getOid();
115
                                        $hash_payload = $ns_oid.':'.$this->nodeId(false);
116
                                        $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
117
                                        $obj_hex = strtoupper(str_pad(dechex(smallhash($hash_payload)),8,'0',STR_PAD_LEFT));
118
                                        $aid = 'D276000186B20005'.$sid_hex.$obj_hex;
1329 daniel-mar 119
                                        $ids[] = new OIDplusAltId('aid', $aid,
1330 daniel-mar 120
                                                _L('OIDplus Information Object Application Identifier (ISO/IEC 7816)'),
121
                                                ' ('._L('No PIX allowed').')',
122
                                                'https://oidplus.viathinksoft.com/oidplus/?goto=aid%3AD276000186B20005');
1327 daniel-mar 123
                                }
124
                        }
1330 daniel-mar 125
 
126
                        // Make a MAC based on AAI (not 100% worldwide unique!)
127
                        // ... exclude MACs, because an MAC is already a MAC
128
                        if ($this->ns() != 'mac') {
129
                                $ns_oid = $this->getPlugin()->getManifest()->getOid();
130
                                $obj_name = $this->nodeId(false);
131
                                $mac = strtoupper(substr(sha1($ns_oid.':'.$obj_name),-12));
132
                                $mac = rtrim(chunk_split($mac, 2, '-'),'-');
133
 
134
                                $mac[1] = '2'; // 2=AAI Unicast
135
                                $ids[] = new OIDplusAltId('mac', $mac, _L('OIDplus Information Object MAC address, Unicast (AAI)'));
136
 
137
                                $mac[1] = '3'; // 3=AAI Multicast
138
                                $ids[] = new OIDplusAltId('mac', $mac, _L('OIDplus Information Object MAC address, Multicast (AAI)'));
139
                        }
1397 daniel-mar 140
 
141
                        // Make a DN based on DN
142
                        // ... exclude DN, because an DN is already a DN
143
                        if ($this->ns() != 'x500dn') {
144
                                $sysid = OIDplus::getSystemId(false);
145
                                if ($sysid !== false) {
146
                                        $ns_oid = $this->getPlugin()->getManifest()->getOid();
147
                                        $hash_payload = $ns_oid.':'.$this->nodeId(false);
148
                                        $objhash = smallhash($hash_payload);
149
 
150
                                        $oid_at_sysid = '1.3.6.1.4.1.37476.2.5.2.9.4.1';
151
                                        $oid_at_objhash = '1.3.6.1.4.1.37476.2.5.2.9.4.2';
152
                                        $dn = '/dc=com/dc=example/cn=oidplus/'."\n".
153
                                        $oid_at_sysid.'='.$sysid.'/'."\n".
154
                                        $oid_at_objhash.'='.$objhash;
155
 
156
                                        $ids[] = new OIDplusAltId('x500dn', $dn, _L('OIDplus Information Object X.500 DN'));
157
                                }
158
                        }
193 daniel-mar 159
                }
1078 daniel-mar 160
 
193 daniel-mar 161
                return $ids;
83 daniel-mar 162
        }
163
 
1116 daniel-mar 164
        /**
165
         * @return string
166
         */
167
        public abstract static function objectTypeTitle(): string;
2 daniel-mar 168
 
1116 daniel-mar 169
        /**
170
         * @return string
171
         */
172
        public abstract static function objectTypeTitleShort(): string;
2 daniel-mar 173
 
1116 daniel-mar 174
        /**
175
         * @return OIDplusObjectTypePlugin|null
176
         */
817 daniel-mar 177
        public function getPlugin()/*: ?OIDplusObjectTypePlugin */ {
178
                $plugins = OIDplus::getObjectTypePlugins();
179
                foreach ($plugins as $plugin) {
1116 daniel-mar 180
                        if (get_class($this) == $plugin::getObjectTypeClassName()) {
817 daniel-mar 181
                                return $plugin;
182
                        }
183
                }
1116 daniel-mar 184
                return null;
817 daniel-mar 185
        }
186
 
1116 daniel-mar 187
        /**
188
         * @return string
189
         */
190
        public abstract static function ns(): string;
2 daniel-mar 191
 
1116 daniel-mar 192
        /**
193
         * @return string
194
         */
195
        public abstract static function root(): string;
2 daniel-mar 196
 
1116 daniel-mar 197
        /**
198
         * @return bool
199
         */
200
        public abstract function isRoot(): bool;
2 daniel-mar 201
 
1116 daniel-mar 202
        /**
203
         * @param bool $with_ns
204
         * @return string
205
         */
206
        public abstract function nodeId(bool $with_ns=true): string;
2 daniel-mar 207
 
1116 daniel-mar 208
        /**
209
         * @param string $str
210
         * @return string mixed
211
         * @throws OIDplusException
212
         */
213
        public abstract function addString(string $str): string;
2 daniel-mar 214
 
1116 daniel-mar 215
        /**
216
         * @param OIDplusObject $parent
217
         * @return string
218
         */
219
        public abstract function crudShowId(OIDplusObject $parent): string;
2 daniel-mar 220
 
1116 daniel-mar 221
        /**
222
         * @return string
223
         */
224
        public function crudInsertPrefix(): string {
707 daniel-mar 225
                return '';
226
        }
2 daniel-mar 227
 
1116 daniel-mar 228
        /**
229
         * @return string
230
         */
231
        public function crudInsertSuffix(): string {
707 daniel-mar 232
                return '';
233
        }
234
 
1116 daniel-mar 235
        /**
236
         * @param OIDplusObject|null $parent
237
         * @return string
238
         */
239
        public abstract function jsTreeNodeName(OIDplusObject $parent = null): string;
2 daniel-mar 240
 
1116 daniel-mar 241
        /**
242
         * @return string
243
         */
244
        public abstract function defaultTitle(): string;
2 daniel-mar 245
 
1116 daniel-mar 246
        /**
247
         * @return bool
248
         */
249
        public abstract function isLeafNode(): bool;
16 daniel-mar 250
 
1116 daniel-mar 251
        /**
252
         * @param string $title
253
         * @param string $content
254
         * @param string $icon
255
         * @return void
256
         */
257
        public abstract function getContentPage(string &$title, string &$content, string &$icon);
2 daniel-mar 258
 
1116 daniel-mar 259
        /**
260
         * @param OIDplusRA|string|null $ra
261
         * @return array
262
         * @throws OIDplusConfigInitializationException
263
         * @throws OIDplusException
264
         */
265
        public static function getRaRoots($ra=null) : array{
266
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 267
 
27 daniel-mar 268
                $out = array();
150 daniel-mar 269
 
261 daniel-mar 270
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
1116 daniel-mar 271
                        if (!$ra) {
1171 daniel-mar 272
                                $res = OIDplus::db()->query("select oChild.id as child_id, oChild.ra_email as child_mail, oParent.ra_email as parent_mail from ###objects as oChild ".
1148 daniel-mar 273
                                                            "left join ###objects as oParent on oChild.parent = oParent.id");
1156 daniel-mar 274
                                $res->naturalSortByField('oChild.id');
236 daniel-mar 275
                                while ($row = $res->fetch_array()) {
549 daniel-mar 276
                                        if (!OIDplus::authUtils()->isRaLoggedIn($row['parent_mail']) && OIDplus::authUtils()->isRaLoggedIn($row['child_mail'])) {
1171 daniel-mar 277
                                                $x = self::parse($row['child_id']); // can be NULL if namespace was disabled
150 daniel-mar 278
                                                if ($x) $out[] = $x;
279
                                        }
280
                                }
281
                        } else {
1171 daniel-mar 282
                                $res = OIDplus::db()->query("select oChild.id as child_id from ###objects as oChild ".
261 daniel-mar 283
                                                            "left join ###objects as oParent on oChild.parent = oParent.id ".
433 daniel-mar 284
                                                            "where (".OIDplus::db()->getSlang()->isNullFunction('oParent.ra_email',"''")." <> ? and ".
285
                                                            OIDplus::db()->getSlang()->isNullFunction('oChild.ra_email',"''")." = ?) or ".
1148 daniel-mar 286
                                                            "      (oParent.ra_email is null and ".OIDplus::db()->getSlang()->isNullFunction('oChild.ra_email',"''")." = ?) ",
287
                                                            array($ra, $ra, $ra));
1156 daniel-mar 288
                                $res->naturalSortByField('oChild.id');
236 daniel-mar 289
                                while ($row = $res->fetch_array()) {
1171 daniel-mar 290
                                        $x = self::parse($row['child_id']); // can be NULL if namespace was disabled
1028 daniel-mar 291
                                        if ($x) $out[] = $x;
27 daniel-mar 292
                                }
2 daniel-mar 293
                        }
294
                } else {
1116 daniel-mar 295
                        if (!$ra) {
415 daniel-mar 296
                                $ra_mails_to_check = OIDplus::authUtils()->loggedInRaList();
150 daniel-mar 297
                                if (count($ra_mails_to_check) == 0) return $out;
298
                        } else {
1116 daniel-mar 299
                                $ra_mails_to_check = array($ra);
2 daniel-mar 300
                        }
150 daniel-mar 301
 
302
                        self::buildObjectInformationCache();
303
 
304
                        foreach ($ra_mails_to_check as $check_ra_mail) {
193 daniel-mar 305
                                $out_part = array();
150 daniel-mar 306
 
975 daniel-mar 307
                                foreach (self::$object_info_cache as $id => $cacheitem) {
1028 daniel-mar 308
                                        if ($cacheitem[self::CACHE_RA_EMAIL] == $check_ra_mail) {
309
                                                $parent = $cacheitem[self::CACHE_PARENT];
310
                                                if (!isset(self::$object_info_cache[$parent]) || (self::$object_info_cache[$parent][self::CACHE_RA_EMAIL] != $check_ra_mail)) {
311
                                                        $out_part[] = $id;
150 daniel-mar 312
                                                }
313
                                        }
314
                                }
315
 
316
                                natsort($out_part);
317
 
318
                                foreach ($out_part as $id) {
319
                                        $obj = self::parse($id);
320
                                        if ($obj) $out[] = $obj;
321
                                }
322
                        }
2 daniel-mar 323
                }
150 daniel-mar 324
 
2 daniel-mar 325
                return $out;
326
        }
327
 
1116 daniel-mar 328
        /**
329
         * @return array
330
         * @throws OIDplusException
331
         */
332
        public static function getAllNonConfidential(): array {
150 daniel-mar 333
                $out = array();
334
 
261 daniel-mar 335
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
1148 daniel-mar 336
                        $res = OIDplus::db()->query("select id from ###objects where confidential = ?", array(false));
1156 daniel-mar 337
                        $res->naturalSortByField('id');
236 daniel-mar 338
                        while ($row = $res->fetch_array()) {
150 daniel-mar 339
                                $obj = self::parse($row['id']); // will be NULL if the object type is not registered
169 daniel-mar 340
                                if ($obj && (!$obj->isConfidential())) {
150 daniel-mar 341
                                        $out[] = $row['id'];
342
                                }
2 daniel-mar 343
                        }
344
                } else {
150 daniel-mar 345
                        self::buildObjectInformationCache();
2 daniel-mar 346
 
975 daniel-mar 347
                        foreach (self::$object_info_cache as $id => $cacheitem) {
348
                                $confidential = $cacheitem[self::CACHE_CONFIDENTIAL];
150 daniel-mar 349
                                if (!$confidential) {
350
                                        $obj = self::parse($id); // will be NULL if the object type is not registered
169 daniel-mar 351
                                        if ($obj && (!$obj->isConfidential())) {
150 daniel-mar 352
                                                $out[] = $id;
353
                                        }
354
                                }
2 daniel-mar 355
                        }
356
                }
357
 
358
                return $out;
359
        }
360
 
1116 daniel-mar 361
        /**
362
         * @return bool
363
         * @throws OIDplusException
364
         */
365
        public function isConfidential(): bool {
261 daniel-mar 366
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
426 daniel-mar 367
                        //static $confidential_cache = array();
150 daniel-mar 368
                        $curid = $this->nodeId();
426 daniel-mar 369
                        //$orig_curid = $curid;
370
                        //if (isset($confidential_cache[$curid])) return $confidential_cache[$curid];
150 daniel-mar 371
                        // Recursively search for the confidential flag in the parents
790 daniel-mar 372
                        while (($res = OIDplus::db()->query("select parent, confidential from ###objects where id = ?", array($curid)))->any()) {
236 daniel-mar 373
                                $row = $res->fetch_array();
150 daniel-mar 374
                                if ($row['confidential']) {
426 daniel-mar 375
                                        //$confidential_cache[$curid] = true;
376
                                        //$confidential_cache[$orig_curid] = true;
150 daniel-mar 377
                                        return true;
378
                                } else {
426 daniel-mar 379
                                        //$confidential_cache[$curid] = false;
150 daniel-mar 380
                                }
381
                                $curid = $row['parent'];
426 daniel-mar 382
                                //if (isset($confidential_cache[$curid])) {
383
                                        //$confidential_cache[$orig_curid] = $confidential_cache[$curid];
384
                                        //return $confidential_cache[$curid];
385
                                //}
150 daniel-mar 386
                        }
387
 
426 daniel-mar 388
                        //$confidential_cache[$orig_curid] = false;
150 daniel-mar 389
                        return false;
390
                } else {
391
                        self::buildObjectInformationCache();
392
 
393
                        $curid = $this->nodeId();
394
                        // Recursively search for the confidential flag in the parents
169 daniel-mar 395
                        while (isset(self::$object_info_cache[$curid])) {
150 daniel-mar 396
                                if (self::$object_info_cache[$curid][self::CACHE_CONFIDENTIAL]) return true;
397
                                $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
398
                        }
399
                        return false;
2 daniel-mar 400
                }
401
        }
402
 
1116 daniel-mar 403
        /**
404
         * @param OIDplusObject $obj
405
         * @return bool
406
         * @throws OIDplusException
407
         */
408
        public function isChildOf(OIDplusObject $obj): bool {
261 daniel-mar 409
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
150 daniel-mar 410
                        $curid = $this->nodeId();
790 daniel-mar 411
                        while (($res = OIDplus::db()->query("select parent from ###objects where id = ?", array($curid)))->any()) {
236 daniel-mar 412
                                $row = $res->fetch_array();
150 daniel-mar 413
                                if ($curid == $obj->nodeId()) return true;
414
                                $curid = $row['parent'];
415
                        }
416
                        return false;
417
                } else {
418
                        self::buildObjectInformationCache();
419
 
420
                        $curid = $this->nodeId();
169 daniel-mar 421
                        while (isset(self::$object_info_cache[$curid])) {
150 daniel-mar 422
                                if ($curid == $obj->nodeId()) return true;
423
                                $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
424
                        }
425
                        return false;
2 daniel-mar 426
                }
150 daniel-mar 427
        }
2 daniel-mar 428
 
1116 daniel-mar 429
        /**
430
         * @return array
431
         * @throws OIDplusException
432
         */
433
        public function getChildren(): array {
150 daniel-mar 434
                $out = array();
261 daniel-mar 435
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
436
                        $res = OIDplus::db()->query("select id from ###objects where parent = ?", array($this->nodeId()));
236 daniel-mar 437
                        while ($row = $res->fetch_array()) {
150 daniel-mar 438
                                $obj = self::parse($row['id']);
439
                                if (!$obj) continue;
440
                                $out[] = $obj;
441
                        }
442
                } else {
443
                        self::buildObjectInformationCache();
444
 
975 daniel-mar 445
                        foreach (self::$object_info_cache as $id => $cacheitem) {
446
                                $parent = $cacheitem[self::CACHE_PARENT];
150 daniel-mar 447
                                if ($parent == $this->nodeId()) {
448
                                        $obj = self::parse($id);
449
                                        if (!$obj) continue;
450
                                        $out[] = $obj;
451
                                }
452
                        }
453
                }
454
                return $out;
2 daniel-mar 455
        }
456
 
1116 daniel-mar 457
        /**
1137 daniel-mar 458
         * @return OIDplusRA|null
1116 daniel-mar 459
         * @throws OIDplusException
460
         */
1137 daniel-mar 461
        public function getRa()/*: ?OIDplusRA*/ {
462
                $ra = $this->getRaMail();
463
                return $ra ? new OIDplusRA($ra) : null;
115 daniel-mar 464
        }
465
 
1116 daniel-mar 466
        /**
467
         * @param OIDplusRA|string|null $ra
468
         * @return bool
469
         * @throws OIDplusConfigInitializationException
470
         * @throws OIDplusException
471
         */
472
        public function userHasReadRights($ra=null): bool {
473
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 474
 
2 daniel-mar 475
                // If it is not confidential, everybody can read/see it.
416 daniel-mar 476
                // Note: This also checks if superior OIDs are confidential.
2 daniel-mar 477
                if (!$this->isConfidential()) return true;
478
 
1116 daniel-mar 479
                if (!$ra) {
416 daniel-mar 480
                        // Admin may do everything
549 daniel-mar 481
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
416 daniel-mar 482
 
483
                        // If the RA is logged in, then they can see the OID.
1137 daniel-mar 484
                        $ownRa = $this->getRaMail();
485
                        if ($ownRa && OIDplus::authUtils()->isRaLoggedIn($ownRa)) return true;
2 daniel-mar 486
                } else {
416 daniel-mar 487
                        // If this OID belongs to the requested RA, then they may see it.
1116 daniel-mar 488
                        if ($this->getRaMail() == $ra) return true;
2 daniel-mar 489
                }
490
 
491
                // If someone has rights to an object below our confidential node,
492
                // we let him see the confidential node,
493
                // Otherwise he could not browse through to his own node.
1116 daniel-mar 494
                $roots = $this->getRaRoots($ra);
2 daniel-mar 495
                foreach ($roots as $root) {
496
                        if ($root->isChildOf($this)) return true;
497
                }
498
 
499
                return false;
500
        }
501
 
1116 daniel-mar 502
        /**
503
         * @param array|null $row
504
         * @return string|null
505
         * @throws OIDplusException
506
         */
507
        public function getIcon(array $row=null) {
20 daniel-mar 508
                $namespace = $this->ns(); // must use $this, not self::, otherwise the virtual method will not be called
2 daniel-mar 509
 
510
                if (is_null($row)) {
150 daniel-mar 511
                        $ra_email = $this->getRaMail();
512
                } else {
513
                        $ra_email = $row['ra_email'];
2 daniel-mar 514
                }
632 daniel-mar 515
 
1127 daniel-mar 516
                // $dirs = glob(OIDplus::localpath().'plugins/'.'*'.'/objectTypes/'.$namespace.'/');
517
                // if (count($dirs) == 0) return null; // default icon (folder)
518
                // $dir = substr($dirs[0], strlen(OIDplus::localpath()));
519
                $reflection = new \ReflectionClass($this);
520
                $dir = dirname($reflection->getFilename());
521
                $dir = substr($dir, strlen(OIDplus::localpath()));
1130 daniel-mar 522
                $dir = str_replace('\\', '/', $dir);
632 daniel-mar 523
 
1124 daniel-mar 524
                if ($this->isRoot()) {
1127 daniel-mar 525
                        $icon = $dir . '/' . $this::treeIconFilename('root');
2 daniel-mar 526
                } else {
1124 daniel-mar 527
                        // We use $this:: instead of self:: , because we want to call the overridden methods
1126 daniel-mar 528
                        if ($ra_email && OIDplus::authUtils()->isRaLoggedIn($ra_email)) {
1124 daniel-mar 529
                                if ($this->isLeafNode()) {
530
                                        $icon = $dir . '/' . $this::treeIconFilename('own_leaf');
531
                                        if (!file_exists($icon)) $icon = $dir . '/' . $this::treeIconFilename('own');
532
                                } else {
533
                                        $icon = $dir . '/' . $this::treeIconFilename('own');
534
                                }
535
                        } else {
536
                                if ($this->isLeafNode()) {
537
                                        $icon = $dir . '/' . $this::treeIconFilename('general_leaf');
538
                                        if (!file_exists($icon)) $icon = $dir . '/' . $this::treeIconFilename('general');
539
                                } else {
540
                                        $icon = $dir . '/' . $this::treeIconFilename('general');
541
                                }
542
                        }
2 daniel-mar 543
                }
632 daniel-mar 544
 
545
                if (!file_exists($icon)) return null; // default icon (folder)
546
 
2 daniel-mar 547
                return $icon;
548
        }
549
 
1116 daniel-mar 550
        /**
551
         * @param string $id
552
         * @return bool
553
         * @throws OIDplusException
554
         */
555
        public static function exists(string $id): bool {
261 daniel-mar 556
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
557
                        $res = OIDplus::db()->query("select id from ###objects where id = ?", array($id));
790 daniel-mar 558
                        return $res->any();
150 daniel-mar 559
                } else {
560
                        self::buildObjectInformationCache();
561
                        return isset(self::$object_info_cache[$id]);
562
                }
12 daniel-mar 563
        }
564
 
1116 daniel-mar 565
        /**
566
         * Get parent gives the next possible parent which is EXISTING in OIDplus
567
         * It does not give the immediate parent
568
         * @return OIDplusObject|null
569
         * @throws OIDplusException
570
         */
979 daniel-mar 571
        public function getParent()/*: ?OIDplusObject*/ {
261 daniel-mar 572
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
573
                        $res = OIDplus::db()->query("select parent from ###objects where id = ?", array($this->nodeId()));
1271 daniel-mar 574
                        if ($res->any()) {
575
                                $row = $res->fetch_array();
576
                                $parent = $row['parent'];
577
                                $obj = OIDplusObject::parse($parent);
578
                                if ($obj) return $obj;
579
                        }
150 daniel-mar 580
                } else {
581
                        self::buildObjectInformationCache();
582
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
583
                                $parent = self::$object_info_cache[$this->nodeId()][self::CACHE_PARENT];
584
                                $obj = OIDplusObject::parse($parent);
585
                                if ($obj) return $obj;
586
                        }
1271 daniel-mar 587
                }
20 daniel-mar 588
 
1271 daniel-mar 589
                // If this OID does not exist, the SQL query "select parent from ..." does not work. So we try to find the next possible parent using one_up()
590
                $cur = $this->one_up();
591
                if (!$cur) return null;
592
                do {
593
                        // findFitting() checks if that OID exists
594
                        if ($fitting = self::findFitting($cur->nodeId())) return $fitting;
595
 
596
                        $prev = $cur;
597
                        $cur = $cur->one_up();
418 daniel-mar 598
                        if (!$cur) return null;
1271 daniel-mar 599
                } while ($prev->nodeId() !== $cur->nodeId());
20 daniel-mar 600
 
979 daniel-mar 601
                return null;
2 daniel-mar 602
        }
603
 
1116 daniel-mar 604
        /**
1137 daniel-mar 605
         * @return string|null
1116 daniel-mar 606
         * @throws OIDplusException
607
         */
2 daniel-mar 608
        public function getRaMail() {
261 daniel-mar 609
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
610
                        $res = OIDplus::db()->query("select ra_email from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 611
                        if (!$res->any()) return null;
236 daniel-mar 612
                        $row = $res->fetch_array();
150 daniel-mar 613
                        return $row['ra_email'];
614
                } else {
615
                        self::buildObjectInformationCache();
616
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
617
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_RA_EMAIL];
618
                        }
1137 daniel-mar 619
                        return null;
150 daniel-mar 620
                }
2 daniel-mar 621
        }
622
 
1116 daniel-mar 623
        /**
1142 daniel-mar 624
         * @return string|null
1116 daniel-mar 625
         * @throws OIDplusException
626
         */
192 daniel-mar 627
        public function getTitle() {
261 daniel-mar 628
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
629
                        $res = OIDplus::db()->query("select title from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 630
                        if (!$res->any()) return null;
236 daniel-mar 631
                        $row = $res->fetch_array();
192 daniel-mar 632
                        return $row['title'];
633
                } else {
634
                        self::buildObjectInformationCache();
635
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
636
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_TITLE];
637
                        }
1142 daniel-mar 638
                        return null;
192 daniel-mar 639
                }
640
        }
641
 
1116 daniel-mar 642
        /**
1142 daniel-mar 643
         * @return string|null
1116 daniel-mar 644
         * @throws OIDplusException
645
         */
975 daniel-mar 646
        public function getDescription() {
647
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
648
                        $res = OIDplus::db()->query("select description from ###objects where id = ?", array($this->nodeId()));
649
                        if (!$res->any()) return null;
650
                        $row = $res->fetch_array();
651
                        return $row['description'];
652
                } else {
653
                        self::buildObjectInformationCache();
654
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
655
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_DESCRIPTION];
656
                        }
1142 daniel-mar 657
                        return null;
975 daniel-mar 658
                }
659
        }
660
 
1116 daniel-mar 661
        /**
1142 daniel-mar 662
         * @return string|null
1116 daniel-mar 663
         * @throws OIDplusException
664
         */
975 daniel-mar 665
        public function getComment() {
666
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
667
                        $res = OIDplus::db()->query("select comment from ###objects where id = ?", array($this->nodeId()));
668
                        if (!$res->any()) return null;
669
                        $row = $res->fetch_array();
670
                        return $row['comment'];
671
                } else {
672
                        self::buildObjectInformationCache();
673
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
674
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_COMMENT];
675
                        }
1142 daniel-mar 676
                        return null;
975 daniel-mar 677
                }
678
        }
679
 
1116 daniel-mar 680
        /**
1142 daniel-mar 681
         * @return string|null
1116 daniel-mar 682
         * @throws OIDplusException
683
         */
975 daniel-mar 684
        public function getCreatedTime() {
685
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
686
                        $res = OIDplus::db()->query("select created from ###objects where id = ?", array($this->nodeId()));
687
                        if (!$res->any()) return null;
688
                        $row = $res->fetch_array();
689
                        return $row['created'];
690
                } else {
691
                        self::buildObjectInformationCache();
692
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
693
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_CREATED];
694
                        }
1142 daniel-mar 695
                        return null;
975 daniel-mar 696
                }
697
        }
698
 
1116 daniel-mar 699
        /**
1142 daniel-mar 700
         * @return string|null
1116 daniel-mar 701
         * @throws OIDplusException
702
         */
975 daniel-mar 703
        public function getUpdatedTime() {
704
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
705
                        $res = OIDplus::db()->query("select updated from ###objects where id = ?", array($this->nodeId()));
706
                        if (!$res->any()) return null;
707
                        $row = $res->fetch_array();
708
                        return $row['updated'];
709
                } else {
710
                        self::buildObjectInformationCache();
711
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
712
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_UPDATED];
713
                        }
1142 daniel-mar 714
                        return null;
975 daniel-mar 715
                }
716
        }
717
 
1116 daniel-mar 718
        /**
719
         * @param OIDplusRA|string|null $ra
720
         * @return bool
721
         * @throws OIDplusException
722
         */
1130 daniel-mar 723
        public function userHasParentalWriteRights($ra=null): bool {
1116 daniel-mar 724
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 725
 
1116 daniel-mar 726
                if (!$ra) {
549 daniel-mar 727
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
2 daniel-mar 728
                }
729
 
730
                $objParent = $this->getParent();
419 daniel-mar 731
                if (!$objParent) return false;
1116 daniel-mar 732
                return $objParent->userHasWriteRights($ra);
2 daniel-mar 733
        }
734
 
1116 daniel-mar 735
        /**
736
         * @param OIDplusRA|string|null $ra
737
         * @return bool
738
         * @throws OIDplusException
739
         */
740
        public function userHasWriteRights($ra=null): bool {
741
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 742
 
1116 daniel-mar 743
                if (!$ra) {
549 daniel-mar 744
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
1269 daniel-mar 745
                        // TODO: should we allow that the parent RA also may update title/description about this OID (since they delegated it?)
1137 daniel-mar 746
                        $ownRa = $this->getRaMail();
747
                        return $ownRa && OIDplus::authUtils()->isRaLoggedIn($ownRa);
2 daniel-mar 748
                } else {
1116 daniel-mar 749
                        return $this->getRaMail() == $ra;
2 daniel-mar 750
                }
751
        }
12 daniel-mar 752
 
1116 daniel-mar 753
        /**
754
         * @param string|OIDplusObject $to
755
         * @return int|null
756
         */
757
        public function distance($to)/*: ?int*/ {
12 daniel-mar 758
                return null; // not implemented
759
        }
20 daniel-mar 760
 
1116 daniel-mar 761
        /**
1130 daniel-mar 762
         * @param OIDplusObject|string $obj
1116 daniel-mar 763
         * @return bool
764
         */
1130 daniel-mar 765
        public function equals($obj): bool {
1121 daniel-mar 766
                if (!$obj) return false;
20 daniel-mar 767
                if (!is_object($obj)) $obj = OIDplusObject::parse($obj);
1121 daniel-mar 768
                if (!$obj) return false;
28 daniel-mar 769
                if (!($obj instanceof $this)) return false;
770
 
20 daniel-mar 771
                $distance = $this->distance($obj);
772
                if (is_numeric($distance)) return $distance === 0; // if the distance function is implemented, use it
773
 
774
                return $this->nodeId() == $obj->nodeId(); // otherwise compare the node id case-sensitive
775
        }
776
 
1116 daniel-mar 777
        /**
778
         * @param string $id
779
         * @return OIDplusObject|false
780
         * @throws OIDplusException
781
         */
977 daniel-mar 782
        public static function findFitting(string $id) {
20 daniel-mar 783
                $obj = OIDplusObject::parse($id);
969 daniel-mar 784
                if (!$obj) return false; // e.g. if ObjectType plugin is disabled
20 daniel-mar 785
 
261 daniel-mar 786
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
787
                        $res = OIDplus::db()->query("select id from ###objects where id like ?", array($obj->ns().':%'));
236 daniel-mar 788
                        while ($row = $res->fetch_object()) {
150 daniel-mar 789
                                $test = OIDplusObject::parse($row->id);
790
                                if ($obj->equals($test)) return $test;
791
                        }
792
                        return false;
793
                } else {
794
                        self::buildObjectInformationCache();
975 daniel-mar 795
                        foreach (self::$object_info_cache as $id => $cacheitem) {
150 daniel-mar 796
                                if (strpos($id, $obj->ns().':') === 0) {
797
                                        $test = OIDplusObject::parse($id);
798
                                        if ($obj->equals($test)) return $test;
799
                                }
800
                        }
801
                        return false;
20 daniel-mar 802
                }
803
        }
804
 
1116 daniel-mar 805
        /**
806
         * @return OIDplusObject|null
807
         */
808
        public function one_up()/*: ?OIDplusObject*/ {
20 daniel-mar 809
                return null; // not implemented
810
        }
150 daniel-mar 811
 
812
        // Caching stuff
813
 
814
        protected static $object_info_cache = null;
815
 
1116 daniel-mar 816
        /**
817
         * @return void
818
         */
150 daniel-mar 819
        public static function resetObjectInformationCache() {
820
                self::$object_info_cache = null;
821
        }
822
 
975 daniel-mar 823
        const CACHE_ID = 'id';
824
        const CACHE_PARENT = 'parent';
825
        const CACHE_TITLE = 'title';
826
        const CACHE_DESCRIPTION = 'description';
827
        const CACHE_RA_EMAIL = 'ra_email';
828
        const CACHE_CONFIDENTIAL = 'confidential';
829
        const CACHE_CREATED = 'created';
830
        const CACHE_UPDATED = 'updated';
831
        const CACHE_COMMENT = 'comment';
150 daniel-mar 832
 
1116 daniel-mar 833
        /**
834
         * @return void
835
         * @throws OIDplusException
836
         */
150 daniel-mar 837
        private static function buildObjectInformationCache() {
838
                if (is_null(self::$object_info_cache)) {
839
                        self::$object_info_cache = array();
975 daniel-mar 840
                        $res = OIDplus::db()->query("select * from ###objects");
236 daniel-mar 841
                        while ($row = $res->fetch_array()) {
975 daniel-mar 842
                                self::$object_info_cache[$row['id']] = $row;
150 daniel-mar 843
                        }
844
                }
845
        }
513 daniel-mar 846
 
1116 daniel-mar 847
        /**
848
         * override this function if you want your object type to save
849
         * attachments in directories with easy names.
850
         * Take care that your custom directory name will not allow jailbreaks (../) !
851
         * @return string
852
         * @throws OIDplusException
853
         */
854
        public function getDirectoryName(): string {
514 daniel-mar 855
                if ($this->isRoot()) return $this->ns();
856
                return $this->getLegacyDirectoryName();
513 daniel-mar 857
        }
858
 
1116 daniel-mar 859
        /**
860
         * @return string
861
         * @throws OIDplusException
862
         */
863
        public final function getLegacyDirectoryName(): string {
804 daniel-mar 864
                if ($this::ns() == 'oid') {
513 daniel-mar 865
                        $oid = $this->nodeId(false);
866
                } else {
867
                        $oid = null;
868
                        $alt_ids = $this->getAltIds();
869
                        foreach ($alt_ids as $alt_id) {
870
                                if ($alt_id->getNamespace() == 'oid') {
871
                                        $oid = $alt_id->getId();
872
                                        break; // we prefer the first OID (for GUIDs, the first OID is the OIDplus-OID, and the second OID is the UUID OID)
873
                                }
874
                        }
875
                }
876
 
877
                if (!is_null($oid) && ($oid != '')) {
878
                        // For OIDs, it is the OID, for other identifiers
879
                        // it it the OID alt ID (generated using the SystemID)
880
                        return str_replace('.', '_', $oid);
881
                } else {
882
                        // Can happen if you don't have a system ID (due to missing OpenSSL plugin)
883
                        return md5($this->nodeId(true)); // we don't use $id, because $this->nodeId(true) is possibly more canonical than $id
884
                }
885
        }
800 daniel-mar 886
 
1116 daniel-mar 887
        /**
888
         * @param string $mode
889
         * @return string
890
         */
891
        public static function treeIconFilename(string $mode): string {
800 daniel-mar 892
                // for backwards-compatibility with older plugins
893
                return 'img/treeicon_'.$mode.'.png';
894
        }
895
 
415 daniel-mar 896
}