File inc/keys.inc.php changed (mode: 100644) (index c2e75a2..c146b8b) |
... |
... |
function rg_keys_remove($db, $ui, $list) |
297 |
297 |
rg_sql_free_result($res); |
rg_sql_free_result($res); |
298 |
298 |
|
|
299 |
299 |
$event = array("category" => 1001, "prio" => 50, |
$event = array("category" => 1001, "prio" => 50, |
300 |
|
'ui' => array('email' => $ui['confirmed'] > 0 ? $ui['email'] : ""), |
|
|
300 |
|
'ui' => array( |
|
301 |
|
'uid' => $ui['uid'], |
|
302 |
|
'email' => $ui['confirmed'] > 0 ? $ui['email'] : "", |
|
303 |
|
), |
301 |
304 |
"keys" => implode(",", $my_list)); |
"keys" => implode(",", $my_list)); |
302 |
305 |
$r = rg_event_add($db, $event); |
$r = rg_event_add($db, $event); |
303 |
306 |
if ($r !== TRUE) { |
if ($r !== TRUE) { |
|
... |
... |
function rg_keys_add($db, $ui, $key) |
403 |
406 |
rg_sql_free_result($res); |
rg_sql_free_result($res); |
404 |
407 |
|
|
405 |
408 |
$event = array("category" => 1000, "prio" => 50, |
$event = array("category" => 1000, "prio" => 50, |
406 |
|
'ui' => array('email' => $ui['confirmed'] > 0 ? $ui['email'] : ""), |
|
|
409 |
|
'ui' => array( |
|
410 |
|
'uid' => $ui['uid'], |
|
411 |
|
'email' => $ui['confirmed'] > 0 ? $ui['email'] : "" |
|
412 |
|
), |
407 |
413 |
'ki' => $ki, |
'ki' => $ki, |
408 |
414 |
'key_id' => $key_id); |
'key_id' => $key_id); |
409 |
415 |
$r = rg_event_add($db, $event); |
$r = rg_event_add($db, $event); |
File inc/user.inc.php changed (mode: 100644) (index b7511a6..e739774) |
... |
... |
function rg_user_event_login($db, $event) |
74 |
74 |
{ |
{ |
75 |
75 |
$ret = FALSE; |
$ret = FALSE; |
76 |
76 |
while (1) { |
while (1) { |
77 |
|
$r = rg_user_set_last_seen($db, $event['uid'], $event['itime'], |
|
|
77 |
|
$r = rg_user_set_last_seen($db, $event['ui']['uid'], $event['itime'], |
78 |
78 |
$event['ip']); |
$event['ip']); |
79 |
79 |
if ($r !== TRUE) |
if ($r !== TRUE) |
80 |
80 |
break; |
break; |
|
... |
... |
function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip, |
925 |
925 |
} |
} |
926 |
926 |
|
|
927 |
927 |
$event = array( |
$event = array( |
|
928 |
|
'ui' => array('uid' => $ui0['uid']), |
928 |
929 |
'category' => 2001, |
'category' => 2001, |
929 |
930 |
'prio' => 100, |
'prio' => 100, |
930 |
|
'uid' => $ui0['uid'], |
|
931 |
931 |
'itime' => time()); |
'itime' => time()); |
932 |
932 |
$r = rg_event_add($db, $event); |
$r = rg_event_add($db, $event); |
933 |
933 |
if ($r !== TRUE) { |
if ($r !== TRUE) { |
|
... |
... |
function rg_user_forgot_pass_uid($db, $token) |
1190 |
1190 |
*/ |
*/ |
1191 |
1191 |
function rg_user_forgot_pass_mail_prepare($db, $email) |
function rg_user_forgot_pass_mail_prepare($db, $email) |
1192 |
1192 |
{ |
{ |
|
1193 |
|
rg_prof_start('user_forgot_pass_mail_prepare'); |
1193 |
1194 |
rg_log_enter("user_forgot_pass_mail_prepare: email=$email"); |
rg_log_enter("user_forgot_pass_mail_prepare: email=$email"); |
1194 |
1195 |
|
|
1195 |
1196 |
$ret = array(); |
$ret = array(); |
|
... |
... |
function rg_user_forgot_pass_mail_prepare($db, $email) |
1234 |
1235 |
rg_log("DEBUG: user_forgot_pass_mail_prepare: ret=" . rg_array2string($ret)); |
rg_log("DEBUG: user_forgot_pass_mail_prepare: ret=" . rg_array2string($ret)); |
1235 |
1236 |
|
|
1236 |
1237 |
rg_log_exit(); |
rg_log_exit(); |
|
1238 |
|
rg_prof_end('user_forgot_pass_mail_prepare'); |
1237 |
1239 |
return $ret; |
return $ret; |
1238 |
1240 |
} |
} |
1239 |
1241 |
|
|
1240 |
1242 |
/* |
/* |
1241 |
1243 |
* Reset password function (send mail) |
* Reset password function (send mail) |
|
1244 |
|
* TODO: Convert to rg_mail_template! Maybe also other functions! |
1242 |
1245 |
*/ |
*/ |
1243 |
1246 |
function rg_user_forgot_pass_mail($db, $email) |
function rg_user_forgot_pass_mail($db, $email) |
1244 |
1247 |
{ |
{ |
1245 |
1248 |
global $php_errormsg; |
global $php_errormsg; |
1246 |
1249 |
global $rg_admin_name, $rg_admin_email; |
global $rg_admin_name, $rg_admin_email; |
1247 |
1250 |
|
|
1248 |
|
rg_log("user_forgot_pass_mail: email=$email"); |
|
|
1251 |
|
rg_prof_start('user_forgot_pass_mail'); |
|
1252 |
|
rg_log_enter("user_forgot_pass_mail: email=$email"); |
1249 |
1253 |
|
|
1250 |
1254 |
$ret = array(); |
$ret = array(); |
1251 |
1255 |
$ret['ok'] = 0; |
$ret['ok'] = 0; |
|
... |
... |
function rg_user_forgot_pass_mail($db, $email) |
1284 |
1288 |
|
|
1285 |
1289 |
rg_log("DEBUG: user_forgot_pass_mail: ret=" . rg_array2string($ret) . "."); |
rg_log("DEBUG: user_forgot_pass_mail: ret=" . rg_array2string($ret) . "."); |
1286 |
1290 |
rg_log_exit(); |
rg_log_exit(); |
|
1291 |
|
rg_prof_end('user_forgot_pass_mail'); |
1287 |
1292 |
return $ret; |
return $ret; |
1288 |
1293 |
} |
} |
1289 |
1294 |
|
|
|
... |
... |
function rg_user_forgot_pass_mail($db, $email) |
1292 |
1297 |
*/ |
*/ |
1293 |
1298 |
function rg_user_forgot_pass_destroy($db, $uid) |
function rg_user_forgot_pass_destroy($db, $uid) |
1294 |
1299 |
{ |
{ |
|
1300 |
|
rg_prof_start('user_forgot_pass_destroy'); |
1295 |
1301 |
rg_log_enter("user_forgot_pass_destroy: uid=$uid"); |
rg_log_enter("user_forgot_pass_destroy: uid=$uid"); |
1296 |
1302 |
|
|
1297 |
1303 |
$ret = FALSE; |
$ret = FALSE; |
|
... |
... |
function rg_user_forgot_pass_destroy($db, $uid) |
1310 |
1316 |
} |
} |
1311 |
1317 |
|
|
1312 |
1318 |
rg_log_exit(); |
rg_log_exit(); |
|
1319 |
|
rg_prof_end('user_forgot_pass_destroy'); |
1313 |
1320 |
return $ret; |
return $ret; |
1314 |
1321 |
} |
} |
1315 |
1322 |
|
|
File inc/webhooks.inc.php changed (mode: 100644) (index 4306a6f..29610f0) |
... |
... |
function rg_wh_send_one($db, $event) |
93 |
93 |
curl_setopt($c, CURLOPT_SSLCERT, $cert_file); |
curl_setopt($c, CURLOPT_SSLCERT, $cert_file); |
94 |
94 |
} else { |
} else { |
95 |
95 |
rg_log('DEBUG: will NOT provide client cert...'); |
rg_log('DEBUG: will NOT provide client cert...'); |
|
96 |
|
// TODO: Somehow, without next line, the cert is still sent! |
|
97 |
|
curl_setopt($c, CURLOPT_SSLCERT, FALSE); |
96 |
98 |
} |
} |
97 |
99 |
|
|
98 |
100 |
if (!empty($info['client_ca_cert'])) { |
if (!empty($info['client_ca_cert'])) { |
|
... |
... |
function rg_wh_send_one($db, $event) |
102 |
104 |
break; |
break; |
103 |
105 |
|
|
104 |
106 |
curl_setopt($c, CURLOPT_CAINFO, $ca_file); |
curl_setopt($c, CURLOPT_CAINFO, $ca_file); |
|
107 |
|
} else { |
|
108 |
|
curl_setopt($c, CURLOPT_CAINFO, FALSE); |
105 |
109 |
} |
} |
106 |
110 |
|
|
107 |
111 |
$r = curl_exec($c); |
$r = curl_exec($c); |
File tests/cache.php changed (mode: 100644) (index 137abe8..69233f4) |
... |
... |
rg_cache_core_unset("x::y::z"); |
49 |
49 |
$e = FALSE; |
$e = FALSE; |
50 |
50 |
$r = rg_cache_core_get("x::y::z"); |
$r = rg_cache_core_get("x::y::z"); |
51 |
51 |
if ($r !== $e) { |
if ($r !== $e) { |
52 |
|
print_r($rg_cache); |
|
|
52 |
|
rg_log_ml('rg_cache: ' . print_r($rg_cache, TRUE)); |
53 |
53 |
rg_log("r must be FALSE ($r)!"); |
rg_log("r must be FALSE ($r)!"); |
54 |
54 |
exit(1); |
exit(1); |
55 |
55 |
} |
} |
|
... |
... |
rg_cache_core_apush("v", "aa"); |
62 |
62 |
$e = "0=[1] 1=[2] 2=[aa]"; |
$e = "0=[1] 1=[2] 2=[aa]"; |
63 |
63 |
$r = rg_cache_core_adump("v"); |
$r = rg_cache_core_adump("v"); |
64 |
64 |
if ($r !== $e) { |
if ($r !== $e) { |
65 |
|
print_r($rg_cache); |
|
|
65 |
|
rg_log_ml('rg_cache: ' . print_r($rg_cache, TRUE)); |
66 |
66 |
rg_log("[$r] != [$e]!"); |
rg_log("[$r] != [$e]!"); |
67 |
67 |
exit(1); |
exit(1); |
68 |
68 |
} |
} |
|
... |
... |
if ($r !== $e) { |
70 |
70 |
$e = "aa"; |
$e = "aa"; |
71 |
71 |
$r = rg_cache_core_apop("v"); |
$r = rg_cache_core_apop("v"); |
72 |
72 |
if ($r !== $e) { |
if ($r !== $e) { |
73 |
|
print_r($rg_cache); |
|
|
73 |
|
rg_log_ml('rg_cache: ' . print_r($rg_cache, TRUE)); |
74 |
74 |
rg_log("[$r] != [$e]!"); |
rg_log("[$r] != [$e]!"); |
75 |
75 |
exit(1); |
exit(1); |
76 |
76 |
} |
} |
|
... |
... |
if ($r !== $e) { |
78 |
78 |
$e = "1"; |
$e = "1"; |
79 |
79 |
$r = rg_cache_core_ashift("v"); |
$r = rg_cache_core_ashift("v"); |
80 |
80 |
if ($r !== $e) { |
if ($r !== $e) { |
81 |
|
print_r($rg_cache); |
|
|
81 |
|
rg_log_ml('rg_cache: ' . print_r($rg_cache, TRUE)); |
82 |
82 |
rg_log("[$r] != [$e]!"); |
rg_log("[$r] != [$e]!"); |
83 |
83 |
exit(1); |
exit(1); |
84 |
84 |
} |
} |
|
... |
... |
$e = FALSE; |
87 |
87 |
$r = rg_cache_core_ashift("v"); |
$r = rg_cache_core_ashift("v"); |
88 |
88 |
$r = rg_cache_core_ashift("v"); |
$r = rg_cache_core_ashift("v"); |
89 |
89 |
if ($r !== $e) { |
if ($r !== $e) { |
90 |
|
print_r($rg_cache); |
|
|
90 |
|
rg_log_ml('rg_cache: ' . print_r($rg_cache, TRUE)); |
91 |
91 |
rg_log("r is not FALSE ($r)!"); |
rg_log("r is not FALSE ($r)!"); |
92 |
92 |
exit(1); |
exit(1); |
93 |
93 |
} |
} |
|
... |
... |
rg_cache_set('test::B', 'B', 0); |
101 |
101 |
rg_cache_sleep(); |
rg_cache_sleep(); |
102 |
102 |
$rg_cache = array(); |
$rg_cache = array(); |
103 |
103 |
rg_cache_get('test::B'); |
rg_cache_get('test::B'); |
104 |
|
$rg_cache_timeout = 3000; |
|
|
104 |
|
$rg_cache_timeout = 6000; |
105 |
105 |
$r = rg_cache_get('test::A'); |
$r = rg_cache_get('test::A'); |
106 |
106 |
if ($r !== 'A') { |
if ($r !== 'A') { |
107 |
|
rg_log('Small timeout test failed! r=' . print_r($r)); |
|
|
107 |
|
rg_log('Small timeout test failed! r=' . print_r($r, TRUE)); |
108 |
108 |
exit(1); |
exit(1); |
109 |
109 |
} |
} |
110 |
110 |
|
|
File tests/http_settings.php changed (mode: 100644) (index 3bc0787..8cc001c) |
... |
... |
$data = array( |
70 |
70 |
); |
); |
71 |
71 |
$headers = array("Cookie: sid=" . $good_sid); |
$headers = array("Cookie: sid=" . $good_sid); |
72 |
72 |
$r = do_req($test_url . "/op/settings/change_pass?t=post_change_pass_form", $data, $headers); |
$r = do_req($test_url . "/op/settings/change_pass?t=post_change_pass_form", $data, $headers); |
73 |
|
if (!strstr($r['body'], "Password has been updated with success")) { |
|
|
73 |
|
if (!strstr($r['body'], "Password has been successfully updated")) { |
74 |
74 |
rg_log_ml('r: ' . print_r($r, TRUE)); |
rg_log_ml('r: ' . print_r($r, TRUE)); |
75 |
75 |
rg_log("Cannot change pass!"); |
rg_log("Cannot change pass!"); |
76 |
76 |
exit(1); |
exit(1); |
|
... |
... |
$data = array( |
141 |
141 |
); |
); |
142 |
142 |
$headers = array("Cookie: sid=" . $good_sid); |
$headers = array("Cookie: sid=" . $good_sid); |
143 |
143 |
$r = do_req($test_url . "/op/settings/edit_info?t=post_edit_info_form", $data, $headers); |
$r = do_req($test_url . "/op/settings/edit_info?t=post_edit_info_form", $data, $headers); |
144 |
|
if (!strstr($r['body'], "Information has been updated with success")) { |
|
|
144 |
|
if (!strstr($r['body'], "Information has been successfully updated")) { |
145 |
145 |
rg_log_ml('r: ' . print_r($r, TRUE)); |
rg_log_ml('r: ' . print_r($r, TRUE)); |
146 |
|
rg_log("Cannot change back the pass to aaaa!"); |
|
|
146 |
|
rg_log("Cannot change account info!"); |
147 |
147 |
exit(1); |
exit(1); |
148 |
148 |
} |
} |
149 |
149 |
|
|
File tests/wh.php changed (mode: 100644) (index 0bbf288..9c902f0) |
... |
... |
$rg_event_socket = "/var/lib/rocketgit/sockets/event.sock"; |
22 |
22 |
|
|
23 |
23 |
$port1 = 64000 + (rand(0, 100000) + time()) % 1000; |
$port1 = 64000 + (rand(0, 100000) + time()) % 1000; |
24 |
24 |
$port2 = $port1 + 1; |
$port2 = $port1 + 1; |
25 |
|
rg_log('port1=' . $port1 . ' port2=' . $port2); |
|
|
25 |
|
$port3 = $port2 + 1; |
|
26 |
|
rg_log('port1=' . $port1 . ' port2=' . $port2 . ' port3=' . $port3); |
26 |
27 |
|
|
27 |
28 |
function clean() |
function clean() |
28 |
29 |
{ |
{ |
29 |
|
system('fuser -k wh-stunnel-1.log 1>/dev/null 2>&1'); |
|
30 |
|
system('fuser -k wh-stunnel-2.log 1>/dev/null 2>&1'); |
|
|
30 |
|
system('fuser -k -9 wh-stunnel-1.log 1>/dev/null 2>&1'); |
|
31 |
|
system('fuser -k -9 wh-stunnel-2.log 1>/dev/null 2>&1'); |
|
32 |
|
system('fuser -k -9 wh-stunnel-3.log 1>/dev/null 2>&1'); |
31 |
33 |
|
|
32 |
34 |
@unlink('wh-stunnel.conf-1.tmp'); |
@unlink('wh-stunnel.conf-1.tmp'); |
33 |
35 |
@unlink('wh-stunnel.conf-2.tmp'); |
@unlink('wh-stunnel.conf-2.tmp'); |
|
36 |
|
@unlink('wh-stunnel.conf-3.tmp'); |
34 |
37 |
} |
} |
35 |
38 |
register_shutdown_function('clean'); |
register_shutdown_function('clean'); |
36 |
39 |
|
|
|
... |
... |
if ($x === FALSE) { |
52 |
55 |
rg_log('Cannot load conf file'); |
rg_log('Cannot load conf file'); |
53 |
56 |
exit(1); |
exit(1); |
54 |
57 |
} |
} |
|
58 |
|
|
55 |
59 |
$y = str_replace('@@port@@', $port1, $x); |
$y = str_replace('@@port@@', $port1, $x); |
|
60 |
|
$y = str_replace('@@verify@@', '2', $y); |
|
61 |
|
$y = str_replace('@@id@@', '1', $y); |
56 |
62 |
file_put_contents('wh-stunnel.conf-1.tmp', $y); |
file_put_contents('wh-stunnel.conf-1.tmp', $y); |
|
63 |
|
|
57 |
64 |
$y = str_replace('@@port@@', $port2, $x); |
$y = str_replace('@@port@@', $port2, $x); |
|
65 |
|
$y = str_replace('@@verify@@', '2', $y); |
|
66 |
|
$y = str_replace('@@id@@', '2', $y); |
58 |
67 |
file_put_contents('wh-stunnel.conf-2.tmp', $y); |
file_put_contents('wh-stunnel.conf-2.tmp', $y); |
59 |
68 |
|
|
|
69 |
|
$y = str_replace('@@port@@', $port3, $x); |
|
70 |
|
$y = str_replace('@@verify@@', '0', $y); |
|
71 |
|
$y = str_replace('@@id@@', '3', $y); |
|
72 |
|
file_put_contents('wh-stunnel.conf-3.tmp', $y); |
|
73 |
|
|
60 |
74 |
|
|
61 |
75 |
rg_log(''); |
rg_log(''); |
62 |
76 |
rg_log('Starting stunnel1...'); |
rg_log('Starting stunnel1...'); |
|
... |
... |
if ($pid == 0) { //child |
86 |
100 |
rg_log('Started stunnel with pid ' . $pid); |
rg_log('Started stunnel with pid ' . $pid); |
87 |
101 |
|
|
88 |
102 |
|
|
|
103 |
|
rg_log(''); |
|
104 |
|
rg_log('Starting stunnel3...'); |
|
105 |
|
$pid = pcntl_fork(); |
|
106 |
|
if ($pid == -1) { |
|
107 |
|
rg_log('Cannot fork'); |
|
108 |
|
exit(1); |
|
109 |
|
} |
|
110 |
|
if ($pid == 0) { //child |
|
111 |
|
$r = exec('stunnel wh-stunnel.conf-3.tmp 2>/dev/null'); |
|
112 |
|
exit(0); |
|
113 |
|
} |
|
114 |
|
rg_log('Started stunnel with pid ' . $pid); |
|
115 |
|
|
|
116 |
|
|
89 |
117 |
rg_log(''); |
rg_log(''); |
90 |
118 |
rg_log("Creating a user..."); |
rg_log("Creating a user..."); |
91 |
119 |
rg_test_create_user($db, $rg_ui); |
rg_test_create_user($db, $rg_ui); |
92 |
120 |
$key1 = 'DEBUG::' . $rg_ui['uid'] . '::webhooks::' . $port1; |
$key1 = 'DEBUG::' . $rg_ui['uid'] . '::webhooks::' . $port1; |
93 |
121 |
$key2 = 'DEBUG::' . $rg_ui['uid'] . '::webhooks::' . $port2; |
$key2 = 'DEBUG::' . $rg_ui['uid'] . '::webhooks::' . $port2; |
|
122 |
|
$key3 = 'DEBUG::' . $rg_ui['uid'] . '::webhooks::' . $port3; |
94 |
123 |
|
|
95 |
124 |
|
|
96 |
125 |
rg_log(''); |
rg_log(''); |
|
... |
... |
$extra = array( |
137 |
166 |
rg_test_wh_add_edit($db, $rg_ui, $good_sid, $extra); |
rg_test_wh_add_edit($db, $rg_ui, $good_sid, $extra); |
138 |
167 |
|
|
139 |
168 |
|
|
|
169 |
|
rg_log(''); |
|
170 |
|
rg_log('Registering webhook3...'); |
|
171 |
|
$extra = array( |
|
172 |
|
'wh::url' => 'https://localhost:' . $port3 . '/wh.html', |
|
173 |
|
'wh::type' => 1, |
|
174 |
|
'wh::events[C]' => 'on', |
|
175 |
|
'wh::events[P]' => 'on', |
|
176 |
|
'wh::events[B]' => 'on', |
|
177 |
|
'wh::description' => 'description1 <xss>', |
|
178 |
|
'wh::key' => 'key2 <xss>', |
|
179 |
|
'wh::opaque' => $port3, |
|
180 |
|
'wh::client_cert' => '', |
|
181 |
|
'wh::client_ca_cert' => file_get_contents('ca/wh/certs/cacert.pem') |
|
182 |
|
); |
|
183 |
|
rg_test_wh_add_edit($db, $rg_ui, $good_sid, $extra); |
|
184 |
|
|
|
185 |
|
|
140 |
186 |
rg_log('Finding out the ids...'); |
rg_log('Finding out the ids...'); |
141 |
187 |
for ($i = 0; $i < 10; $i++) { |
for ($i = 0; $i < 10; $i++) { |
142 |
188 |
$r = rg_cache_get('user::' . $rg_ui['uid'] . '::wh'); |
$r = rg_cache_get('user::' . $rg_ui['uid'] . '::wh'); |
|
... |
... |
rg_log_ml('r=' . print_r($r, TRUE)); |
152 |
198 |
$t = array_keys($r); |
$t = array_keys($r); |
153 |
199 |
$wh_id1 = $t[0]; |
$wh_id1 = $t[0]; |
154 |
200 |
$wh_id2 = $t[1]; |
$wh_id2 = $t[1]; |
|
201 |
|
$wh_id3 = $t[2]; |
155 |
202 |
rg_log('wh_id1=' . $wh_id1); |
rg_log('wh_id1=' . $wh_id1); |
156 |
203 |
rg_log('wh_id2=' . $wh_id2); |
rg_log('wh_id2=' . $wh_id2); |
|
204 |
|
rg_log('wh_id3=' . $wh_id3); |
157 |
205 |
|
|
158 |
206 |
|
|
159 |
207 |
rg_log(''); |
rg_log(''); |
|
... |
... |
if (strcmp($r, "OK") != 0) { |
198 |
246 |
} |
} |
199 |
247 |
|
|
200 |
248 |
|
|
|
249 |
|
rg_log(''); |
|
250 |
|
rg_log('Testing if the curl posted with success (wh3)'); |
|
251 |
|
for ($i = 0; $i < 10; $i++) { |
|
252 |
|
$r = rg_cache_get($key3 . '::' . $wh_id3); |
|
253 |
|
rg_log_ml('cache: ' . print_r($r, TRUE)); |
|
254 |
|
if ($r !== FALSE) |
|
255 |
|
break; |
|
256 |
|
sleep(1); |
|
257 |
|
} |
|
258 |
|
if (strcmp($r, "OK") != 0) { |
|
259 |
|
rg_log('Seems wh3 did not returned success' |
|
260 |
|
. ' (r=' . $r . ')!'); |
|
261 |
|
exit(1); |
|
262 |
|
} |
|
263 |
|
|
|
264 |
|
|
201 |
265 |
rg_log(''); |
rg_log(''); |
202 |
266 |
rg_log('Testing the edit of webhook1...'); |
rg_log('Testing the edit of webhook1...'); |
203 |
267 |
$extra = array( |
$extra = array( |