Rev 444 | Rev 448 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 444 | Rev 447 | ||
---|---|---|---|
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * OIDplus 2.0 |
4 | * OIDplus 2.0 |
5 | * Copyright 2019 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2019 Daniel Marschall, ViaThinkSoft |
6 | * |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * you may not use this file except in compliance with 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 |
9 | * You may obtain a copy of the License at |
10 | * |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
17 | * limitations under the License. |
18 | */ |
18 | */ |
19 | 19 | ||
20 | class OIDplus { |
20 | class OIDplus { |
21 | private static /*OIDplusPagePlugin[]*/ $pagePlugins = array(); |
21 | private static /*OIDplusPagePlugin[]*/ $pagePlugins = array(); |
22 | private static /*OIDplusAuthPlugin[]*/ $authPlugins = array(); |
22 | private static /*OIDplusAuthPlugin[]*/ $authPlugins = array(); |
23 | private static /*OIDplusLoggerPlugin[]*/ $loggerPlugins = array(); |
23 | private static /*OIDplusLoggerPlugin[]*/ $loggerPlugins = array(); |
24 | private static /*OIDplusObjectTypePlugin[]*/ $objectTypePlugins = array(); |
24 | private static /*OIDplusObjectTypePlugin[]*/ $objectTypePlugins = array(); |
25 | private static /*string[]*/ $enabledObjectTypes = array(); |
25 | private static /*string[]*/ $enabledObjectTypes = array(); |
26 | private static /*string[]*/ $disabledObjectTypes = array(); |
26 | private static /*string[]*/ $disabledObjectTypes = array(); |
27 | private static /*OIDplusDatabasePlugin[]*/ $dbPlugins = array(); |
27 | private static /*OIDplusDatabasePlugin[]*/ $dbPlugins = array(); |
28 | private static /*OIDplusSqlSlangPlugin[]*/ $sqlSlangPlugins = array(); |
28 | private static /*OIDplusSqlSlangPlugin[]*/ $sqlSlangPlugins = array(); |
29 | private static /*OIDplusLanguagePlugin[]*/ $languagePlugins = array(); |
29 | private static /*OIDplusLanguagePlugin[]*/ $languagePlugins = array(); |
30 | 30 | ||
31 | protected static $html = true; |
31 | protected static $html = true; |
32 | 32 | ||
33 | /*public*/ const DEFAULT_LANGUAGE = 'enus'; // the language of the source code |
33 | /*public*/ const DEFAULT_LANGUAGE = 'enus'; // the language of the source code |
34 | 34 | ||
35 | private function __construct() { |
35 | private function __construct() { |
36 | } |
36 | } |
37 | 37 | ||
38 | # --- Static classes |
38 | # --- Static classes |
39 | 39 | ||
40 | private static $baseConfig = null; |
40 | private static $baseConfig = null; |
41 | private static $old_config_format = false; |
41 | private static $old_config_format = false; |
42 | public static function baseConfig() { |
42 | public static function baseConfig() { |
43 | $first_init = false; |
43 | $first_init = false; |
44 | 44 | ||
45 | if ($first_init = is_null(self::$baseConfig)) { |
45 | if ($first_init = is_null(self::$baseConfig)) { |
46 | self::$baseConfig = new OIDplusBaseConfig(); |
46 | self::$baseConfig = new OIDplusBaseConfig(); |
47 | } |
47 | } |
48 | 48 | ||
49 | if ($first_init) { |
49 | if ($first_init) { |
50 | // Include a file containing various size/depth limitations of OIDs |
50 | // Include a file containing various size/depth limitations of OIDs |
51 | // It is important to include it before userdata/baseconfig/config.inc.php was included, |
51 | // It is important to include it before userdata/baseconfig/config.inc.php was included, |
52 | // so we can give userdata/baseconfig/config.inc.php the chance to override the values. |
52 | // so we can give userdata/baseconfig/config.inc.php the chance to override the values. |
53 | 53 | ||
54 | include OIDplus::basePath().'/includes/oidplus_limits.inc.php'; |
54 | include OIDplus::basePath().'/includes/oidplus_limits.inc.php'; |
55 | 55 | ||
56 | // Include config file |
56 | // Include config file |
57 | 57 | ||
58 | $config_file = OIDplus::basePath() . '/userdata/baseconfig/config.inc.php'; |
58 | $config_file = OIDplus::basePath() . '/userdata/baseconfig/config.inc.php'; |
59 | $config_file_old = OIDplus::basePath() . '/includes/config.inc.php'; // backwards compatibility |
59 | $config_file_old = OIDplus::basePath() . '/includes/config.inc.php'; // backwards compatibility |
60 | 60 | ||
61 | if (!file_exists($config_file) && file_exists($config_file_old)) { |
61 | if (!file_exists($config_file) && file_exists($config_file_old)) { |
62 | $config_file = $config_file_old; |
62 | $config_file = $config_file_old; |
63 | } |
63 | } |
64 | 64 | ||
65 | if (file_exists($config_file)) { |
65 | if (file_exists($config_file)) { |
66 | if (self::$old_config_format) { |
66 | if (self::$old_config_format) { |
67 | // Note: We may only include it once due to backwards compatibility, |
67 | // Note: We may only include it once due to backwards compatibility, |
68 | // since in version 2.0, the configuration was defined using define() statements |
68 | // since in version 2.0, the configuration was defined using define() statements |
69 | // Attention: This does mean that a full re-init (e.g. for test cases) is not possible |
69 | // Attention: This does mean that a full re-init (e.g. for test cases) is not possible |
70 | // if a version 2.0 config is used! |
70 | // if a version 2.0 config is used! |
71 | include_once $config_file; |
71 | include_once $config_file; |
72 | } else { |
72 | } else { |
73 | include $config_file; |
73 | include $config_file; |
74 | } |
74 | } |
75 | 75 | ||
76 | if (defined('OIDPLUS_CONFIG_VERSION') && (OIDPLUS_CONFIG_VERSION == 2.0)) { |
76 | if (defined('OIDPLUS_CONFIG_VERSION') && (OIDPLUS_CONFIG_VERSION == 2.0)) { |
77 | self::$old_config_format = true; |
77 | self::$old_config_format = true; |
78 | 78 | ||
79 | // Backwards compatibility 2.0 => 2.1 |
79 | // Backwards compatibility 2.0 => 2.1 |
80 | foreach (get_defined_constants(true)['user'] as $name => $value) { |
80 | foreach (get_defined_constants(true)['user'] as $name => $value) { |
81 | $name = str_replace('OIDPLUS_', '', $name); |
81 | $name = str_replace('OIDPLUS_', '', $name); |
82 | if ($name == 'SESSION_SECRET') $name = 'SERVER_SECRET'; |
82 | if ($name == 'SESSION_SECRET') $name = 'SERVER_SECRET'; |
83 | if ($name == 'MYSQL_QUERYLOG') $name = 'QUERY_LOGFILE'; |
83 | if ($name == 'MYSQL_QUERYLOG') $name = 'QUERY_LOGFILE'; |
84 | if (($name == 'MYSQL_PASSWORD') || ($name == 'ODBC_PASSWORD') || ($name == 'PDO_PASSWORD') || ($name == 'PGSQL_PASSWORD')) { |
84 | if (($name == 'MYSQL_PASSWORD') || ($name == 'ODBC_PASSWORD') || ($name == 'PDO_PASSWORD') || ($name == 'PGSQL_PASSWORD')) { |
85 | self::$baseConfig->setValue($name, base64_decode($value)); |
85 | self::$baseConfig->setValue($name, base64_decode($value)); |
86 | } else { |
86 | } else { |
87 | if ($name == 'CONFIG_VERSION') $value = 2.1; |
87 | if ($name == 'CONFIG_VERSION') $value = 2.1; |
88 | self::$baseConfig->setValue($name, $value); |
88 | self::$baseConfig->setValue($name, $value); |
89 | } |
89 | } |
90 | } |
90 | } |
91 | } |
91 | } |
92 | } else { |
92 | } else { |
93 | if (!is_dir(OIDplus::basePath().'/setup')) { |
93 | if (!is_dir(OIDplus::basePath().'/setup')) { |
94 | throw new OIDplusConfigInitializationException(_L('File %1 is missing, but setup can\'t be started because its directory missing.','userdata/baseconfig/config.inc.php')); |
94 | throw new OIDplusConfigInitializationException(_L('File %1 is missing, but setup can\'t be started because its directory missing.','userdata/baseconfig/config.inc.php')); |
95 | } else { |
95 | } else { |
96 | if (self::$html) { |
96 | if (self::$html) { |
97 | if (strpos($_SERVER['REQUEST_URI'], OIDplus::getSystemUrl(true).'setup/') !== 0) { |
97 | if (strpos($_SERVER['REQUEST_URI'], OIDplus::getSystemUrl(true).'setup/') !== 0) { |
98 | header('Location:'.OIDplus::getSystemUrl().'setup/'); |
98 | header('Location:'.OIDplus::getSystemUrl().'setup/'); |
99 | die(_L('Redirecting to setup...')); |
99 | die(_L('Redirecting to setup...')); |
100 | } else { |
100 | } else { |
101 | return self::$baseConfig; |
101 | return self::$baseConfig; |
102 | } |
102 | } |
103 | } else { |
103 | } else { |
104 | // This can be displayed in e.g. ajax.php |
104 | // This can be displayed in e.g. ajax.php |
105 | throw new OIDplusConfigInitializationException(_L('File %1 is missing. Please run setup again.','userdata/baseconfig/config.inc.php')); |
105 | throw new OIDplusConfigInitializationException(_L('File %1 is missing. Please run setup again.','userdata/baseconfig/config.inc.php')); |
106 | } |
106 | } |
107 | } |
107 | } |
108 | } |
108 | } |
109 | 109 | ||
110 | // Check important config settings |
110 | // Check important config settings |
111 | 111 | ||
112 | if (self::$baseConfig->getValue('CONFIG_VERSION') != 2.1) { |
112 | if (self::$baseConfig->getValue('CONFIG_VERSION') != 2.1) { |
113 | throw new OIDplusConfigInitializationException(_L("The information located in %1 is outdated.",$config_file)); |
113 | throw new OIDplusConfigInitializationException(_L("The information located in %1 is outdated.",$config_file)); |
114 | } |
114 | } |
115 | 115 | ||
116 | if (self::$baseConfig->getValue('SERVER_SECRET', '') === '') { |
116 | if (self::$baseConfig->getValue('SERVER_SECRET', '') === '') { |
117 | throw new OIDplusConfigInitializationException(_L("You must set a value for SERVER_SECRET in %1 for the system to operate secure.",$config_file)); |
117 | throw new OIDplusConfigInitializationException(_L("You must set a value for SERVER_SECRET in %1 for the system to operate secure.",$config_file)); |
118 | } |
118 | } |
119 | } |
119 | } |
120 | 120 | ||
121 | return self::$baseConfig; |
121 | return self::$baseConfig; |
122 | } |
122 | } |
123 | 123 | ||
124 | private static $config = null; |
124 | private static $config = null; |
125 | public static function config() { |
125 | public static function config() { |
126 | if ($first_init = is_null(self::$config)) { |
126 | if ($first_init = is_null(self::$config)) { |
127 | self::$config = new OIDplusConfig(); |
127 | self::$config = new OIDplusConfig(); |
128 | } |
128 | } |
129 | 129 | ||
130 | if ($first_init) { |
130 | if ($first_init) { |
131 | // These are important settings for base functionalities and therefore are not inside plugins |
131 | // These are important settings for base functionalities and therefore are not inside plugins |
132 | self::$config->prepareConfigKey('system_title', 'What is the name of your RA?', 'OIDplus 2.0', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
132 | self::$config->prepareConfigKey('system_title', 'What is the name of your RA?', 'OIDplus 2.0', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
133 | if (empty($value)) { |
133 | if (empty($value)) { |
134 | throw new OIDplusException(_L('Please enter a value for the system title.')); |
134 | throw new OIDplusException(_L('Please enter a value for the system title.')); |
135 | } |
135 | } |
136 | }); |
136 | }); |
137 | self::$config->prepareConfigKey('admin_email', 'E-Mail address of the system administrator', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
137 | self::$config->prepareConfigKey('admin_email', 'E-Mail address of the system administrator', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
138 | if (!empty($value) && !OIDplus::mailUtils()->validMailAddress($value)) { |
138 | if (!empty($value) && !OIDplus::mailUtils()->validMailAddress($value)) { |
139 | throw new OIDplusException(_L('This is not a correct email address')); |
139 | throw new OIDplusException(_L('This is not a correct email address')); |
140 | } |
140 | } |
141 | }); |
141 | }); |
142 | self::$config->prepareConfigKey('global_cc', 'Global CC for all outgoing emails?', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
142 | self::$config->prepareConfigKey('global_cc', 'Global CC for all outgoing emails?', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
143 | if (!empty($value) && !OIDplus::mailUtils()->validMailAddress($value)) { |
143 | if (!empty($value) && !OIDplus::mailUtils()->validMailAddress($value)) { |
144 | throw new OIDplusException(_L('This is not a correct email address')); |
144 | throw new OIDplusException(_L('This is not a correct email address')); |
145 | } |
145 | } |
146 | }); |
146 | }); |
147 | self::$config->prepareConfigKey('objecttypes_initialized', 'List of object type plugins that were initialized once', '', OIDplusConfig::PROTECTION_READONLY, function($value) { |
147 | self::$config->prepareConfigKey('objecttypes_initialized', 'List of object type plugins that were initialized once', '', OIDplusConfig::PROTECTION_READONLY, function($value) { |
148 | // Nothing here yet |
148 | // Nothing here yet |
149 | }); |
149 | }); |
150 | self::$config->prepareConfigKey('objecttypes_enabled', 'Enabled object types and their order, separated with a semicolon (please reload the page so that the change is applied)', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
150 | self::$config->prepareConfigKey('objecttypes_enabled', 'Enabled object types and their order, separated with a semicolon (please reload the page so that the change is applied)', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
151 | # TODO: when objecttypes_enabled is changed at the admin control panel, we need to do a reload of the page, so that jsTree will be updated. Is there anything we can do? |
151 | # TODO: when objecttypes_enabled is changed at the admin control panel, we need to do a reload of the page, so that jsTree will be updated. Is there anything we can do? |
152 | 152 | ||
153 | $ary = explode(';',$value); |
153 | $ary = explode(';',$value); |
154 | $uniq_ary = array_unique($ary); |
154 | $uniq_ary = array_unique($ary); |
155 | 155 | ||
156 | if (count($ary) != count($uniq_ary)) { |
156 | if (count($ary) != count($uniq_ary)) { |
157 | throw new OIDplusException(_L('Please check your input. Some object types are double.')); |
157 | throw new OIDplusException(_L('Please check your input. Some object types are double.')); |
158 | } |
158 | } |
159 | 159 | ||
160 | foreach ($ary as $ot_check) { |
160 | foreach ($ary as $ot_check) { |
161 | $ns_found = false; |
161 | $ns_found = false; |
162 | foreach (OIDplus::getEnabledObjectTypes() as $ot) { |
162 | foreach (OIDplus::getEnabledObjectTypes() as $ot) { |
163 | if ($ot::ns() == $ot_check) { |
163 | if ($ot::ns() == $ot_check) { |
164 | $ns_found = true; |
164 | $ns_found = true; |
165 | break; |
165 | break; |
166 | } |
166 | } |
167 | } |
167 | } |
168 | foreach (OIDplus::getDisabledObjectTypes() as $ot) { |
168 | foreach (OIDplus::getDisabledObjectTypes() as $ot) { |
169 | if ($ot::ns() == $ot_check) { |
169 | if ($ot::ns() == $ot_check) { |
170 | $ns_found = true; |
170 | $ns_found = true; |
171 | break; |
171 | break; |
172 | } |
172 | } |
173 | } |
173 | } |
174 | if (!$ns_found) { |
174 | if (!$ns_found) { |
175 | throw new OIDplusException(_L('Please check your input. Namespace "%1" is not found',$ot_check)); |
175 | throw new OIDplusException(_L('Please check your input. Namespace "%1" is not found',$ot_check)); |
176 | } |
176 | } |
177 | } |
177 | } |
178 | }); |
178 | }); |
179 | self::$config->prepareConfigKey('oidplus_private_key', 'Private key for this system', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) { |
179 | self::$config->prepareConfigKey('oidplus_private_key', 'Private key for this system', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) { |
180 | // Nothing here yet |
180 | // Nothing here yet |
181 | }); |
181 | }); |
182 | self::$config->prepareConfigKey('oidplus_public_key', 'Public key for this system. If you "clone" your system, you must delete this key (e.g. using phpMyAdmin), so that a new one is created.', '', OIDplusConfig::PROTECTION_READONLY, function($value) { |
182 | self::$config->prepareConfigKey('oidplus_public_key', 'Public key for this system. If you "clone" your system, you must delete this key (e.g. using phpMyAdmin), so that a new one is created.', '', OIDplusConfig::PROTECTION_READONLY, function($value) { |
183 | // Nothing here yet |
183 | // Nothing here yet |
184 | }); |
184 | }); |
185 | self::$config->prepareConfigKey('last_known_system_url', 'Last known System URL', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) { |
185 | self::$config->prepareConfigKey('last_known_system_url', 'Last known System URL', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) { |
186 | // Nothing here yet |
186 | // Nothing here yet |
187 | }); |
187 | }); |
188 | self::$config->prepareConfigKey('last_known_version', 'Last known OIDplus Version', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) { |
188 | self::$config->prepareConfigKey('last_known_version', 'Last known OIDplus Version', '', OIDplusConfig::PROTECTION_HIDDEN, function($value) { |
189 | // Nothing here yet |
189 | // Nothing here yet |
190 | }); |
190 | }); |
191 | } |
191 | } |
192 | 192 | ||
193 | return self::$config; |
193 | return self::$config; |
194 | } |
194 | } |
195 | 195 | ||
196 | private static $gui = null; |
196 | private static $gui = null; |
197 | public static function gui() { |
197 | public static function gui() { |
198 | if (is_null(self::$gui)) { |
198 | if (is_null(self::$gui)) { |
199 | self::$gui = new OIDplusGui(); |
199 | self::$gui = new OIDplusGui(); |
200 | } |
200 | } |
201 | return self::$gui; |
201 | return self::$gui; |
202 | } |
202 | } |
203 | 203 | ||
204 | private static $authUtils = null; |
204 | private static $authUtils = null; |
205 | public static function authUtils() { |
205 | public static function authUtils() { |
206 | if (is_null(self::$authUtils)) { |
206 | if (is_null(self::$authUtils)) { |
207 | self::$authUtils = new OIDplusAuthUtils(); |
207 | self::$authUtils = new OIDplusAuthUtils(); |
208 | } |
208 | } |
209 | return self::$authUtils; |
209 | return self::$authUtils; |
210 | } |
210 | } |
211 | 211 | ||
212 | private static $mailUtils = null; |
212 | private static $mailUtils = null; |
213 | public static function mailUtils() { |
213 | public static function mailUtils() { |
214 | if (is_null(self::$mailUtils)) { |
214 | if (is_null(self::$mailUtils)) { |
215 | self::$mailUtils = new OIDplusMailUtils(); |
215 | self::$mailUtils = new OIDplusMailUtils(); |
216 | } |
216 | } |
217 | return self::$mailUtils; |
217 | return self::$mailUtils; |
218 | } |
218 | } |
219 | 219 | ||
220 | private static $menuUtils = null; |
220 | private static $menuUtils = null; |
221 | public static function menuUtils() { |
221 | public static function menuUtils() { |
222 | if (is_null(self::$menuUtils)) { |
222 | if (is_null(self::$menuUtils)) { |
223 | self::$menuUtils = new OIDplusMenuUtils(); |
223 | self::$menuUtils = new OIDplusMenuUtils(); |
224 | } |
224 | } |
225 | return self::$menuUtils; |
225 | return self::$menuUtils; |
226 | } |
226 | } |
227 | 227 | ||
228 | private static $logger = null; |
228 | private static $logger = null; |
229 | public static function logger() { |
229 | public static function logger() { |
230 | if (is_null(self::$logger)) { |
230 | if (is_null(self::$logger)) { |
231 | self::$logger = new OIDplusLogger(); |
231 | self::$logger = new OIDplusLogger(); |
232 | } |
232 | } |
233 | return self::$logger; |
233 | return self::$logger; |
234 | } |
234 | } |
235 | 235 | ||
236 | private static $sesHandler = null; |
236 | private static $sesHandler = null; |
237 | public static function sesHandler() { |
237 | public static function sesHandler() { |
238 | if (is_null(self::$sesHandler)) { |
238 | if (is_null(self::$sesHandler)) { |
239 | self::$sesHandler = new OIDplusSessionHandler(); |
239 | self::$sesHandler = new OIDplusSessionHandler(); |
240 | } |
240 | } |
241 | return self::$sesHandler; |
241 | return self::$sesHandler; |
242 | } |
242 | } |
243 | 243 | ||
244 | # --- SQL slang plugin |
244 | # --- SQL slang plugin |
245 | 245 | ||
246 | private static function registerSqlSlangPlugin(OIDplusSqlSlangPlugin $plugin) { |
246 | private static function registerSqlSlangPlugin(OIDplusSqlSlangPlugin $plugin) { |
247 | $name = $plugin::id(); |
247 | $name = $plugin::id(); |
248 | if ($name === false) return false; |
248 | if ($name === false) return false; |
249 | 249 | ||
250 | self::$sqlSlangPlugins[$name] = $plugin; |
250 | self::$sqlSlangPlugins[$name] = $plugin; |
251 | 251 | ||
252 | return true; |
252 | return true; |
253 | } |
253 | } |
254 | 254 | ||
255 | public static function getSqlSlangPlugins() { |
255 | public static function getSqlSlangPlugins() { |
256 | return self::$sqlSlangPlugins; |
256 | return self::$sqlSlangPlugins; |
257 | } |
257 | } |
258 | 258 | ||
259 | public static function getSqlSlangPlugin($id)/*: ?OIDplusSqlSlangPlugin*/ { |
259 | public static function getSqlSlangPlugin($id)/*: ?OIDplusSqlSlangPlugin*/ { |
260 | if (isset(self::$sqlSlangPlugins[$id])) { |
260 | if (isset(self::$sqlSlangPlugins[$id])) { |
261 | return self::$sqlSlangPlugins[$id]; |
261 | return self::$sqlSlangPlugins[$id]; |
262 | } else { |
262 | } else { |
263 | return null; |
263 | return null; |
264 | } |
264 | } |
265 | } |
265 | } |
266 | 266 | ||
267 | # --- Database plugin |
267 | # --- Database plugin |
268 | 268 | ||
269 | private static function registerDatabasePlugin(OIDplusDatabasePlugin $plugin) { |
269 | private static function registerDatabasePlugin(OIDplusDatabasePlugin $plugin) { |
270 | $name = $plugin::id(); |
270 | $name = $plugin::id(); |
271 | if ($name === false) return false; |
271 | if ($name === false) return false; |
272 | 272 | ||
273 | self::$dbPlugins[$name] = $plugin; |
273 | self::$dbPlugins[$name] = $plugin; |
274 | 274 | ||
275 | return true; |
275 | return true; |
276 | } |
276 | } |
277 | 277 | ||
278 | public static function getDatabasePlugins() { |
278 | public static function getDatabasePlugins() { |
279 | return self::$dbPlugins; |
279 | return self::$dbPlugins; |
280 | } |
280 | } |
281 | 281 | ||
282 | public static function getActiveDatabasePlugin() { |
282 | public static function getActiveDatabasePlugin() { |
283 | if (OIDplus::baseConfig()->getValue('DATABASE_PLUGIN', '') === '') { |
283 | if (OIDplus::baseConfig()->getValue('DATABASE_PLUGIN', '') === '') { |
284 | throw new OIDplusConfigInitializationException(_L('No database plugin selected in config file')); |
284 | throw new OIDplusConfigInitializationException(_L('No database plugin selected in config file')); |
285 | } |
285 | } |
286 | if (!isset(self::$dbPlugins[OIDplus::baseConfig()->getValue('DATABASE_PLUGIN')])) { |
286 | if (!isset(self::$dbPlugins[OIDplus::baseConfig()->getValue('DATABASE_PLUGIN')])) { |
287 | $db_plugin_name = OIDplus::baseConfig()->getValue('DATABASE_PLUGIN'); |
287 | $db_plugin_name = OIDplus::baseConfig()->getValue('DATABASE_PLUGIN'); |
288 | throw new OIDplusConfigInitializationException(_L('Database plugin "%1" not found',$db_plugin_name)); |
288 | throw new OIDplusConfigInitializationException(_L('Database plugin "%1" not found',$db_plugin_name)); |
289 | } |
289 | } |
290 | return self::$dbPlugins[OIDplus::baseConfig()->getValue('DATABASE_PLUGIN')]; |
290 | return self::$dbPlugins[OIDplus::baseConfig()->getValue('DATABASE_PLUGIN')]; |
291 | } |
291 | } |
292 | 292 | ||
293 | private static $dbMainSession = null; |
293 | private static $dbMainSession = null; |
294 | public static function db() { |
294 | public static function db() { |
295 | if (is_null(self::$dbMainSession)) { |
295 | if (is_null(self::$dbMainSession)) { |
296 | self::$dbMainSession = self::getActiveDatabasePlugin()->newConnection(); |
296 | self::$dbMainSession = self::getActiveDatabasePlugin()->newConnection(); |
297 | } |
297 | } |
298 | if (!self::$dbMainSession->isConnected()) self::$dbMainSession->connect(); |
298 | if (!self::$dbMainSession->isConnected()) self::$dbMainSession->connect(); |
299 | return self::$dbMainSession; |
299 | return self::$dbMainSession; |
300 | } |
300 | } |
301 | 301 | ||
302 | private static $dbIsolatedSession = null; |
302 | private static $dbIsolatedSession = null; |
303 | public static function dbIsolated() { |
303 | public static function dbIsolated() { |
304 | if (is_null(self::$dbIsolatedSession)) { |
304 | if (is_null(self::$dbIsolatedSession)) { |
305 | self::$dbIsolatedSession = self::getActiveDatabasePlugin()->newConnection(); |
305 | self::$dbIsolatedSession = self::getActiveDatabasePlugin()->newConnection(); |
306 | } |
306 | } |
307 | if (!self::$dbIsolatedSession->isConnected()) self::$dbIsolatedSession->connect(); |
307 | if (!self::$dbIsolatedSession->isConnected()) self::$dbIsolatedSession->connect(); |
308 | return self::$dbIsolatedSession; |
308 | return self::$dbIsolatedSession; |
309 | } |
309 | } |
310 | 310 | ||
311 | # --- Page plugin |
311 | # --- Page plugin |
312 | 312 | ||
313 | private static function registerPagePlugin(OIDplusPagePlugin $plugin) { |
313 | private static function registerPagePlugin(OIDplusPagePlugin $plugin) { |
314 | self::$pagePlugins[] = $plugin; |
314 | self::$pagePlugins[] = $plugin; |
315 | 315 | ||
316 | return true; |
316 | return true; |
317 | } |
317 | } |
318 | 318 | ||
319 | public static function getPagePlugins() { |
319 | public static function getPagePlugins() { |
320 | return self::$pagePlugins; |
320 | return self::$pagePlugins; |
321 | } |
321 | } |
322 | 322 | ||
323 | # --- Auth plugin |
323 | # --- Auth plugin |
324 | 324 | ||
325 | private static function registerAuthPlugin(OIDplusAuthPlugin $plugin) { |
325 | private static function registerAuthPlugin(OIDplusAuthPlugin $plugin) { |
326 | self::$authPlugins[] = $plugin; |
326 | self::$authPlugins[] = $plugin; |
327 | return true; |
327 | return true; |
328 | } |
328 | } |
329 | 329 | ||
330 | public static function getAuthPlugins() { |
330 | public static function getAuthPlugins() { |
331 | return self::$authPlugins; |
331 | return self::$authPlugins; |
332 | } |
332 | } |
333 | 333 | ||
334 | # --- Language plugin |
334 | # --- Language plugin |
335 | 335 | ||
336 | private static function registerLanguagePlugin(OIDplusLanguagePlugin $plugin) { |
336 | private static function registerLanguagePlugin(OIDplusLanguagePlugin $plugin) { |
337 | self::$languagePlugins[] = $plugin; |
337 | self::$languagePlugins[] = $plugin; |
338 | return true; |
338 | return true; |
339 | } |
339 | } |
340 | 340 | ||
341 | public static function getLanguagePlugins() { |
341 | public static function getLanguagePlugins() { |
342 | return self::$languagePlugins; |
342 | return self::$languagePlugins; |
343 | } |
343 | } |
344 | 344 | ||
345 | # --- Logger plugin |
345 | # --- Logger plugin |
346 | 346 | ||
347 | private static function registerLoggerPlugin(OIDplusLoggerPlugin $plugin) { |
347 | private static function registerLoggerPlugin(OIDplusLoggerPlugin $plugin) { |
348 | self::$loggerPlugins[] = $plugin; |
348 | self::$loggerPlugins[] = $plugin; |
349 | return true; |
349 | return true; |
350 | } |
350 | } |
351 | 351 | ||
352 | public static function getLoggerPlugins() { |
352 | public static function getLoggerPlugins() { |
353 | return self::$loggerPlugins; |
353 | return self::$loggerPlugins; |
354 | } |
354 | } |
355 | 355 | ||
356 | # --- Object type plugin |
356 | # --- Object type plugin |
357 | 357 | ||
358 | private static function registerObjectTypePlugin(OIDplusObjectTypePlugin $plugin) { |
358 | private static function registerObjectTypePlugin(OIDplusObjectTypePlugin $plugin) { |
359 | self::$objectTypePlugins[] = $plugin; |
359 | self::$objectTypePlugins[] = $plugin; |
360 | 360 | ||
361 | $ot = $plugin::getObjectTypeClassName(); |
361 | $ot = $plugin::getObjectTypeClassName(); |
362 | self::registerObjectType($ot); |
362 | self::registerObjectType($ot); |
363 | 363 | ||
364 | return true; |
364 | return true; |
365 | } |
365 | } |
366 | 366 | ||
367 | private static function registerObjectType($ot) { |
367 | private static function registerObjectType($ot) { |
368 | $ns = $ot::ns(); |
368 | $ns = $ot::ns(); |
369 | 369 | ||
370 | if (empty($ns)) throw new OIDplusException(_L('Attention: Empty NS at %1',$ot)); |
370 | if (empty($ns)) throw new OIDplusException(_L('Attention: Empty NS at %1',$ot)); |
371 | 371 | ||
372 | $ns_found = false; |
372 | $ns_found = false; |
373 | foreach (array_merge(OIDplus::getEnabledObjectTypes(), OIDplus::getDisabledObjectTypes()) as $test_ot) { |
373 | foreach (array_merge(OIDplus::getEnabledObjectTypes(), OIDplus::getDisabledObjectTypes()) as $test_ot) { |
374 | if ($test_ot::ns() == $ns) { |
374 | if ($test_ot::ns() == $ns) { |
375 | $ns_found = true; |
375 | $ns_found = true; |
376 | break; |
376 | break; |
377 | } |
377 | } |
378 | } |
378 | } |
379 | if ($ns_found) { |
379 | if ($ns_found) { |
380 | throw new OIDplusException(_L('Attention: Two objectType plugins use the same namespace "%1"!',$ns)); |
380 | throw new OIDplusException(_L('Attention: Two objectType plugins use the same namespace "%1"!',$ns)); |
381 | } |
381 | } |
382 | 382 | ||
383 | $init = OIDplus::config()->getValue("objecttypes_initialized"); |
383 | $init = OIDplus::config()->getValue("objecttypes_initialized"); |
384 | $init_ary = empty($init) ? array() : explode(';', $init); |
384 | $init_ary = empty($init) ? array() : explode(';', $init); |
385 | $init_ary = array_map('trim', $init_ary); |
385 | $init_ary = array_map('trim', $init_ary); |
386 | 386 | ||
387 | $enabled = OIDplus::config()->getValue("objecttypes_enabled"); |
387 | $enabled = OIDplus::config()->getValue("objecttypes_enabled"); |
388 | $enabled_ary = empty($enabled) ? array() : explode(';', $enabled); |
388 | $enabled_ary = empty($enabled) ? array() : explode(';', $enabled); |
389 | $enabled_ary = array_map('trim', $enabled_ary); |
389 | $enabled_ary = array_map('trim', $enabled_ary); |
390 | 390 | ||
391 | $do_enable = false; |
391 | $do_enable = false; |
392 | if (in_array($ns, $enabled_ary)) { |
392 | if (in_array($ns, $enabled_ary)) { |
- | 393 | // If it is in the list of enabled object types, it is enabled (obviously) |
|
393 | $do_enable = true; |
394 | $do_enable = true; |
394 | } else { |
395 | } else { |
395 | if (!OIDplus::config()->getValue('registration_done')) { |
396 | if (!OIDplus::config()->getValue('oobe_objects_done')) { |
- | 397 | // If the OOBE wizard is NOT done, then just enable the "oid" object type by default |
|
396 | $do_enable = $ns == 'oid'; |
398 | $do_enable = $ns == 'oid'; |
397 | } else { |
399 | } else { |
- | 400 | // If the OOBE wizard was done (once), then |
|
- | 401 | // we will enable all object types which were never initialized |
|
- | 402 | // (i.e. a plugin folder was freshly added) |
|
398 | $do_enable = !in_array($ns, $init_ary); |
403 | $do_enable = !in_array($ns, $init_ary); |
399 | } |
404 | } |
400 | } |
405 | } |
401 | 406 | ||
402 | if ($do_enable) { |
407 | if ($do_enable) { |
403 | self::$enabledObjectTypes[] = $ot; |
408 | self::$enabledObjectTypes[] = $ot; |
404 | usort(self::$enabledObjectTypes, function($a, $b) { |
409 | usort(self::$enabledObjectTypes, function($a, $b) { |
405 | $enabled = OIDplus::config()->getValue("objecttypes_enabled"); |
410 | $enabled = OIDplus::config()->getValue("objecttypes_enabled"); |
406 | $enabled_ary = explode(';', $enabled); |
411 | $enabled_ary = explode(';', $enabled); |
407 | 412 | ||
408 | $idx_a = array_search($a::ns(), $enabled_ary); |
413 | $idx_a = array_search($a::ns(), $enabled_ary); |
409 | $idx_b = array_search($b::ns(), $enabled_ary); |
414 | $idx_b = array_search($b::ns(), $enabled_ary); |
410 | 415 | ||
411 | if ($idx_a == $idx_b) { |
416 | if ($idx_a == $idx_b) { |
412 | return 0; |
417 | return 0; |
413 | } |
418 | } |
414 | return ($idx_a > $idx_b) ? +1 : -1; |
419 | return ($idx_a > $idx_b) ? +1 : -1; |
415 | }); |
420 | }); |
416 | } else { |
421 | } else { |
417 | self::$disabledObjectTypes[] = $ot; |
422 | self::$disabledObjectTypes[] = $ot; |
418 | } |
423 | } |
419 | 424 | ||
420 | if (!in_array($ns, $init_ary)) { |
425 | if (!in_array($ns, $init_ary)) { |
421 | // Was never initialized before, so we add it to the list of enabled object types once |
426 | // Was never initialized before, so we add it to the list of enabled object types once |
422 | 427 | ||
423 | if ($do_enable) { |
428 | if ($do_enable) { |
424 | $enabled_ary[] = $ns; |
429 | $enabled_ary[] = $ns; |
425 | OIDplus::config()->setValue("objecttypes_enabled", implode(';', $enabled_ary)); |
430 | OIDplus::config()->setValue("objecttypes_enabled", implode(';', $enabled_ary)); |
426 | } |
431 | } |
427 | 432 | ||
428 | $init_ary[] = $ns; |
433 | $init_ary[] = $ns; |
429 | OIDplus::config()->setValue("objecttypes_initialized", implode(';', $init_ary)); |
434 | OIDplus::config()->setValue("objecttypes_initialized", implode(';', $init_ary)); |
430 | } |
435 | } |
431 | } |
436 | } |
432 | 437 | ||
433 | public static function getObjectTypePlugins() { |
438 | public static function getObjectTypePlugins() { |
434 | return self::$objectTypePlugins; |
439 | return self::$objectTypePlugins; |
435 | } |
440 | } |
436 | 441 | ||
437 | public static function getObjectTypePluginsEnabled() { |
442 | public static function getObjectTypePluginsEnabled() { |
438 | $res = array(); |
443 | $res = array(); |
439 | foreach (self::$objectTypePlugins as $plugin) { |
444 | foreach (self::$objectTypePlugins as $plugin) { |
440 | $ot = $plugin::getObjectTypeClassName(); |
445 | $ot = $plugin::getObjectTypeClassName(); |
441 | if (in_array($ot, self::$enabledObjectTypes)) $res[] = $plugin; |
446 | if (in_array($ot, self::$enabledObjectTypes)) $res[] = $plugin; |
442 | } |
447 | } |
443 | return $res; |
448 | return $res; |
444 | } |
449 | } |
445 | 450 | ||
446 | public static function getObjectTypePluginsDisabled() { |
451 | public static function getObjectTypePluginsDisabled() { |
447 | $res = array(); |
452 | $res = array(); |
448 | foreach (self::$objectTypePlugins as $plugin) { |
453 | foreach (self::$objectTypePlugins as $plugin) { |
449 | $ot = $plugin::getObjectTypeClassName(); |
454 | $ot = $plugin::getObjectTypeClassName(); |
450 | if (in_array($ot, self::$disabledObjectTypes)) $res[] = $plugin; |
455 | if (in_array($ot, self::$disabledObjectTypes)) $res[] = $plugin; |
451 | } |
456 | } |
452 | return $res; |
457 | return $res; |
453 | } |
458 | } |
454 | 459 | ||
455 | public static function getEnabledObjectTypes() { |
460 | public static function getEnabledObjectTypes() { |
456 | return self::$enabledObjectTypes; |
461 | return self::$enabledObjectTypes; |
457 | } |
462 | } |
458 | 463 | ||
459 | public static function getDisabledObjectTypes() { |
464 | public static function getDisabledObjectTypes() { |
460 | return self::$disabledObjectTypes; |
465 | return self::$disabledObjectTypes; |
461 | } |
466 | } |
462 | 467 | ||
463 | # --- Plugin handling functions |
468 | # --- Plugin handling functions |
464 | 469 | ||
465 | public static function getAllPlugins()/*: array*/ { |
470 | public static function getAllPlugins()/*: array*/ { |
466 | $res = array(); |
471 | $res = array(); |
467 | $res = array_merge($res, self::$pagePlugins); |
472 | $res = array_merge($res, self::$pagePlugins); |
468 | $res = array_merge($res, self::$authPlugins); |
473 | $res = array_merge($res, self::$authPlugins); |
469 | $res = array_merge($res, self::$loggerPlugins); |
474 | $res = array_merge($res, self::$loggerPlugins); |
470 | $res = array_merge($res, self::$objectTypePlugins); |
475 | $res = array_merge($res, self::$objectTypePlugins); |
471 | $res = array_merge($res, self::$dbPlugins); |
476 | $res = array_merge($res, self::$dbPlugins); |
472 | $res = array_merge($res, self::$sqlSlangPlugins); |
477 | $res = array_merge($res, self::$sqlSlangPlugins); |
473 | $res = array_merge($res, self::$languagePlugins); |
478 | $res = array_merge($res, self::$languagePlugins); |
474 | return $res; |
479 | return $res; |
475 | } |
480 | } |
476 | 481 | ||
477 | public static function getPluginByOid($oid)/*: ?OIDplusPlugin*/ { |
482 | public static function getPluginByOid($oid)/*: ?OIDplusPlugin*/ { |
478 | $plugins = self::getAllPlugins(); |
483 | $plugins = self::getAllPlugins(); |
479 | foreach ($plugins as $plugin) { |
484 | foreach ($plugins as $plugin) { |
480 | if (oid_dotnotation_equal($plugin->getManifest()->getOid(), $oid)) { |
485 | if (oid_dotnotation_equal($plugin->getManifest()->getOid(), $oid)) { |
481 | return $plugin; |
486 | return $plugin; |
482 | } |
487 | } |
483 | } |
488 | } |
484 | return null; |
489 | return null; |
485 | } |
490 | } |
486 | 491 | ||
487 | public static function getPluginByClassName($classname)/*: ?OIDplusPlugin*/ { |
492 | public static function getPluginByClassName($classname)/*: ?OIDplusPlugin*/ { |
488 | $plugins = self::getAllPlugins(); |
493 | $plugins = self::getAllPlugins(); |
489 | foreach ($plugins as $plugin) { |
494 | foreach ($plugins as $plugin) { |
490 | if (get_class($plugin) === $classname) { |
495 | if (get_class($plugin) === $classname) { |
491 | return $plugin; |
496 | return $plugin; |
492 | } |
497 | } |
493 | } |
498 | } |
494 | return null; |
499 | return null; |
495 | } |
500 | } |
496 | 501 | ||
497 | public static function getAllPluginManifests($pluginFolderMask='*', $flat=true): array { |
502 | public static function getAllPluginManifests($pluginFolderMask='*', $flat=true): array { |
498 | $out = array(); |
503 | $out = array(); |
499 | // Note: glob() will sort by default, so we do not need a page priority attribute. |
504 | // Note: glob() will sort by default, so we do not need a page priority attribute. |
500 | // So you just need to use a numeric plugin directory prefix (padded). |
505 | // So you just need to use a numeric plugin directory prefix (padded). |
501 | $ary = glob(OIDplus::basePath().'/plugins/'.$pluginFolderMask.'/'.'*'.'/manifest.xml'); |
506 | $ary = glob(OIDplus::basePath().'/plugins/'.$pluginFolderMask.'/'.'*'.'/manifest.xml'); |
502 | sort($ary); |
507 | sort($ary); |
503 | foreach ($ary as $ini) { |
508 | foreach ($ary as $ini) { |
504 | if (!file_exists($ini)) continue; |
509 | if (!file_exists($ini)) continue; |
505 | 510 | ||
506 | $manifest = new OIDplusPluginManifest(); |
511 | $manifest = new OIDplusPluginManifest(); |
507 | $manifest->loadManifest($ini); |
512 | $manifest->loadManifest($ini); |
508 | 513 | ||
509 | if ($flat) { |
514 | if ($flat) { |
510 | $out[] = $manifest; |
515 | $out[] = $manifest; |
511 | } else { |
516 | } else { |
512 | $plugintype_folder = basename(dirname(dirname($ini))); |
517 | $plugintype_folder = basename(dirname(dirname($ini))); |
513 | $pluginname_folder = basename(dirname($ini)); |
518 | $pluginname_folder = basename(dirname($ini)); |
514 | 519 | ||
515 | if (!isset($out[$plugintype_folder])) $out[$plugintype_folder] = array(); |
520 | if (!isset($out[$plugintype_folder])) $out[$plugintype_folder] = array(); |
516 | if (!isset($out[$plugintype_folder][$pluginname_folder])) $out[$plugintype_folder][$pluginname_folder] = array(); |
521 | if (!isset($out[$plugintype_folder][$pluginname_folder])) $out[$plugintype_folder][$pluginname_folder] = array(); |
517 | $out[$plugintype_folder][$pluginname_folder] = $manifest; |
522 | $out[$plugintype_folder][$pluginname_folder] = $manifest; |
518 | } |
523 | } |
519 | } |
524 | } |
520 | return $out; |
525 | return $out; |
521 | } |
526 | } |
522 | 527 | ||
523 | public static function registerAllPlugins($pluginDirName, $expectedPluginClass, $registerCallback): array { |
528 | public static function registerAllPlugins($pluginDirName, $expectedPluginClass, $registerCallback): array { |
524 | $out = array(); |
529 | $out = array(); |
525 | $ary = self::getAllPluginManifests($pluginDirName, false); |
530 | $ary = self::getAllPluginManifests($pluginDirName, false); |
526 | $known_plugin_oids = array(); |
531 | $known_plugin_oids = array(); |
527 | $fake_feature = uuid_to_oid(gen_uuid()); |
532 | $fake_feature = uuid_to_oid(gen_uuid()); |
528 | foreach ($ary as $plugintype_folder => $bry) { |
533 | foreach ($ary as $plugintype_folder => $bry) { |
529 | foreach ($bry as $pluginname_folder => $manifest) { |
534 | foreach ($bry as $pluginname_folder => $manifest) { |
530 | $class_name = $manifest->getPhpMainClass(); |
535 | $class_name = $manifest->getPhpMainClass(); |
531 | 536 | ||
532 | // Before we load the plugin, we want to make some checks to confirm |
537 | // Before we load the plugin, we want to make some checks to confirm |
533 | // that the plugin is working correctly. |
538 | // that the plugin is working correctly. |
534 | 539 | ||
535 | if (!$class_name) { |
540 | if (!$class_name) { |
536 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Manifest does not declare a PHP main class')); |
541 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Manifest does not declare a PHP main class')); |
537 | } |
542 | } |
538 | if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_'.$class_name, false)) { |
543 | if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_'.$class_name, false)) { |
539 | continue; |
544 | continue; |
540 | } |
545 | } |
541 | if (!class_exists($class_name)) { |
546 | if (!class_exists($class_name)) { |
542 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Manifest declares PHP main class as "%1", but it could not be found',$class_name)); |
547 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Manifest declares PHP main class as "%1", but it could not be found',$class_name)); |
543 | } |
548 | } |
544 | if (!is_subclass_of($class_name, $expectedPluginClass)) { |
549 | if (!is_subclass_of($class_name, $expectedPluginClass)) { |
545 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Plugin main class "%1" is expected to be a subclass of "%2"',$class_name,$expectedPluginClass)); |
550 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Plugin main class "%1" is expected to be a subclass of "%2"',$class_name,$expectedPluginClass)); |
546 | } |
551 | } |
547 | if (($class_name!=$manifest->getTypeClass()) && (!is_subclass_of($class_name,$manifest->getTypeClass()))) { |
552 | if (($class_name!=$manifest->getTypeClass()) && (!is_subclass_of($class_name,$manifest->getTypeClass()))) { |
548 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Plugin main class "%1" is expected to be a subclass of "%2", according to type declared in manifest',$class_name,$manifest->getTypeClass())); |
553 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Plugin main class "%1" is expected to be a subclass of "%2", according to type declared in manifest',$class_name,$manifest->getTypeClass())); |
549 | } |
554 | } |
550 | if (($manifest->getTypeClass()!=$expectedPluginClass) && (!is_subclass_of($manifest->getTypeClass(),$expectedPluginClass))) { |
555 | if (($manifest->getTypeClass()!=$expectedPluginClass) && (!is_subclass_of($manifest->getTypeClass(),$expectedPluginClass))) { |
551 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Class declared in manifest is "%1" does not fit expected class for this plugin type "%2"',$manifest->getTypeClass(),$expectedPluginClass)); |
556 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Class declared in manifest is "%1" does not fit expected class for this plugin type "%2"',$manifest->getTypeClass(),$expectedPluginClass)); |
552 | } |
557 | } |
553 | 558 | ||
554 | $plugin_oid = $manifest->getOid(); |
559 | $plugin_oid = $manifest->getOid(); |
555 | if (!$plugin_oid) { |
560 | if (!$plugin_oid) { |
556 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Does not have an OID')); |
561 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Does not have an OID')); |
557 | } |
562 | } |
558 | if (!oid_valid_dotnotation($plugin_oid, false, false, 2)) { |
563 | if (!oid_valid_dotnotation($plugin_oid, false, false, 2)) { |
559 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Plugin OID "%1" is invalid (needs to be valid dot-notation)',$plugin_oid)); |
564 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('Plugin OID "%1" is invalid (needs to be valid dot-notation)',$plugin_oid)); |
560 | } |
565 | } |
561 | if (isset($known_plugin_oids[$plugin_oid])) { |
566 | if (isset($known_plugin_oids[$plugin_oid])) { |
562 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('The OID "%1" is already used by the plugin "%2"',$plugin_oid,$known_plugin_oids[$plugin_oid])); |
567 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('The OID "%1" is already used by the plugin "%2"',$plugin_oid,$known_plugin_oids[$plugin_oid])); |
563 | } else { |
568 | } else { |
564 | $known_plugin_oids[$plugin_oid] = $plugintype_folder.'/'.$pluginname_folder; |
569 | $known_plugin_oids[$plugin_oid] = $plugintype_folder.'/'.$pluginname_folder; |
565 | } |
570 | } |
566 | 571 | ||
567 | $obj = new $class_name(); |
572 | $obj = new $class_name(); |
568 | if ($obj->implementsFeature($fake_feature)) { |
573 | if ($obj->implementsFeature($fake_feature)) { |
569 | // see https://devblogs.microsoft.com/oldnewthing/20040211-00/?p=40663 |
574 | // see https://devblogs.microsoft.com/oldnewthing/20040211-00/?p=40663 |
570 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('implementsFeature() always returns true')); |
575 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('implementsFeature() always returns true')); |
571 | } |
576 | } |
572 | 577 | ||
573 | // TODO: Maybe as additional plugin-test, we should also check if plugins are allowed to define CSS/JS (since only page plugins may have them!) |
578 | // TODO: Maybe as additional plugin-test, we should also check if plugins are allowed to define CSS/JS (since only page plugins may have them!) |
574 | $tmp = array_merge( |
579 | $tmp = array_merge( |
575 | $manifest->getJSFiles(), |
580 | $manifest->getJSFiles(), |
576 | $manifest->getCSSFiles(), |
581 | $manifest->getCSSFiles(), |
577 | $manifest->getJSFilesSetup(), |
582 | $manifest->getJSFilesSetup(), |
578 | $manifest->getCSSFilesSetup() |
583 | $manifest->getCSSFilesSetup() |
579 | ); |
584 | ); |
580 | foreach ($tmp as $file) { |
585 | foreach ($tmp as $file) { |
581 | if (!file_exists($file)) { |
586 | if (!file_exists($file)) { |
582 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('File %1 was defined in manifest, but it is not existing',$file)); |
587 | throw new OIDplusException(_L('Plugin "%1/%2" is erroneous',$plugintype_folder,$pluginname_folder).': '._L('File %1 was defined in manifest, but it is not existing',$file)); |
583 | } |
588 | } |
584 | } |
589 | } |
585 | 590 | ||
586 | // Now we can continue |
591 | // Now we can continue |
587 | 592 | ||
588 | $out[] = $class_name; |
593 | $out[] = $class_name; |
589 | if (!is_null($registerCallback)) { |
594 | if (!is_null($registerCallback)) { |
590 | call_user_func($registerCallback, $obj); |
595 | call_user_func($registerCallback, $obj); |
591 | 596 | ||
592 | // Alternative approaches: |
597 | // Alternative approaches: |
593 | //$registerCallback[0]::{$registerCallback[1]}($obj); |
598 | //$registerCallback[0]::{$registerCallback[1]}($obj); |
594 | // or: |
599 | // or: |
595 | //forward_static_call($registerCallback, $obj); |
600 | //forward_static_call($registerCallback, $obj); |
596 | } |
601 | } |
597 | } |
602 | } |
598 | 603 | ||
599 | } |
604 | } |
600 | return $out; |
605 | return $out; |
601 | } |
606 | } |
602 | 607 | ||
603 | # --- Initialization of OIDplus |
608 | # --- Initialization of OIDplus |
604 | 609 | ||
605 | public static function init($html=true, $keepBaseConfig=true) { |
610 | public static function init($html=true, $keepBaseConfig=true) { |
606 | self::$html = $html; |
611 | self::$html = $html; |
607 | 612 | ||
608 | // Reset internal state, so we can re-init verything if required |
613 | // Reset internal state, so we can re-init verything if required |
609 | 614 | ||
610 | if (self::$old_config_format) { |
615 | if (self::$old_config_format) { |
611 | // Note: This can only happen in very special cases (e.g. test cases) where you call init() twice |
616 | // Note: This can only happen in very special cases (e.g. test cases) where you call init() twice |
612 | throw new OIDplusConfigInitializationException(_L('A full re-initialization is not possible if a version 2.0 config file (containing "defines") is used. Please update to a config 2.1 file by running setup again.')); |
617 | throw new OIDplusConfigInitializationException(_L('A full re-initialization is not possible if a version 2.0 config file (containing "defines") is used. Please update to a config 2.1 file by running setup again.')); |
613 | } |
618 | } |
614 | 619 | ||
615 | self::$config = null; |
620 | self::$config = null; |
616 | if (!$keepBaseConfig) self::$baseConfig = null; // for test cases we need to be able to control base config and setting values manually, so $keepBaseConfig needs to be true |
621 | if (!$keepBaseConfig) self::$baseConfig = null; // for test cases we need to be able to control base config and setting values manually, so $keepBaseConfig needs to be true |
617 | self::$gui = null; |
622 | self::$gui = null; |
618 | self::$authUtils = null; |
623 | self::$authUtils = null; |
619 | self::$mailUtils = null; |
624 | self::$mailUtils = null; |
620 | self::$menuUtils = null; |
625 | self::$menuUtils = null; |
621 | self::$logger = null; |
626 | self::$logger = null; |
622 | self::$sesHandler = null; |
627 | self::$sesHandler = null; |
623 | self::$dbMainSession = null; |
628 | self::$dbMainSession = null; |
624 | self::$dbIsolatedSession = null; |
629 | self::$dbIsolatedSession = null; |
625 | self::$pagePlugins = array(); |
630 | self::$pagePlugins = array(); |
626 | self::$authPlugins = array(); |
631 | self::$authPlugins = array(); |
627 | self::$loggerPlugins = array(); |
632 | self::$loggerPlugins = array(); |
628 | self::$objectTypePlugins = array(); |
633 | self::$objectTypePlugins = array(); |
629 | self::$enabledObjectTypes = array(); |
634 | self::$enabledObjectTypes = array(); |
630 | self::$disabledObjectTypes = array(); |
635 | self::$disabledObjectTypes = array(); |
631 | self::$dbPlugins = array(); |
636 | self::$dbPlugins = array(); |
632 | self::$sqlSlangPlugins = array(); |
637 | self::$sqlSlangPlugins = array(); |
633 | self::$languagePlugins = array(); |
638 | self::$languagePlugins = array(); |
634 | self::$system_id_cache = null; |
639 | self::$system_id_cache = null; |
635 | self::$sslAvailableCache = null; |
640 | self::$sslAvailableCache = null; |
636 | 641 | ||
637 | // Continue... |
642 | // Continue... |
638 | 643 | ||
639 | OIDplus::baseConfig(); // this loads the base configuration located in userdata/baseconfig/config.inc.php (once!) |
644 | OIDplus::baseConfig(); // this loads the base configuration located in userdata/baseconfig/config.inc.php (once!) |
640 | // You can do changes to the configuration afterwards using OIDplus::baseConfig()->... |
645 | // You can do changes to the configuration afterwards using OIDplus::baseConfig()->... |
641 | 646 | ||
642 | // Register database types (highest priority) |
647 | // Register database types (highest priority) |
643 | 648 | ||
644 | // SQL slangs |
649 | // SQL slangs |
645 | 650 | ||
646 | self::registerAllPlugins('sqlSlang', 'OIDplusSqlSlangPlugin', array('OIDplus','registerSqlSlangPlugin')); |
651 | self::registerAllPlugins('sqlSlang', 'OIDplusSqlSlangPlugin', array('OIDplus','registerSqlSlangPlugin')); |
647 | foreach (OIDplus::getSqlSlangPlugins() as $plugin) { |
652 | foreach (OIDplus::getSqlSlangPlugins() as $plugin) { |
648 | $plugin->init($html); |
653 | $plugin->init($html); |
649 | } |
654 | } |
650 | 655 | ||
651 | // Database providers |
656 | // Database providers |
652 | 657 | ||
653 | self::registerAllPlugins('database', 'OIDplusDatabasePlugin', array('OIDplus','registerDatabasePlugin')); |
658 | self::registerAllPlugins('database', 'OIDplusDatabasePlugin', array('OIDplus','registerDatabasePlugin')); |
654 | foreach (OIDplus::getDatabasePlugins() as $plugin) { |
659 | foreach (OIDplus::getDatabasePlugins() as $plugin) { |
655 | $plugin->init($html); |
660 | $plugin->init($html); |
656 | } |
661 | } |
657 | 662 | ||
658 | // Do redirect stuff etc. |
663 | // Do redirect stuff etc. |
659 | 664 | ||
660 | self::isSslAvailable(); // This function does automatic redirects |
665 | self::isSslAvailable(); // This function does automatic redirects |
661 | 666 | ||
662 | // Construct the configuration manager |
667 | // Construct the configuration manager |
663 | 668 | ||
664 | OIDplus::config(); // During the construction, various system settings are prepared if required |
669 | OIDplus::config(); // During the construction, various system settings are prepared if required |
665 | 670 | ||
666 | // Initialize public / private keys |
671 | // Initialize public / private keys |
667 | 672 | ||
668 | OIDplus::getPkiStatus(true); |
673 | OIDplus::getPkiStatus(true); |
669 | 674 | ||
670 | // Register non-DB plugins |
675 | // Register non-DB plugins |
671 | 676 | ||
672 | self::registerAllPlugins('*Pages', 'OIDplusPagePlugin', array('OIDplus','registerPagePlugin')); |
677 | self::registerAllPlugins('*Pages', 'OIDplusPagePlugin', array('OIDplus','registerPagePlugin')); |
673 | self::registerAllPlugins('auth', 'OIDplusAuthPlugin', array('OIDplus','registerAuthPlugin')); |
678 | self::registerAllPlugins('auth', 'OIDplusAuthPlugin', array('OIDplus','registerAuthPlugin')); |
674 | self::registerAllPlugins('logger', 'OIDplusLoggerPlugin', array('OIDplus','registerLoggerPlugin')); |
679 | self::registerAllPlugins('logger', 'OIDplusLoggerPlugin', array('OIDplus','registerLoggerPlugin')); |
675 | self::registerAllPlugins('objectTypes', 'OIDplusObjectTypePlugin', array('OIDplus','registerObjectTypePlugin')); |
680 | self::registerAllPlugins('objectTypes', 'OIDplusObjectTypePlugin', array('OIDplus','registerObjectTypePlugin')); |
676 | self::registerAllPlugins('language', 'OIDplusLanguagePlugin', array('OIDplus','registerLanguagePlugin')); |
681 | self::registerAllPlugins('language', 'OIDplusLanguagePlugin', array('OIDplus','registerLanguagePlugin')); |
677 | 682 | ||
678 | // Initialize non-DB plugins |
683 | // Initialize non-DB plugins |
679 | 684 | ||
680 | foreach (OIDplus::getPagePlugins() as $plugin) { |
685 | foreach (OIDplus::getPagePlugins() as $plugin) { |
681 | $plugin->init($html); |
686 | $plugin->init($html); |
682 | } |
687 | } |
683 | foreach (OIDplus::getAuthPlugins() as $plugin) { |
688 | foreach (OIDplus::getAuthPlugins() as $plugin) { |
684 | $plugin->init($html); |
689 | $plugin->init($html); |
685 | } |
690 | } |
686 | foreach (OIDplus::getLoggerPlugins() as $plugin) { |
691 | foreach (OIDplus::getLoggerPlugins() as $plugin) { |
687 | $plugin->init($html); |
692 | $plugin->init($html); |
688 | } |
693 | } |
689 | foreach (OIDplus::getObjectTypePlugins() as $plugin) { |
694 | foreach (OIDplus::getObjectTypePlugins() as $plugin) { |
690 | $plugin->init($html); |
695 | $plugin->init($html); |
691 | } |
696 | } |
692 | foreach (OIDplus::getLanguagePlugins() as $plugin) { |
697 | foreach (OIDplus::getLanguagePlugins() as $plugin) { |
693 | $plugin->init($html); |
698 | $plugin->init($html); |
694 | } |
699 | } |
695 | 700 | ||
696 | // Initialize other stuff (i.e. things which require the logger!) |
701 | // Initialize other stuff (i.e. things which require the logger!) |
697 | 702 | ||
698 | OIDplus::recognizeSystemUrl(); // Make sure "last_known_system_url" is set |
703 | OIDplus::recognizeSystemUrl(); // Make sure "last_known_system_url" is set |
699 | OIDplus::recognizeVersion(); // Make sure "last_known_version" is set and a log entry is created |
704 | OIDplus::recognizeVersion(); // Make sure "last_known_version" is set and a log entry is created |
700 | } |
705 | } |
701 | 706 | ||
702 | # --- System URL, System ID, PKI, and other functions |
707 | # --- System URL, System ID, PKI, and other functions |
703 | 708 | ||
704 | public static function basePath() { |
709 | public static function basePath() { |
705 | return realpath(__DIR__ . '/../../'); |
710 | return realpath(__DIR__ . '/../../'); |
706 | } |
711 | } |
707 | 712 | ||
708 | private static function recognizeSystemUrl() { |
713 | private static function recognizeSystemUrl() { |
709 | try { |
714 | try { |
710 | $url = OIDplus::getSystemUrl(); |
715 | $url = OIDplus::getSystemUrl(); |
711 | OIDplus::config()->setValue('last_known_system_url', $url); |
716 | OIDplus::config()->setValue('last_known_system_url', $url); |
712 | } catch (Exception $e) { |
717 | } catch (Exception $e) { |
713 | } |
718 | } |
714 | } |
719 | } |
715 | 720 | ||
716 | public static function getSystemUrl($relative=false) { |
721 | public static function getSystemUrl($relative=false) { |
717 | if (!$relative) { |
722 | if (!$relative) { |
718 | $res = OIDplus::baseConfig()->getValue('EXPLICIT_ABSOLUTE_SYSTEM_URL', ''); |
723 | $res = OIDplus::baseConfig()->getValue('EXPLICIT_ABSOLUTE_SYSTEM_URL', ''); |
719 | if ($res !== '') { |
724 | if ($res !== '') { |
720 | return $res; |
725 | return $res; |
721 | } |
726 | } |
722 | } |
727 | } |
723 | 728 | ||
724 | if (!isset($_SERVER["SCRIPT_NAME"])) return false; |
729 | if (!isset($_SERVER["SCRIPT_NAME"])) return false; |
725 | 730 | ||
726 | $test_dir = dirname($_SERVER['SCRIPT_FILENAME']); |
731 | $test_dir = dirname($_SERVER['SCRIPT_FILENAME']); |
727 | $test_dir = str_replace('\\', '/', $test_dir); |
732 | $test_dir = str_replace('\\', '/', $test_dir); |
728 | $c = 0; |
733 | $c = 0; |
729 | while (!file_exists($test_dir.'/oidplus_base.js')) { |
734 | while (!file_exists($test_dir.'/oidplus_base.js')) { |
730 | $test_dir = dirname($test_dir); |
735 | $test_dir = dirname($test_dir); |
731 | $c++; |
736 | $c++; |
732 | if ($c == 1000) return false; |
737 | if ($c == 1000) return false; |
733 | } |
738 | } |
734 | 739 | ||
735 | $res = dirname($_SERVER['SCRIPT_NAME'].'xxx'); |
740 | $res = dirname($_SERVER['SCRIPT_NAME'].'xxx'); |
736 | 741 | ||
737 | for ($i=1; $i<=$c; $i++) { |
742 | for ($i=1; $i<=$c; $i++) { |
738 | $res = dirname($res); |
743 | $res = dirname($res); |
739 | } |
744 | } |
740 | 745 | ||
741 | $res = str_replace('\\', '/', $res); |
746 | $res = str_replace('\\', '/', $res); |
742 | if ($res == '/') $res = ''; |
747 | if ($res == '/') $res = ''; |
743 | $res .= '/'; |
748 | $res .= '/'; |
744 | 749 | ||
745 | if (!$relative) { |
750 | if (!$relative) { |
746 | if (php_sapi_name() == 'cli') { |
751 | if (php_sapi_name() == 'cli') { |
747 | try { |
752 | try { |
748 | return OIDplus::config()->getValue('last_known_system_url', false); |
753 | return OIDplus::config()->getValue('last_known_system_url', false); |
749 | } catch (Exception $e) { |
754 | } catch (Exception $e) { |
750 | return false; |
755 | return false; |
751 | } |
756 | } |
752 | } |
757 | } |
753 | 758 | ||
754 | $is_ssl = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on'); |
759 | $is_ssl = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on'); |
755 | $protocol = $is_ssl ? 'https' : 'http'; // do not translate |
760 | $protocol = $is_ssl ? 'https' : 'http'; // do not translate |
756 | $host = $_SERVER['HTTP_HOST']; // includes port if it is not 80/443 |
761 | $host = $_SERVER['HTTP_HOST']; // includes port if it is not 80/443 |
757 | $res = $protocol.'://'.$host.$res; |
762 | $res = $protocol.'://'.$host.$res; |
758 | } |
763 | } |
759 | 764 | ||
760 | return $res; |
765 | return $res; |
761 | } |
766 | } |
762 | 767 | ||
763 | private static $system_id_cache = null; |
768 | private static $system_id_cache = null; |
764 | public static function getSystemId($oid=false) { |
769 | public static function getSystemId($oid=false) { |
765 | if (!is_null(self::$system_id_cache)) { |
770 | if (!is_null(self::$system_id_cache)) { |
766 | $out = self::$system_id_cache; |
771 | $out = self::$system_id_cache; |
767 | } else { |
772 | } else { |
768 | $out = false; |
773 | $out = false; |
769 | 774 | ||
770 | if (self::getPkiStatus(true)) { |
775 | if (self::getPkiStatus(true)) { |
771 | $pubKey = OIDplus::config()->getValue('oidplus_public_key'); |
776 | $pubKey = OIDplus::config()->getValue('oidplus_public_key'); |
772 | $m = array(); |
777 | $m = array(); |
773 | if (preg_match('@BEGIN PUBLIC KEY\-+(.+)\-+END PUBLIC KEY@ismU', $pubKey, $m)) { |
778 | if (preg_match('@BEGIN PUBLIC KEY\-+(.+)\-+END PUBLIC KEY@ismU', $pubKey, $m)) { |
774 | $out = smallhash(base64_decode($m[1])); |
779 | $out = smallhash(base64_decode($m[1])); |
775 | } |
780 | } |
776 | } |
781 | } |
777 | self::$system_id_cache = $out; |
782 | self::$system_id_cache = $out; |
778 | } |
783 | } |
779 | if (!$out) return false; |
784 | if (!$out) return false; |
780 | return ($oid ? '1.3.6.1.4.1.37476.30.9.' : '').$out; |
785 | return ($oid ? '1.3.6.1.4.1.37476.30.9.' : '').$out; |
781 | } |
786 | } |
782 | 787 | ||
783 | public static function getPkiStatus($try_generate=true) { |
788 | public static function getPkiStatus($try_generate=true) { |
784 | if (!function_exists('openssl_pkey_new')) return false; |
789 | if (!function_exists('openssl_pkey_new')) return false; |
785 | 790 | ||
786 | $privKey = OIDplus::config()->getValue('oidplus_private_key'); |
791 | $privKey = OIDplus::config()->getValue('oidplus_private_key'); |
787 | $pubKey = OIDplus::config()->getValue('oidplus_public_key'); |
792 | $pubKey = OIDplus::config()->getValue('oidplus_public_key'); |
788 | 793 | ||
789 | if ($try_generate && !verify_private_public_key($privKey, $pubKey)) { |
794 | if ($try_generate && !verify_private_public_key($privKey, $pubKey)) { |
790 | $pkey_config = array( |
795 | $pkey_config = array( |
791 | "digest_alg" => "sha512", |
796 | "digest_alg" => "sha512", |
792 | "private_key_bits" => 2048, |
797 | "private_key_bits" => 2048, |
793 | "private_key_type" => OPENSSL_KEYTYPE_RSA, |
798 | "private_key_type" => OPENSSL_KEYTYPE_RSA, |
794 | ); |
799 | ); |
795 | 800 | ||
796 | // Create the private and public key |
801 | // Create the private and public key |
797 | $res = openssl_pkey_new($pkey_config); |
802 | $res = openssl_pkey_new($pkey_config); |
798 | 803 | ||
799 | if (!$res) return false; |
804 | if (!$res) return false; |
800 | 805 | ||
801 | // Extract the private key from $res to $privKey |
806 | // Extract the private key from $res to $privKey |
802 | openssl_pkey_export($res, $privKey); |
807 | openssl_pkey_export($res, $privKey); |
803 | 808 | ||
804 | // Extract the public key from $res to $pubKey |
809 | // Extract the public key from $res to $pubKey |
805 | $pubKey = openssl_pkey_get_details($res)["key"]; |
810 | $pubKey = openssl_pkey_get_details($res)["key"]; |
806 | 811 | ||
807 | // Log |
812 | // Log |
808 | OIDplus::logger()->log("[INFO]A!", "Generating new SystemID using a new key pair"); |
813 | OIDplus::logger()->log("[INFO]A!", "Generating new SystemID using a new key pair"); |
809 | 814 | ||
810 | // Save the key pair to database |
815 | // Save the key pair to database |
811 | OIDplus::config()->setValue('oidplus_private_key', $privKey); |
816 | OIDplus::config()->setValue('oidplus_private_key', $privKey); |
812 | OIDplus::config()->setValue('oidplus_public_key', $pubKey); |
817 | OIDplus::config()->setValue('oidplus_public_key', $pubKey); |
813 | 818 | ||
814 | // Log the new system ID |
819 | // Log the new system ID |
815 | $m = array(); |
820 | $m = array(); |
816 | if (preg_match('@BEGIN PUBLIC KEY\-+(.+)\-+END PUBLIC KEY@ismU', $pubKey, $m)) { |
821 | if (preg_match('@BEGIN PUBLIC KEY\-+(.+)\-+END PUBLIC KEY@ismU', $pubKey, $m)) { |
817 | $system_id = smallhash(base64_decode($m[1])); |
822 | $system_id = smallhash(base64_decode($m[1])); |
818 | OIDplus::logger()->log("[INFO]A!", "Your SystemID is now $system_id"); |
823 | OIDplus::logger()->log("[INFO]A!", "Your SystemID is now $system_id"); |
819 | } |
824 | } |
820 | } |
825 | } |
821 | 826 | ||
822 | return verify_private_public_key($privKey, $pubKey); |
827 | return verify_private_public_key($privKey, $pubKey); |
823 | } |
828 | } |
824 | 829 | ||
825 | public static function getInstallType() { |
830 | public static function getInstallType() { |
826 | if (!file_exists(OIDplus::basePath().'/oidplus_version.txt') && !is_dir(OIDplus::basePath().'/.svn')) { |
831 | if (!file_exists(OIDplus::basePath().'/oidplus_version.txt') && !is_dir(OIDplus::basePath().'/.svn')) { |
827 | return 'unknown'; // do not translate |
832 | return 'unknown'; // do not translate |
828 | } |
833 | } |
829 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt') && is_dir(OIDplus::basePath().'/.svn')) { |
834 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt') && is_dir(OIDplus::basePath().'/.svn')) { |
830 | return 'ambigous'; // do not translate |
835 | return 'ambigous'; // do not translate |
831 | } |
836 | } |
832 | if (is_dir(OIDplus::basePath().'/.svn')) { |
837 | if (is_dir(OIDplus::basePath().'/.svn')) { |
833 | return 'svn-wc'; // do not translate |
838 | return 'svn-wc'; // do not translate |
834 | } |
839 | } |
835 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt')) { |
840 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt')) { |
836 | return 'svn-snapshot'; // do not translate |
841 | return 'svn-snapshot'; // do not translate |
837 | } |
842 | } |
838 | } |
843 | } |
839 | 844 | ||
840 | private static function recognizeVersion() { |
845 | private static function recognizeVersion() { |
841 | try { |
846 | try { |
842 | $ver_prev = OIDplus::config()->getValue("last_known_version"); |
847 | $ver_prev = OIDplus::config()->getValue("last_known_version"); |
843 | $ver_now = OIDplus::getVersion(); |
848 | $ver_now = OIDplus::getVersion(); |
844 | if (($ver_now != '') && ($ver_prev != '') && ($ver_now != $ver_prev)) { |
849 | if (($ver_now != '') && ($ver_prev != '') && ($ver_now != $ver_prev)) { |
845 | OIDplus::logger()->log("[INFO]A!", "System version changed from '$ver_prev' to '$ver_now'"); |
850 | OIDplus::logger()->log("[INFO]A!", "System version changed from '$ver_prev' to '$ver_now'"); |
846 | } |
851 | } |
847 | OIDplus::config()->setValue("last_known_version", $ver_now); |
852 | OIDplus::config()->setValue("last_known_version", $ver_now); |
848 | } catch (Exception $e) { |
853 | } catch (Exception $e) { |
849 | } |
854 | } |
850 | } |
855 | } |
851 | 856 | ||
852 | public static function getVersion() { |
857 | public static function getVersion() { |
853 | static $cachedVersion = null; |
858 | static $cachedVersion = null; |
854 | if (!is_null($cachedVersion)) { |
859 | if (!is_null($cachedVersion)) { |
855 | return $cachedVersion; |
860 | return $cachedVersion; |
856 | } |
861 | } |
857 | 862 | ||
858 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt') && is_dir(OIDplus::basePath().'/.svn')) { |
863 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt') && is_dir(OIDplus::basePath().'/.svn')) { |
859 | return ($cachedVersion = false); // version is ambiguous |
864 | return ($cachedVersion = false); // version is ambiguous |
860 | } |
865 | } |
861 | 866 | ||
862 | if (is_dir(OIDplus::basePath().'/.svn')) { |
867 | if (is_dir(OIDplus::basePath().'/.svn')) { |
863 | // Try to get the version via SQLite3 |
868 | // Try to get the version via SQLite3 |
864 | if (class_exists('SQLite3')) { |
869 | if (class_exists('SQLite3')) { |
865 | try { |
870 | try { |
866 | $db = new SQLite3(OIDplus::basePath().'/.svn/wc.db'); |
871 | $db = new SQLite3(OIDplus::basePath().'/.svn/wc.db'); |
867 | $results = $db->query('SELECT MIN(revision) AS rev FROM NODES_BASE'); |
872 | $results = $db->query('SELECT MIN(revision) AS rev FROM NODES_BASE'); |
868 | while ($row = $results->fetchArray()) { |
873 | while ($row = $results->fetchArray()) { |
869 | return ($cachedVersion = 'svn-'.$row['rev']); // do not translate |
874 | return ($cachedVersion = 'svn-'.$row['rev']); // do not translate |
870 | } |
875 | } |
871 | $db->close(); |
876 | $db->close(); |
872 | $db = null; |
877 | $db = null; |
873 | } catch (Exception $e) { |
878 | } catch (Exception $e) { |
874 | } |
879 | } |
875 | } |
880 | } |
876 | if (class_exists('PDO')) { |
881 | if (class_exists('PDO')) { |
877 | try { |
882 | try { |
878 | $pdo = new PDO('sqlite:' . OIDplus::basePath().'/.svn/wc.db'); |
883 | $pdo = new PDO('sqlite:' . OIDplus::basePath().'/.svn/wc.db'); |
879 | $res = $pdo->query('SELECT MIN(revision) AS rev FROM NODES_BASE'); |
884 | $res = $pdo->query('SELECT MIN(revision) AS rev FROM NODES_BASE'); |
880 | $row = $res->fetch(); |
885 | $row = $res->fetch(); |
881 | if ($row !== false) { |
886 | if ($row !== false) { |
882 | return ($cachedVersion = 'svn-'.$row['rev']); // do not translate |
887 | return ($cachedVersion = 'svn-'.$row['rev']); // do not translate |
883 | } |
888 | } |
884 | $pdo = null; |
889 | $pdo = null; |
885 | } catch (Exception $e) { |
890 | } catch (Exception $e) { |
886 | } |
891 | } |
887 | } |
892 | } |
888 | 893 | ||
889 | // Try to find out the SVN version using the shell |
894 | // Try to find out the SVN version using the shell |
890 | // We don't prioritize this method, because a failed shell access will flood the apache error log with STDERR messages |
895 | // We don't prioritize this method, because a failed shell access will flood the apache error log with STDERR messages |
891 | $output = @shell_exec('svnversion '.escapeshellarg(OIDplus::basePath())); |
896 | $output = @shell_exec('svnversion '.escapeshellarg(OIDplus::basePath())); |
892 | $match = array(); |
897 | $match = array(); |
893 | if (preg_match('/\d+/', $output, $match)) { |
898 | if (preg_match('/\d+/', $output, $match)) { |
894 | return ($cachedVersion = 'svn-'.$match[0]); // do not translate |
899 | return ($cachedVersion = 'svn-'.$match[0]); // do not translate |
895 | } |
900 | } |
896 | 901 | ||
897 | $output = @shell_exec('svn info '.escapeshellarg(OIDplus::basePath())); |
902 | $output = @shell_exec('svn info '.escapeshellarg(OIDplus::basePath())); |
898 | if (preg_match('/Revision:\s*(\d+)/m', $output, $match)) { // do not translate |
903 | if (preg_match('/Revision:\s*(\d+)/m', $output, $match)) { // do not translate |
899 | return ($cachedVersion = 'svn-'.$match[1]); // do not translate |
904 | return ($cachedVersion = 'svn-'.$match[1]); // do not translate |
900 | } |
905 | } |
901 | } |
906 | } |
902 | 907 | ||
903 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt')) { |
908 | if (file_exists(OIDplus::basePath().'/oidplus_version.txt')) { |
904 | $cont = file_get_contents(OIDplus::basePath().'/oidplus_version.txt'); |
909 | $cont = file_get_contents(OIDplus::basePath().'/oidplus_version.txt'); |
905 | $m = array(); |
910 | $m = array(); |
906 | if (preg_match('@Revision (\d+)@', $cont, $m)) // do not translate |
911 | if (preg_match('@Revision (\d+)@', $cont, $m)) // do not translate |
907 | return ($cachedVersion = 'svn-'.$m[1]); // do not translate |
912 | return ($cachedVersion = 'svn-'.$m[1]); // do not translate |
908 | } |
913 | } |
909 | 914 | ||
910 | return ($cachedVersion = false); |
915 | return ($cachedVersion = false); |
911 | } |
916 | } |
912 | 917 | ||
913 | private static $sslAvailableCache = null; |
918 | private static $sslAvailableCache = null; |
914 | public static function isSslAvailable() { |
919 | public static function isSslAvailable() { |
915 | if (!is_null(self::$sslAvailableCache)) return self::$sslAvailableCache; |
920 | if (!is_null(self::$sslAvailableCache)) return self::$sslAvailableCache; |
916 | 921 | ||
917 | if (php_sapi_name() == 'cli') { |
922 | if (php_sapi_name() == 'cli') { |
918 | self::$sslAvailableCache = false; |
923 | self::$sslAvailableCache = false; |
919 | return false; |
924 | return false; |
920 | } |
925 | } |
921 | 926 | ||
922 | $timeout = 2; |
927 | $timeout = 2; |
923 | $already_ssl = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == "on"); |
928 | $already_ssl = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == "on"); |
924 | $ssl_port = 443; |
929 | $ssl_port = 443; |
925 | $cookie_path = OIDplus::getSystemUrl(true); |
930 | $cookie_path = OIDplus::getSystemUrl(true); |
926 | if (empty($cookie_path)) $cookie_path = '/'; |
931 | if (empty($cookie_path)) $cookie_path = '/'; |
927 | 932 | ||
928 | $mode = OIDplus::baseConfig()->getValue('ENFORCE_SSL', 2/*auto*/); |
933 | $mode = OIDplus::baseConfig()->getValue('ENFORCE_SSL', 2/*auto*/); |
929 | 934 | ||
930 | if ($mode == 0) { |
935 | if ($mode == 0) { |
931 | // No SSL available |
936 | // No SSL available |
932 | self::$sslAvailableCache = $already_ssl; |
937 | self::$sslAvailableCache = $already_ssl; |
933 | return $already_ssl; |
938 | return $already_ssl; |
934 | } |
939 | } |
935 | 940 | ||
936 | if ($mode == 1) { |
941 | if ($mode == 1) { |
937 | // Force SSL |
942 | // Force SSL |
938 | if ($already_ssl) { |
943 | if ($already_ssl) { |
939 | self::$sslAvailableCache = true; |
944 | self::$sslAvailableCache = true; |
940 | return true; |
945 | return true; |
941 | } else { |
946 | } else { |
942 | $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
947 | $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
943 | header('Location:'.$location); |
948 | header('Location:'.$location); |
944 | die(_L('Redirecting to HTTPS...')); |
949 | die(_L('Redirecting to HTTPS...')); |
945 | self::$sslAvailableCache = true; |
950 | self::$sslAvailableCache = true; |
946 | return true; |
951 | return true; |
947 | } |
952 | } |
948 | } |
953 | } |
949 | 954 | ||
950 | if ($mode == 2) { |
955 | if ($mode == 2) { |
951 | // Automatic SSL detection |
956 | // Automatic SSL detection |
952 | 957 | ||
953 | if ($already_ssl) { |
958 | if ($already_ssl) { |
954 | // we are already on HTTPS |
959 | // we are already on HTTPS |
955 | setcookie('SSL_CHECK', '1', 0, $cookie_path, '', false, true); |
960 | setcookie('SSL_CHECK', '1', 0, $cookie_path, '', false, true); |
956 | self::$sslAvailableCache = true; |
961 | self::$sslAvailableCache = true; |
957 | return true; |
962 | return true; |
958 | } else { |
963 | } else { |
959 | if (isset($_COOKIE['SSL_CHECK'])) { |
964 | if (isset($_COOKIE['SSL_CHECK'])) { |
960 | // We already had the HTTPS detection done before. |
965 | // We already had the HTTPS detection done before. |
961 | if ($_COOKIE['SSL_CHECK']) { |
966 | if ($_COOKIE['SSL_CHECK']) { |
962 | // HTTPS was detected before, but we are HTTP. Redirect now |
967 | // HTTPS was detected before, but we are HTTP. Redirect now |
963 | $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
968 | $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
964 | header('Location:'.$location); |
969 | header('Location:'.$location); |
965 | die(_L('Redirecting to HTTPS...')); |
970 | die(_L('Redirecting to HTTPS...')); |
966 | self::$sslAvailableCache = true; |
971 | self::$sslAvailableCache = true; |
967 | return true; |
972 | return true; |
968 | } else { |
973 | } else { |
969 | // No HTTPS available. Do nothing. |
974 | // No HTTPS available. Do nothing. |
970 | self::$sslAvailableCache = false; |
975 | self::$sslAvailableCache = false; |
971 | return false; |
976 | return false; |
972 | } |
977 | } |
973 | } else { |
978 | } else { |
974 | // This is our first check (or the browser didn't accept the SSL_CHECK cookie) |
979 | // This is our first check (or the browser didn't accept the SSL_CHECK cookie) |
975 | $errno = -1; |
980 | $errno = -1; |
976 | $errstr = ''; |
981 | $errstr = ''; |
977 | if (@fsockopen($_SERVER['HTTP_HOST'], $ssl_port, $errno, $errstr, $timeout)) { |
982 | if (@fsockopen($_SERVER['HTTP_HOST'], $ssl_port, $errno, $errstr, $timeout)) { |
978 | // HTTPS detected. Redirect now, and remember that we had detected HTTPS |
983 | // HTTPS detected. Redirect now, and remember that we had detected HTTPS |
979 | setcookie('SSL_CHECK', '1', 0, $cookie_path, '', false, true); |
984 | setcookie('SSL_CHECK', '1', 0, $cookie_path, '', false, true); |
980 | $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
985 | $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
981 | header('Location:'.$location); |
986 | header('Location:'.$location); |
982 | die(_L('Redirecting to HTTPS...')); |
987 | die(_L('Redirecting to HTTPS...')); |
983 | self::$sslAvailableCache = true; |
988 | self::$sslAvailableCache = true; |
984 | return true; |
989 | return true; |
985 | } else { |
990 | } else { |
986 | // No HTTPS detected. Do nothing, and next time, don't try to detect HTTPS again. |
991 | // No HTTPS detected. Do nothing, and next time, don't try to detect HTTPS again. |
987 | setcookie('SSL_CHECK', '0', 0, $cookie_path, '', false, true); |
992 | setcookie('SSL_CHECK', '0', 0, $cookie_path, '', false, true); |
988 | self::$sslAvailableCache = false; |
993 | self::$sslAvailableCache = false; |
989 | return false; |
994 | return false; |
990 | } |
995 | } |
991 | } |
996 | } |
992 | } |
997 | } |
993 | } |
998 | } |
994 | } |
999 | } |
995 | 1000 | ||
996 | public static function webpath($target) { |
1001 | public static function webpath($target) { |
997 | $dir = __DIR__; |
1002 | $dir = __DIR__; |
998 | $dir = dirname($dir); |
1003 | $dir = dirname($dir); |
999 | $dir = dirname($dir); |
1004 | $dir = dirname($dir); |
1000 | $target = substr($target, strlen($dir)+1, strlen($target)-strlen($dir)-1); |
1005 | $target = substr($target, strlen($dir)+1, strlen($target)-strlen($dir)-1); |
1001 | if ($target != '') { |
1006 | if ($target != '') { |
1002 | $target = str_replace('\\','/',$target).'/'; |
1007 | $target = str_replace('\\','/',$target).'/'; |
1003 | } |
1008 | } |
1004 | return $target; |
1009 | return $target; |
1005 | } |
1010 | } |
1006 | 1011 | ||
1007 | public static function getAvailableLangs() { |
1012 | public static function getAvailableLangs() { |
1008 | $langs = array(); |
1013 | $langs = array(); |
1009 | foreach (OIDplus::getAllPluginManifests('language') as $pluginManifest) { |
1014 | foreach (OIDplus::getAllPluginManifests('language') as $pluginManifest) { |
1010 | $code = $pluginManifest->getLanguageCode(); |
1015 | $code = $pluginManifest->getLanguageCode(); |
1011 | $langs[] = $code; |
1016 | $langs[] = $code; |
1012 | } |
1017 | } |
1013 | return $langs; |
1018 | return $langs; |
1014 | } |
1019 | } |
1015 | 1020 | ||
1016 | public static function getCurrentLang() { |
1021 | public static function getCurrentLang() { |
1017 | if (isset($_GET['lang'])) { |
1022 | if (isset($_GET['lang'])) { |
1018 | $lang = $_GET['lang']; |
1023 | $lang = $_GET['lang']; |
1019 | } else if (isset($_POST['lang'])) { |
1024 | } else if (isset($_POST['lang'])) { |
1020 | $lang = $_POST['lang']; |
1025 | $lang = $_POST['lang']; |
1021 | } else if (isset($_COOKIE['LANGUAGE'])) { |
1026 | } else if (isset($_COOKIE['LANGUAGE'])) { |
1022 | $lang = $_COOKIE['LANGUAGE']; |
1027 | $lang = $_COOKIE['LANGUAGE']; |
1023 | } else { |
1028 | } else { |
1024 | $lang = self::DEFAULT_LANGUAGE; |
1029 | $lang = self::DEFAULT_LANGUAGE; |
1025 | } |
1030 | } |
1026 | $lang = substr(preg_replace('@[^a-z]@ismU', '', $lang),0,4); // sanitize |
1031 | $lang = substr(preg_replace('@[^a-z]@ismU', '', $lang),0,4); // sanitize |
1027 | return $lang; |
1032 | return $lang; |
1028 | } |
1033 | } |
1029 | 1034 | ||
1030 | public static function handleLangArgument() { |
1035 | public static function handleLangArgument() { |
1031 | if (isset($_GET['lang'])) { |
1036 | if (isset($_GET['lang'])) { |
1032 | // The "?lang=" argument is only for NoScript-Browsers/SearchEngines |
1037 | // The "?lang=" argument is only for NoScript-Browsers/SearchEngines |
1033 | // In case someone who has JavaScript clicks a ?lang= link, they should get |
1038 | // In case someone who has JavaScript clicks a ?lang= link, they should get |
1034 | // the page in that language, but the cookie must be set, otherwise |
1039 | // the page in that language, but the cookie must be set, otherwise |
1035 | // the menu and other stuff would be in their cookie-based-language and not the |
1040 | // the menu and other stuff would be in their cookie-based-language and not the |
1036 | // argument-based-language. |
1041 | // argument-based-language. |
1037 | $cookie_path = OIDplus::getSystemUrl(true); |
1042 | $cookie_path = OIDplus::getSystemUrl(true); |
1038 | if (empty($cookie_path)) $cookie_path = '/'; |
1043 | if (empty($cookie_path)) $cookie_path = '/'; |
1039 | setcookie('LANGUAGE', $_GET['lang'], 0, $cookie_path, '', false, false/*HttpOnly off, because JavaScript also needs translation*/); |
1044 | setcookie('LANGUAGE', $_GET['lang'], 0, $cookie_path, '', false, false/*HttpOnly off, because JavaScript also needs translation*/); |
1040 | } else if (isset($_POST['lang'])) { |
1045 | } else if (isset($_POST['lang'])) { |
1041 | $cookie_path = OIDplus::getSystemUrl(true); |
1046 | $cookie_path = OIDplus::getSystemUrl(true); |
1042 | if (empty($cookie_path)) $cookie_path = '/'; |
1047 | if (empty($cookie_path)) $cookie_path = '/'; |
1043 | setcookie('LANGUAGE', $_POST['lang'], 0, $cookie_path, '', false, false/*HttpOnly off, because JavaScript also needs translation*/); |
1048 | setcookie('LANGUAGE', $_POST['lang'], 0, $cookie_path, '', false, false/*HttpOnly off, because JavaScript also needs translation*/); |
1044 | } |
1049 | } |
1045 | } |
1050 | } |
1046 | 1051 | ||
1047 | public static function getTranslationArray() { |
1052 | public static function getTranslationArray() { |
1048 | $translation_array = array(); |
1053 | $translation_array = array(); |
1049 | foreach (OIDplus::getAllPluginManifests('language') as $pluginManifest) { |
1054 | foreach (OIDplus::getAllPluginManifests('language') as $pluginManifest) { |
1050 | $lang = $pluginManifest->getLanguageCode(); |
1055 | $lang = $pluginManifest->getLanguageCode(); |
1051 | $translation_array[$lang] = array(); |
1056 | $translation_array[$lang] = array(); |
1052 | if (strpos($lang,'/') !== false) continue; // just to be sure |
1057 | if (strpos($lang,'/') !== false) continue; // just to be sure |
1053 | if (strpos($lang,'\\') !== false) continue; // just to be sure |
1058 | if (strpos($lang,'\\') !== false) continue; // just to be sure |
1054 | if (strpos($lang,'..') !== false) continue; // just to be sure |
1059 | if (strpos($lang,'..') !== false) continue; // just to be sure |
1055 | 1060 | ||
1056 | $wildcard = $pluginManifest->getLanguageMessages(); |
1061 | $wildcard = $pluginManifest->getLanguageMessages(); |
1057 | if (strpos($wildcard,'/') !== false) continue; // just to be sure |
1062 | if (strpos($wildcard,'/') !== false) continue; // just to be sure |
1058 | if (strpos($wildcard,'\\') !== false) continue; // just to be sure |
1063 | if (strpos($wildcard,'\\') !== false) continue; // just to be sure |
1059 | if (strpos($wildcard,'..') !== false) continue; // just to be sure |
1064 | if (strpos($wildcard,'..') !== false) continue; // just to be sure |
1060 | 1065 | ||
1061 | $translation_files = glob(__DIR__.'/../../plugins/language/'.$lang.'/'.$wildcard); |
1066 | $translation_files = glob(__DIR__.'/../../plugins/language/'.$lang.'/'.$wildcard); |
1062 | sort($translation_files); |
1067 | sort($translation_files); |
1063 | foreach ($translation_files as $translation_file) { |
1068 | foreach ($translation_files as $translation_file) { |
1064 | if (!file_exists($translation_file)) continue; |
1069 | if (!file_exists($translation_file)) continue; |
1065 | $xml = @simplexml_load_string(file_get_contents($translation_file)); |
1070 | $xml = @simplexml_load_string(file_get_contents($translation_file)); |
1066 | if (!$xml) continue; // if there is an UTF-8 or parsing error, don't output any errors, otherwise the JavaScript is corrupt and the page won't render correctly |
1071 | if (!$xml) continue; // if there is an UTF-8 or parsing error, don't output any errors, otherwise the JavaScript is corrupt and the page won't render correctly |
1067 | foreach ($xml->message as $msg) { |
1072 | foreach ($xml->message as $msg) { |
1068 | $src = trim($msg->source->__toString()); |
1073 | $src = trim($msg->source->__toString()); |
1069 | $dst = trim($msg->target->__toString()); |
1074 | $dst = trim($msg->target->__toString()); |
1070 | $translation_array[$lang][$src] = $dst; |
1075 | $translation_array[$lang][$src] = $dst; |
1071 | } |
1076 | } |
1072 | } |
1077 | } |
1073 | } |
1078 | } |
1074 | return $translation_array; |
1079 | return $translation_array; |
1075 | } |
1080 | } |
1076 | 1081 | ||
1077 | } |
1082 | } |
1078 | 1083 |