Rev 6 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 6 | Rev 7 | ||
---|---|---|---|
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * ViaThinkSoft CryptoChat |
4 | * ViaThinkSoft CryptoChat |
5 | * Copyright 2014 - 2018 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2014 - 2018 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 | // ----------------------------------------------------------------------------------------------- |
20 | // ----------------------------------------------------------------------------------------------- |
21 | 21 | ||
22 | // feature: user comes online, user comes offline, user is inactive, user is active, user is typing, get user list |
22 | // feature: user comes online, user comes offline, user is inactive, user is active, user is typing, get user list |
23 | // todo: hide password length to avoid screensaves revealing confidential information |
23 | // todo: hide password length to avoid screensaves revealing confidential information |
24 | // feature: split screen (frames), with united document.title ! (sum unread posts of each chat) |
24 | // feature: split screen (frames), with united document.title ! (sum unread posts of each chat) |
25 | // ng: smileys |
25 | // ng: smileys |
26 | // ng: roomname, username encrypt |
26 | // ng: roomname, username encrypt |
27 | // ng: sha3 |
27 | // ng: sha3 |
28 | // ng: maybe it would be usefil if you could see by different colors if something was encrypted using a different password |
28 | // ng: maybe it would be usefil if you could see by different colors if something was encrypted using a different password |
29 | // bug: some browsers cannot mark the last line, otherwise everything will be marked |
29 | // bug: some browsers cannot mark the last line, otherwise everything will be marked |
30 | // bug: offline detection does not work... |
30 | // bug: offline detection does not work... |
31 | // todo: "reset chat" (counter=0, document='') while pwd change? |
31 | // todo: "reset chat" (counter=0, document='') while pwd change? |
32 | // feature: verhindern dass man etwas postet ohne ein passwort eingegeben zu haben? -> config |
32 | // feature: verhindern dass man etwas postet ohne ein passwort eingegeben zu haben? -> config |
33 | // feature: posts durchnumerieren? |
33 | // feature: posts durchnumerieren? |
34 | // bug openpandora: password field on change geht net (cannot repro all the time?) ist !== null ok? (fixed now?) |
34 | // bug openpandora: password field on change geht net (cannot repro all the time?) ist !== null ok? (fixed now?) |
35 | // bug cheery laptop: froze, didn't check new messages (mehrfach. repro? Aurora 24.2.0 Gentoo). eigene kommen nicht an. nach einer weile geht es plötzlich wieder? evtl ist vts request 1 mal fehlgeschlagen. (fixed now with fail callbacks?) |
35 | // bug cheery laptop: froze, didn't check new messages (mehrfach. repro? Aurora 24.2.0 Gentoo). eigene kommen nicht an. nach einer weile geht es plötzlich wieder? evtl ist vts request 1 mal fehlgeschlagen. (fixed now with fail callbacks?) |
36 | // feature: shift+enter = new line without sending? |
36 | // feature: shift+enter = new line without sending? |
37 | // rp feature: ((->[i] , rp notizen + cors |
37 | // rp feature: ((->[i] , rp notizen + cors |
38 | // feature: send files? |
38 | // feature: send files? |
39 | // feature: dereferer |
39 | // feature: dereferer |
40 | // feature: configuration option to show a drop down box with all chat rooms |
40 | // feature: configuration option to show a drop down box with all chat rooms |
41 | // todo: always renew cache |
41 | // todo: always renew cache |
42 | // todo: warn if config is not correct, e.g. admin password missing |
42 | // todo: warn if config is not correct, e.g. admin password missing |
43 | // feature: prevent making screenshots? keep clipboard empty? |
43 | // feature: prevent making screenshots? keep clipboard empty? |
44 | // feature: detect NoScript |
44 | // feature: detect NoScript |
45 | // todo: onChatHide() does not get called when tab is active (and visible), but browser window hasn't the focus |
45 | // todo: onChatHide() does not get called when tab is active (and visible), but browser window hasn't the focus |
46 | // feature: loginmessage. show version of user. user soll bei jedem ajax-verkehr die version mitsenden, damit der server ihn ablehnen kann wenn die version zu alt ist!. ggf ein re-load forcing (self.reload in same room/user/pwd)? |
46 | // feature: loginmessage. show version of user. user soll bei jedem ajax-verkehr die version mitsenden, damit der server ihn ablehnen kann wenn die version zu alt ist!. ggf ein re-load forcing (self.reload in same room/user/pwd)? |
47 | // idea: undo decryption if pwd field changed? delete pwd field and save actual decryption key in VAR array. so no screenloggers can see |
47 | // idea: undo decryption if pwd field changed? delete pwd field and save actual decryption key in VAR array. so no screenloggers can see |
48 | // feature?: possibility to disable hash? just try to do AES. |
48 | // feature?: possibility to disable hash? just try to do AES. |
49 | // feature?: possibility to disable user stats or to prune them. |
49 | // feature?: possibility to disable user stats or to prune them. |
50 | // feature?: possibility to prune chat |
50 | // feature?: possibility to prune chat |
51 | // feature?: send pwd hashed with refresh() so you can see which key(s) the active users use? |
51 | // feature?: send pwd hashed with refresh() so you can see which key(s) the active users use? |
52 | // feature?: at "(Logging off)"-Message sending: remove DIRECTLY from stat list |
52 | // feature?: at "(Logging off)"-Message sending: remove DIRECTLY from stat list |
53 | // feature: when configuration symbols are not defined, redefine them with default settings |
53 | // feature: when configuration symbols are not defined, redefine them with default settings |
54 | // feature: online list - instead of showing 2 same names, just write "x2" |
54 | // feature: online list - instead of showing 2 same names, just write "x2" |
55 | 55 | ||
56 | // ----------------------------------------------------------------------------------------------- |
56 | // ----------------------------------------------------------------------------------------------- |
57 | 57 | ||
58 | if (!file_exists(__DIR__ . '/config/config.inc.php')) { |
58 | if (!file_exists(__DIR__ . '/config/config.inc.php')) { |
59 | die('ERROR: File <b>config/config.inc.php</b> does not exist. Please create it using <b>config/config.original.inc.php</b>'); |
59 | die('ERROR: File <b>config/config.inc.php</b> does not exist. Please create it using <b>config/config.original.inc.php</b>'); |
60 | } |
60 | } |
61 | require __DIR__ . '/config/config.inc.php'; |
61 | require __DIR__ . '/config/config.inc.php'; |
62 | define('PRODUCT_NAME', 'CryptoChat'); |
62 | define('PRODUCT_NAME', 'CryptoChat'); |
63 | define('MCC_VER', trim(file_get_contents(__DIR__ . '/VERSION'))); |
63 | define('MCC_VER', trim(file_get_contents(__DIR__ . '/VERSION'))); |
64 | 64 | ||
- | 65 | if (!function_exists('get_magic_quotes_gpc')) { |
|
- | 66 | // Required by sajax.php |
|
- | 67 | function get_magic_quotes_gpc() { |
|
- | 68 | return false; |
|
- | 69 | } |
|
- | 70 | } |
|
- | 71 | ||
65 | require __DIR__ . '/' . DEP_DIR_SAJAX . '/php/sajax.php'; |
72 | require __DIR__ . '/' . DEP_DIR_SAJAX . '/php/sajax.php'; |
66 | require __DIR__ . '/' . DEP_DIR_JSONWRAPPER . '/jsonwrapper.php'; // TODO: make optional. only required if running with PHP 5.1 |
73 | require __DIR__ . '/' . DEP_DIR_JSONWRAPPER . '/jsonwrapper.php'; // TODO: make optional. only required if running with PHP 5.1 |
67 | 74 | ||
68 | header('Content-type: text/html; charset=utf-8'); |
75 | header('Content-type: text/html; charset=utf-8'); |
69 | 76 | ||
70 | if ((REQUIRE_TLS) && (!is_secure())) { |
77 | if ((REQUIRE_TLS) && (!is_secure())) { |
71 | // TODO: redirect |
78 | // TODO: redirect |
72 | echo '<h1>Please connect via HTTPS or change <b>REQUIRE_TLS</b> to false.</h1>'; |
79 | echo '<h1>Please connect via HTTPS or change <b>REQUIRE_TLS</b> to false.</h1>'; |
73 | die(); |
80 | die(); |
74 | } |
81 | } |
75 | 82 | ||
76 | define('ACTION_LIST_ROOMS', 1); |
83 | define('ACTION_LIST_ROOMS', 1); |
77 | define('ACTION_CREATE_ROOM', 2); |
84 | define('ACTION_CREATE_ROOM', 2); |
78 | 85 | ||
79 | $ses_room = (isset($_POST['ses_room'])) ? trim($_POST['ses_room']) : ''; |
86 | $ses_room = (isset($_POST['ses_room'])) ? trim($_POST['ses_room']) : ''; |
80 | $ses_user = (isset($_POST['ses_user'])) ? trim($_POST['ses_user']) : ''; |
87 | $ses_user = (isset($_POST['ses_user'])) ? trim($_POST['ses_user']) : ''; |
81 | $admin_pwd = (isset($_POST['admin_pwd'])) ? trim($_POST['admin_pwd']) : ''; |
88 | $admin_pwd = (isset($_POST['admin_pwd'])) ? trim($_POST['admin_pwd']) : ''; |
82 | 89 | ||
83 | $ses_room = trim(strtolower($ses_room)); |
90 | $ses_room = trim(strtolower($ses_room)); |
84 | $ses_user = trim($ses_user); |
91 | $ses_user = trim($ses_user); |
85 | // $admin_pwd will be trimmed in adminAuth() |
92 | // $admin_pwd will be trimmed in adminAuth() |
86 | 93 | ||
87 | $chat_exists = (!CHAT_MUST_EXIST) || (adminAuth($admin_pwd, ACTION_CREATE_ROOM)) || chat_exists($ses_room); |
94 | $chat_exists = (!CHAT_MUST_EXIST) || (adminAuth($admin_pwd, ACTION_CREATE_ROOM)) || chat_exists($ses_room); |
88 | 95 | ||
89 | $is_logged_in = ($ses_room != '') && ($ses_user != '') && $chat_exists; |
96 | $is_logged_in = ($ses_room != '') && ($ses_user != '') && $chat_exists; |
90 | $show_list = isset($_REQUEST['list_chatrooms']) && (!LIST_CHATS_REQUIRE_PWD || adminAuth($_POST['admin_pwd'], ACTION_LIST_ROOMS)); |
97 | $show_list = isset($_REQUEST['list_chatrooms']) && (!LIST_CHATS_REQUIRE_PWD || adminAuth($_POST['admin_pwd'], ACTION_LIST_ROOMS)); |
91 | 98 | ||
92 | if ($is_logged_in) { |
99 | if ($is_logged_in) { |
93 | $chatroom_file = chatroom_file($ses_room); |
100 | $chatroom_file = chatroom_file($ses_room); |
94 | touch($chatroom_file); |
101 | touch($chatroom_file); |
95 | } |
102 | } |
96 | 103 | ||
97 | function adminAuth($password, $action) { |
104 | function adminAuth($password, $action) { |
98 | if ($action == ACTION_LIST_ROOMS) { |
105 | if ($action == ACTION_LIST_ROOMS) { |
99 | // if (trim(PWD_LIST_ROOMS) == '') return false; |
106 | // if (trim(PWD_LIST_ROOMS) == '') return false; |
100 | return (trim($password) == trim(PWD_LIST_ROOMS)); |
107 | return (trim($password) == trim(PWD_LIST_ROOMS)); |
101 | } else if ($action == ACTION_CREATE_ROOM) { |
108 | } else if ($action == ACTION_CREATE_ROOM) { |
102 | // if (trim(PWD_CREATE_ROOM) == '') return false; |
109 | // if (trim(PWD_CREATE_ROOM) == '') return false; |
103 | return (trim($password) == trim(PWD_CREATE_ROOM)); |
110 | return (trim($password) == trim(PWD_CREATE_ROOM)); |
104 | } else { |
111 | } else { |
105 | throw new Exception('Unknown action'); |
112 | throw new Exception('Unknown action'); |
106 | } |
113 | } |
107 | } |
114 | } |
108 | 115 | ||
109 | function is_secure() { |
116 | function is_secure() { |
110 | // https://stackoverflow.com/questions/1175096/how-to-find-out-if-you-are-using-https-without-serverhttps |
117 | // https://stackoverflow.com/questions/1175096/how-to-find-out-if-you-are-using-https-without-serverhttps |
111 | if(isset($_SERVER['HTTPS'])) { |
118 | if(isset($_SERVER['HTTPS'])) { |
112 | if ($_SERVER['HTTPS'] == 'on') { |
119 | if ($_SERVER['HTTPS'] == 'on') { |
113 | return true; |
120 | return true; |
114 | } |
121 | } |
115 | } |
122 | } |
116 | return false; |
123 | return false; |
117 | } |
124 | } |
118 | 125 | ||
119 | function chat_exists($room) { |
126 | function chat_exists($room) { |
120 | return file_exists(chatroom_file($room)); |
127 | return file_exists(chatroom_file($room)); |
121 | } |
128 | } |
122 | 129 | ||
123 | function chatroom_file($room) { |
130 | function chatroom_file($room) { |
124 | return __DIR__ . '/chats/'.escape_filename($room).'.html'; |
131 | return __DIR__ . '/chats/'.escape_filename($room).'.html'; |
125 | } |
132 | } |
126 | 133 | ||
127 | function chatroom_userstat_file($room, $unique_id) { |
134 | function chatroom_userstat_file($room, $unique_id) { |
128 | return __DIR__ . '/chats/'.escape_filename($room).'_'.escape_filename($unique_id).'.session'; |
135 | return __DIR__ . '/chats/'.escape_filename($room).'_'.escape_filename($unique_id).'.session'; |
129 | } |
136 | } |
130 | 137 | ||
131 | function chatroom_userstat_files($room) { |
138 | function chatroom_userstat_files($room) { |
132 | $ses_ids = array(); |
139 | $ses_ids = array(); |
133 | 140 | ||
134 | $ary = glob(__DIR__ . '/chats/'.escape_filename($room).'_*.session'); |
141 | $ary = glob(__DIR__ . '/chats/'.escape_filename($room).'_*.session'); |
135 | foreach ($ary as &$a) { |
142 | foreach ($ary as &$a) { |
136 | if (!preg_match('@'.preg_quote(__DIR__ . '/chats/'.escape_filename($room).'_').'(.+)\.session@ismU', $a, $m)) { |
143 | if (!preg_match('@'.preg_quote(__DIR__ . '/chats/'.escape_filename($room).'_').'(.+)\.session@ismU', $a, $m)) { |
137 | die('Internal error at '.__LINE__); |
144 | die('Internal error at '.__LINE__); |
138 | } |
145 | } |
139 | $ses_ids[] = $m[1]; |
146 | $ses_ids[] = $m[1]; |
140 | } |
147 | } |
141 | 148 | ||
142 | return $ses_ids; |
149 | return $ses_ids; |
143 | } |
150 | } |
144 | 151 | ||
145 | function write_user_stat($room, $unique_id, $username, $ip) { |
152 | function write_user_stat($room, $unique_id, $username, $ip) { |
146 | $file = chatroom_userstat_file($room, $unique_id); |
153 | $file = chatroom_userstat_file($room, $unique_id); |
147 | 154 | ||
148 | $cont = ''; |
155 | $cont = ''; |
149 | # $cont .= "ROOM:$room\n"; |
156 | # $cont .= "ROOM:$room\n"; |
150 | $cont .= "USERNAME:$username\n"; |
157 | $cont .= "USERNAME:$username\n"; |
151 | # $cont .= "UNIQUEID:$unique_id\n"; |
158 | # $cont .= "UNIQUEID:$unique_id\n"; |
152 | $cont .= "IP:$ip\n"; |
159 | $cont .= "IP:$ip\n"; |
153 | # $now=time(); $cont .= "ACTIVE:$now\n"; |
160 | # $now=time(); $cont .= "ACTIVE:$now\n"; |
154 | # $key=...; $cont .= "KEY:$key\n"; |
161 | # $key=...; $cont .= "KEY:$key\n"; |
155 | 162 | ||
156 | file_put_contents($file, $cont); |
163 | file_put_contents($file, $cont); |
157 | } |
164 | } |
158 | 165 | ||
159 | function file_age($file) { |
166 | function file_age($file) { |
160 | return time()-filemtime($file); |
167 | return time()-filemtime($file); |
161 | } |
168 | } |
162 | 169 | ||
163 | function get_active_users($room, $max_inactivity=USERSTAT_INACTIVE_SECS) { |
170 | function get_active_users($room, $max_inactivity=USERSTAT_INACTIVE_SECS) { |
164 | $ses_ids = array(); |
171 | $ses_ids = array(); |
165 | 172 | ||
166 | $all_ses_ids = chatroom_userstat_files($room); |
173 | $all_ses_ids = chatroom_userstat_files($room); |
167 | foreach ($all_ses_ids as &$ses_id) { |
174 | foreach ($all_ses_ids as &$ses_id) { |
168 | $file = chatroom_userstat_file($room, $ses_id); |
175 | $file = chatroom_userstat_file($room, $ses_id); |
169 | if (file_age($file) <= $max_inactivity) { |
176 | if (file_age($file) <= $max_inactivity) { |
170 | $ses_ids[] = $ses_id; |
177 | $ses_ids[] = $ses_id; |
171 | } else { |
178 | } else { |
172 | if (DELETE_OLD_USERSTATS) { |
179 | if (DELETE_OLD_USERSTATS) { |
173 | $file = chatroom_userstat_file($room, $ses_id); |
180 | $file = chatroom_userstat_file($room, $ses_id); |
174 | unlink($file); |
181 | unlink($file); |
175 | } |
182 | } |
176 | } |
183 | } |
177 | } |
184 | } |
178 | 185 | ||
179 | return $ses_ids; |
186 | return $ses_ids; |
180 | } |
187 | } |
181 | 188 | ||
182 | function read_user_stat($room, $unique_id) { |
189 | function read_user_stat($room, $unique_id) { |
183 | $res = array(); |
190 | $res = array(); |
184 | 191 | ||
185 | $file = chatroom_userstat_file($room, $unique_id); |
192 | $file = chatroom_userstat_file($room, $unique_id); |
186 | 193 | ||
187 | if (!file_exists($file)) return false; |
194 | if (!file_exists($file)) return false; |
188 | 195 | ||
189 | $cont = file($file); |
196 | $cont = file($file); |
190 | foreach ($cont as &$c) { |
197 | foreach ($cont as &$c) { |
191 | $c = trim($c); |
198 | $c = trim($c); |
192 | if ($c == '') continue; |
199 | if ($c == '') continue; |
193 | $nameval = explode(':', $c, 2); |
200 | $nameval = explode(':', $c, 2); |
194 | if (count($nameval) < 2) continue; |
201 | if (count($nameval) < 2) continue; |
195 | 202 | ||
196 | $name = $nameval[0]; |
203 | $name = $nameval[0]; |
197 | $val = $nameval[1]; |
204 | $val = $nameval[1]; |
198 | $res[$name] = $val; |
205 | $res[$name] = $val; |
199 | } |
206 | } |
200 | $res['ACTIVE'] = filemtime($file); |
207 | $res['ACTIVE'] = filemtime($file); |
201 | $res['ROOM'] = $room; |
208 | $res['ROOM'] = $room; |
202 | $res['UNIQUEID'] = $unique_id; |
209 | $res['UNIQUEID'] = $unique_id; |
203 | 210 | ||
204 | return $res; |
211 | return $res; |
205 | } |
212 | } |
206 | 213 | ||
207 | function list_rooms() { |
214 | function list_rooms() { |
208 | $ary = glob(__DIR__ . '/chats/*.html'); |
215 | $ary = glob(__DIR__ . '/chats/*.html'); |
209 | foreach ($ary as &$a) { |
216 | foreach ($ary as &$a) { |
210 | $a = basename($a, '.html'); |
217 | $a = basename($a, '.html'); |
211 | } |
218 | } |
212 | return $ary; |
219 | return $ary; |
213 | } |
220 | } |
214 | 221 | ||
215 | function count_messages($room) { |
222 | function count_messages($room) { |
216 | $room_file = chatroom_file($room); |
223 | $room_file = chatroom_file($room); |
217 | return count(file($room_file)); |
224 | return count(file($room_file)); |
218 | } |
225 | } |
219 | 226 | ||
220 | function count_users($room) { |
227 | function count_users($room) { |
221 | $room_file = chatroom_file($room); |
228 | $room_file = chatroom_file($room); |
222 | $cont = file_get_contents($room_file); |
229 | $cont = file_get_contents($room_file); |
223 | preg_match_all('@\(user:(.*)\)@ismU', $cont, $m); |
230 | preg_match_all('@\(user:(.*)\)@ismU', $cont, $m); |
224 | $users_lookup = array(); |
231 | $users_lookup = array(); |
225 | foreach ($m[1] as &$us) { |
232 | foreach ($m[1] as &$us) { |
226 | $users_lookup[$us] = true; |
233 | $users_lookup[$us] = true; |
227 | } |
234 | } |
228 | return count($users_lookup); |
235 | return count($users_lookup); |
229 | } |
236 | } |
230 | 237 | ||
231 | function escape_filename($str) { |
238 | function escape_filename($str) { |
232 | $str = str_replace('/', '_', $str); |
239 | $str = str_replace('/', '_', $str); |
233 | return $str; |
240 | return $str; |
234 | } |
241 | } |
235 | 242 | ||
236 | /* exported via AJAX */ |
243 | /* exported via AJAX */ |
237 | function add_line($id, $room, $user, $msg) { |
244 | function add_line($id, $room, $user, $msg) { |
238 | if (!chat_exists($room)) return; |
245 | if (!chat_exists($room)) return; |
239 | if ($user == '') return; |
246 | if ($user == '') return; |
240 | 247 | ||
241 | $f = fopen(chatroom_file($room), 'a'); |
248 | $f = fopen(chatroom_file($room), 'a'); |
242 | $dt = date('Y-m-d H:i:s'); |
249 | $dt = date('Y-m-d H:i:s'); |
243 | // $msg = stripslashes($msg); |
250 | // $msg = stripslashes($msg); |
244 | $remote = $_SERVER['REMOTE_ADDR']; |
251 | $remote = $_SERVER['REMOTE_ADDR']; |
245 | 252 | ||
246 | // Escape stuff |
253 | // Escape stuff |
247 | $user = htmlentities($user, ENT_QUOTES, 'UTF-8'); |
254 | $user = htmlentities($user, ENT_QUOTES, 'UTF-8'); |
248 | $msg = htmlentities($msg, ENT_QUOTES, 'UTF-8'); |
255 | $msg = htmlentities($msg, ENT_QUOTES, 'UTF-8'); |
249 | 256 | ||
250 | $msgs = explode("\n", $msg); |
257 | $msgs = explode("\n", $msg); |
251 | foreach ($msgs as &$msg) { |
258 | foreach ($msgs as &$msg) { |
252 | fwrite($f, "(date: $dt)(user: $user)$msg<br>\n"); |
259 | fwrite($f, "(date: $dt)(user: $user)$msg<br>\n"); |
253 | } |
260 | } |
254 | 261 | ||
255 | fclose($f); |
262 | fclose($f); |
256 | 263 | ||
257 | return array($id); |
264 | return array($id); |
258 | } |
265 | } |
259 | 266 | ||
260 | /* exported via AJAX */ |
267 | /* exported via AJAX */ |
261 | function refresh($room, $fromline, $username, $unique_id) { |
268 | function refresh($room, $fromline, $username, $unique_id) { |
262 | if (!chat_exists($room)) return; |
269 | if (!chat_exists($room)) return; |
263 | if ($fromline < 0) return; |
270 | if ($fromline < 0) return; |
264 | 271 | ||
265 | $unique_id = trim($unique_id); |
272 | $unique_id = trim($unique_id); |
266 | if ($unique_id == '') { |
273 | if ($unique_id == '') { |
267 | # TODO: show error message |
274 | # TODO: show error message |
268 | return false; |
275 | return false; |
269 | } |
276 | } |
270 | write_user_stat($room, $unique_id, $username, $_SERVER['REMOTE_ADDR']); |
277 | write_user_stat($room, $unique_id, $username, $_SERVER['REMOTE_ADDR']); |
271 | 278 | ||
272 | $res = ''; |
279 | $res = ''; |
273 | $lines = file(chatroom_file($room)); |
280 | $lines = file(chatroom_file($room)); |
274 | for ($i=$fromline; $i<count($lines); $i++) { |
281 | for ($i=$fromline; $i<count($lines); $i++) { |
275 | $res .= $lines[$i] . "\n"; |
282 | $res .= $lines[$i] . "\n"; |
276 | } |
283 | } |
277 | 284 | ||
278 | $userstats = array(); |
285 | $userstats = array(); |
279 | $ses_ids = get_active_users($room); |
286 | $ses_ids = get_active_users($room); |
280 | foreach ($ses_ids as &$ses_id) { |
287 | foreach ($ses_ids as &$ses_id) { |
281 | $userstats[] = read_user_stat($room, $ses_id); |
288 | $userstats[] = read_user_stat($room, $ses_id); |
282 | } |
289 | } |
283 | 290 | ||
284 | return array(count($lines), $res, $userstats); |
291 | return array(count($lines), $res, $userstats); |
285 | } |
292 | } |
286 | 293 | ||
287 | // $sajax_debug_mode = true; |
294 | // $sajax_debug_mode = true; |
288 | $sajax_failure_redirect = 'http://web.archive.org/web/20090915191608/http://sajax.info/sajaxfail.html'; |
295 | $sajax_failure_redirect = 'http://web.archive.org/web/20090915191608/http://sajax.info/sajaxfail.html'; |
289 | sajax_export( /** @phpstan-ignore-line */ // PHPstan thinks that sajax_export() only accepts 0 parameters?! |
296 | sajax_export( /** @phpstan-ignore-line */ // PHPstan thinks that sajax_export() only accepts 0 parameters?! |
290 | array('name' => 'add_line', 'method' => 'POST'), |
297 | array('name' => 'add_line', 'method' => 'POST'), |
291 | array('name' => 'refresh', 'method' => 'GET') // TODO: post? |
298 | array('name' => 'refresh', 'method' => 'GET') // TODO: post? |
292 | ); |
299 | ); |
293 | sajax_handle_client_request(); |
300 | sajax_handle_client_request(); |
294 | 301 | ||
295 | ?> |
302 | ?> |
296 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
303 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
297 | "http://www.w3.org/TR/html4/loose.dtd"> |
304 | "http://www.w3.org/TR/html4/loose.dtd"> |
298 | <html> |
305 | <html> |
299 | <head> |
306 | <head> |
300 | <META HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8"> |
307 | <META HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8"> |
301 | <title><?php echo htmlentities(PRODUCT_NAME); ?></title> |
308 | <title><?php echo htmlentities(PRODUCT_NAME); ?></title> |
302 | <style> |
309 | <style> |
303 | body { |
310 | body { |
304 | background-color:#000011; |
311 | background-color:#000011; |
305 | color:white; |
312 | color:white; |
306 | font-family:Arial; |
313 | font-family:Arial; |
307 | font-size:13px; |
314 | font-size:13px; |
308 | } |
315 | } |
309 | body.chatroom { |
316 | body.chatroom { |
310 | margin: 0; |
317 | margin: 0; |
311 | background-color:#000011; |
318 | background-color:#000011; |
312 | color:white; |
319 | color:white; |
313 | font-family:Arial; |
320 | font-family:Arial; |
314 | font-size:13px; |
321 | font-size:13px; |
315 | } |
322 | } |
316 | thead td { |
323 | thead td { |
317 | font-weight:bold; |
324 | font-weight:bold; |
318 | background-color:#000044; |
325 | background-color:#000044; |
319 | } |
326 | } |
320 | .encrypted { |
327 | .encrypted { |
321 | color:orange; |
328 | color:orange; |
322 | } |
329 | } |
323 | .error { |
330 | .error { |
324 | color:#dd0000; |
331 | color:#dd0000; |
325 | } |
332 | } |
326 | input, textarea { |
333 | input, textarea { |
327 | background-color:#000011; |
334 | background-color:#000011; |
328 | color:white; |
335 | color:white; |
329 | } |
336 | } |
330 | #postarea textarea { |
337 | #postarea textarea { |
331 | width:80%; |
338 | width:80%; |
332 | background-color:#000011; |
339 | background-color:#000011; |
333 | color:white; |
340 | color:white; |
334 | } |
341 | } |
335 | #header { |
342 | #header { |
336 | padding:5px; |
343 | padding:5px; |
337 | position:fixed; |
344 | position:fixed; |
338 | background-color:#000044; |
345 | background-color:#000044; |
339 | color:white; |
346 | color:white; |
340 | width:100%; |
347 | width:100%; |
341 | height: 50px; |
348 | height: 50px; |
342 | } |
349 | } |
343 | #status { |
350 | #status { |
344 | position:fixed; |
351 | position:fixed; |
345 | right:5px; |
352 | right:5px; |
346 | top:5px; |
353 | top:5px; |
347 | } |
354 | } |
348 | #chatmessages { |
355 | #chatmessages { |
349 | padding-bottom:115px; |
356 | padding-bottom:115px; |
350 | padding-top: 65px; |
357 | padding-top: 65px; |
351 | padding-left: 5px; |
358 | padding-left: 5px; |
352 | padding-right: 5px; |
359 | padding-right: 5px; |
353 | } |
360 | } |
354 | #activeusers { |
361 | #activeusers { |
355 | position:fixed; |
362 | position:fixed; |
356 | top:65px; |
363 | top:65px; |
357 | /*bottom:115px;*/ |
364 | /*bottom:115px;*/ |
358 | right:5px; |
365 | right:5px; |
359 | /*height:100px;*/ |
366 | /*height:100px;*/ |
360 | width:150px; |
367 | width:150px; |
361 | background-color:#222222; |
368 | background-color:#222222; |
362 | padding:10px; |
369 | padding:10px; |
363 | } |
370 | } |
364 | #postarea { |
371 | #postarea { |
365 | padding:5px; |
372 | padding:5px; |
366 | position: fixed; |
373 | position: fixed; |
367 | bottom:0; |
374 | bottom:0; |
368 | height: 100px; |
375 | height: 100px; |
369 | width: 100%; |
376 | width: 100%; |
370 | background-color:#000044; |
377 | background-color:#000044; |
371 | color:white; |
378 | color:white; |
372 | } |
379 | } |
373 | .button { |
380 | .button { |
374 | /* http://www.drweb.de/magazin/browser-cursor-hand/ */ |
381 | /* http://www.drweb.de/magazin/browser-cursor-hand/ */ |
375 | cursor: hand; cursor: pointer; |
382 | cursor: hand; cursor: pointer; |
376 | font-family: Arial; |
383 | font-family: Arial; |
377 | color:white; |
384 | color:white; |
378 | background-color: #000022; |
385 | background-color: #000022; |
379 | border-color:#000000; |
386 | border-color:#000000; |
380 | border-style: inset; |
387 | border-style: inset; |
381 | } |
388 | } |
382 | .button:hover { |
389 | .button:hover { |
383 | color:yellow; |
390 | color:yellow; |
384 | } |
391 | } |
385 | label { |
392 | label { |
386 | /* http://www.drweb.de/magazin/browser-cursor-hand/ */ |
393 | /* http://www.drweb.de/magazin/browser-cursor-hand/ */ |
387 | cursor: hand; cursor: pointer; |
394 | cursor: hand; cursor: pointer; |
388 | } |
395 | } |
389 | label:hover { |
396 | label:hover { |
390 | color:yellow; |
397 | color:yellow; |
391 | } |
398 | } |
392 | a:link { |
399 | a:link { |
393 | color:#ffdd00; |
400 | color:#ffdd00; |
394 | } |
401 | } |
395 | a:visited { |
402 | a:visited { |
396 | color:darkred; |
403 | color:darkred; |
397 | } |
404 | } |
398 | a:active, a:hover { |
405 | a:active, a:hover { |
399 | color:white; |
406 | color:white; |
400 | } |
407 | } |
401 | .logout a { |
408 | .logout a { |
402 | color:white; |
409 | color:white; |
403 | } |
410 | } |
404 | .logout a:hover { |
411 | .logout a:hover { |
405 | color:orange; |
412 | color:orange; |
406 | } |
413 | } |
407 | .logout { |
414 | .logout { |
408 | margin-left: 20px; |
415 | margin-left: 20px; |
409 | } |
416 | } |
410 | </style> |
417 | </style> |
411 | 418 | ||
412 | <?php |
419 | <?php |
413 | if ($is_logged_in) { |
420 | if ($is_logged_in) { |
414 | ?> |
421 | ?> |
415 | 422 | ||
416 | <script type="text/javascript" src="<?php echo DEP_DIR_CRYPTOJS; ?>/rollups/sha256.js"></script> |
423 | <script type="text/javascript" src="<?php echo DEP_DIR_CRYPTOJS; ?>/rollups/sha256.js"></script> |
417 | <script type="text/javascript" src="<?php echo DEP_DIR_CRYPTOJS; ?>/rollups/aes.js"></script> |
424 | <script type="text/javascript" src="<?php echo DEP_DIR_CRYPTOJS; ?>/rollups/aes.js"></script> |
418 | <script type="text/javascript" src="<?php echo DEP_DIR_CRYPTOJS; ?>/rollups/md5.js"></script> |
425 | <script type="text/javascript" src="<?php echo DEP_DIR_CRYPTOJS; ?>/rollups/md5.js"></script> |
419 | 426 | ||
420 | <script type="text/javascript" src="<?php echo DEP_DIR_SAJAX; ?>/php/json_stringify.js"></script> |
427 | <script type="text/javascript" src="<?php echo DEP_DIR_SAJAX; ?>/php/json_stringify.js"></script> |
421 | <script type="text/javascript" src="<?php echo DEP_DIR_SAJAX; ?>/php/json_parse.js"></script> |
428 | <script type="text/javascript" src="<?php echo DEP_DIR_SAJAX; ?>/php/json_parse.js"></script> |
422 | <script type="text/javascript" src="<?php echo DEP_DIR_SAJAX; ?>/php/sajax.js"></script> |
429 | <script type="text/javascript" src="<?php echo DEP_DIR_SAJAX; ?>/php/sajax.js"></script> |
423 | 430 | ||
424 | <script type="text/javascript" src="<?php echo DEP_DIR_CRC32; ?>/crc32.js"></script> |
431 | <script type="text/javascript" src="<?php echo DEP_DIR_CRC32; ?>/crc32.js"></script> |
425 | 432 | ||
426 | <script type="text/javascript"> |
433 | <script type="text/javascript"> |
427 | 434 | ||
428 | <?php |
435 | <?php |
429 | sajax_show_javascript(); |
436 | sajax_show_javascript(); |
430 | ?> |
437 | ?> |
431 | 438 | ||
432 | var product = <?php echo json_encode(PRODUCT_NAME); ?>; |
439 | var product = <?php echo json_encode(PRODUCT_NAME); ?>; |
433 | var version = <?php echo json_encode(MCC_VER); ?>; |
440 | var version = <?php echo json_encode(MCC_VER); ?>; |
434 | var room = <?php echo json_encode($ses_room); ?>; |
441 | var room = <?php echo json_encode($ses_room); ?>; |
435 | var user = <?php echo json_encode($ses_user); ?>; |
442 | var user = <?php echo json_encode($ses_user); ?>; |
436 | var curseq = 0; |
443 | var curseq = 0; |
437 | var roomSalt = "<?php echo sha1(X_SALT.$ses_room); ?>"; |
444 | var roomSalt = "<?php echo sha1(X_SALT.$ses_room); ?>"; |
438 | var curVisible = true; |
445 | var curVisible = true; |
439 | var backgroundSeqCount = 0; |
446 | var backgroundSeqCount = 0; |
440 | var blink = 0; |
447 | var blink = 0; |
441 | var uniqueID = ""; |
448 | var uniqueID = ""; |
442 | 449 | ||
443 | // --- Useful functions |
450 | // --- Useful functions |
444 | 451 | ||
445 | function scrollToBottom() { |
452 | function scrollToBottom() { |
446 | // http://www.sourcetricks.com/2010/07/javascript-scroll-to-bottom-of-page.html |
453 | // http://www.sourcetricks.com/2010/07/javascript-scroll-to-bottom-of-page.html |
447 | window.scrollTo(0, document.body.scrollHeight); |
454 | window.scrollTo(0, document.body.scrollHeight); |
448 | } |
455 | } |
449 | 456 | ||
450 | function randNum(min, max) { |
457 | function randNum(min, max) { |
451 | return Math.floor((Math.random()*max)+min); |
458 | return Math.floor((Math.random()*max)+min); |
452 | } |
459 | } |
453 | 460 | ||
454 | // http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript/1349426#1349426 |
461 | // http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript/1349426#1349426 |
455 | function randomString(len) { |
462 | function randomString(len) { |
456 | var text = ""; |
463 | var text = ""; |
457 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
464 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
458 | 465 | ||
459 | for( var i=0; i < len; i++ ) { |
466 | for( var i=0; i < len; i++ ) { |
460 | text += possible.charAt(Math.floor(Math.random() * possible.length)); |
467 | text += possible.charAt(Math.floor(Math.random() * possible.length)); |
461 | } |
468 | } |
462 | 469 | ||
463 | return text; |
470 | return text; |
464 | } |
471 | } |
465 | 472 | ||
466 | // Array Remove - By John Resig (MIT Licensed) |
473 | // Array Remove - By John Resig (MIT Licensed) |
467 | Array.prototype.remove = function(from, to) { |
474 | Array.prototype.remove = function(from, to) { |
468 | var rest = this.slice((to || from) + 1 || this.length); |
475 | var rest = this.slice((to || from) + 1 || this.length); |
469 | this.length = from < 0 ? this.length + from : from; |
476 | this.length = from < 0 ? this.length + from : from; |
470 | return this.push.apply(this, rest); |
477 | return this.push.apply(this, rest); |
471 | }; |
478 | }; |
472 | 479 | ||
473 | function sleep(milliseconds) { |
480 | function sleep(milliseconds) { |
474 | // http://www.phpied.com/sleep-in-javascript/ |
481 | // http://www.phpied.com/sleep-in-javascript/ |
475 | var start = new Date().getTime(); |
482 | var start = new Date().getTime(); |
476 | for (var i = 0; i < 1e7; i++) { |
483 | for (var i = 0; i < 1e7; i++) { |
477 | if ((new Date().getTime() - start) > milliseconds) { |
484 | if ((new Date().getTime() - start) > milliseconds) { |
478 | break; |
485 | break; |
479 | } |
486 | } |
480 | } |
487 | } |
481 | } |
488 | } |
482 | 489 | ||
483 | function get_html_translation_table(table, quote_style) { |
490 | function get_html_translation_table(table, quote_style) { |
484 | // From: http://phpjs.org/functions |
491 | // From: http://phpjs.org/functions |
485 | // + original by: Philip Peterson |
492 | // + original by: Philip Peterson |
486 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
493 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
487 | // + bugfixed by: noname |
494 | // + bugfixed by: noname |
488 | // + bugfixed by: Alex |
495 | // + bugfixed by: Alex |
489 | // + bugfixed by: Marco |
496 | // + bugfixed by: Marco |
490 | // + bugfixed by: madipta |
497 | // + bugfixed by: madipta |
491 | // + improved by: KELAN |
498 | // + improved by: KELAN |
492 | // + improved by: Brett Zamir (http://brett-zamir.me) |
499 | // + improved by: Brett Zamir (http://brett-zamir.me) |
493 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) |
500 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) |
494 | // + input by: Frank Forte |
501 | // + input by: Frank Forte |
495 | // + bugfixed by: T.Wild |
502 | // + bugfixed by: T.Wild |
496 | // + input by: Ratheous |
503 | // + input by: Ratheous |
497 | // % note: It has been decided that we're not going to add global |
504 | // % note: It has been decided that we're not going to add global |
498 | // % note: dependencies to php.js, meaning the constants are not |
505 | // % note: dependencies to php.js, meaning the constants are not |
499 | // % note: real constants, but strings instead. Integers are also supported if someone |
506 | // % note: real constants, but strings instead. Integers are also supported if someone |
500 | // % note: chooses to create the constants themselves. |
507 | // % note: chooses to create the constants themselves. |
501 | // * example 1: get_html_translation_table('HTML_SPECIALCHARS'); |
508 | // * example 1: get_html_translation_table('HTML_SPECIALCHARS'); |
502 | // * returns 1: {'"': '"', '&': '&', '<': '<', '>': '>'} |
509 | // * returns 1: {'"': '"', '&': '&', '<': '<', '>': '>'} |
503 | var entities = {}, |
510 | var entities = {}, |
504 | hash_map = {}, |
511 | hash_map = {}, |
505 | decimal; |
512 | decimal; |
506 | var constMappingTable = {}, |
513 | var constMappingTable = {}, |
507 | constMappingQuoteStyle = {}; |
514 | constMappingQuoteStyle = {}; |
508 | var useTable = {}, |
515 | var useTable = {}, |
509 | useQuoteStyle = {}; |
516 | useQuoteStyle = {}; |
510 | 517 | ||
511 | // Translate arguments |
518 | // Translate arguments |
512 | constMappingTable[0] = 'HTML_SPECIALCHARS'; |
519 | constMappingTable[0] = 'HTML_SPECIALCHARS'; |
513 | constMappingTable[1] = 'HTML_ENTITIES'; |
520 | constMappingTable[1] = 'HTML_ENTITIES'; |
514 | constMappingQuoteStyle[0] = 'ENT_NOQUOTES'; |
521 | constMappingQuoteStyle[0] = 'ENT_NOQUOTES'; |
515 | constMappingQuoteStyle[2] = 'ENT_COMPAT'; |
522 | constMappingQuoteStyle[2] = 'ENT_COMPAT'; |
516 | constMappingQuoteStyle[3] = 'ENT_QUOTES'; |
523 | constMappingQuoteStyle[3] = 'ENT_QUOTES'; |
517 | 524 | ||
518 | useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS'; |
525 | useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS'; |
519 | useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT'; |
526 | useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT'; |
520 | 527 | ||
521 | if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') { |
528 | if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') { |
522 | throw new Error("Table: " + useTable + ' not supported'); |
529 | throw new Error("Table: " + useTable + ' not supported'); |
523 | // return false; |
530 | // return false; |
524 | } |
531 | } |
525 | 532 | ||
526 | entities['38'] = '&'; |
533 | entities['38'] = '&'; |
527 | if (useTable === 'HTML_ENTITIES') { |
534 | if (useTable === 'HTML_ENTITIES') { |
528 | entities['160'] = ' '; |
535 | entities['160'] = ' '; |
529 | entities['161'] = '¡'; |
536 | entities['161'] = '¡'; |
530 | entities['162'] = '¢'; |
537 | entities['162'] = '¢'; |
531 | entities['163'] = '£'; |
538 | entities['163'] = '£'; |
532 | entities['164'] = '¤'; |
539 | entities['164'] = '¤'; |
533 | entities['165'] = '¥'; |
540 | entities['165'] = '¥'; |
534 | entities['166'] = '¦'; |
541 | entities['166'] = '¦'; |
535 | entities['167'] = '§'; |
542 | entities['167'] = '§'; |
536 | entities['168'] = '¨'; |
543 | entities['168'] = '¨'; |
537 | entities['169'] = '©'; |
544 | entities['169'] = '©'; |
538 | entities['170'] = 'ª'; |
545 | entities['170'] = 'ª'; |
539 | entities['171'] = '«'; |
546 | entities['171'] = '«'; |
540 | entities['172'] = '¬'; |
547 | entities['172'] = '¬'; |
541 | entities['173'] = '­'; |
548 | entities['173'] = '­'; |
542 | entities['174'] = '®'; |
549 | entities['174'] = '®'; |
543 | entities['175'] = '¯'; |
550 | entities['175'] = '¯'; |
544 | entities['176'] = '°'; |
551 | entities['176'] = '°'; |
545 | entities['177'] = '±'; |
552 | entities['177'] = '±'; |
546 | entities['178'] = '²'; |
553 | entities['178'] = '²'; |
547 | entities['179'] = '³'; |
554 | entities['179'] = '³'; |
548 | entities['180'] = '´'; |
555 | entities['180'] = '´'; |
549 | entities['181'] = 'µ'; |
556 | entities['181'] = 'µ'; |
550 | entities['182'] = '¶'; |
557 | entities['182'] = '¶'; |
551 | entities['183'] = '·'; |
558 | entities['183'] = '·'; |
552 | entities['184'] = '¸'; |
559 | entities['184'] = '¸'; |
553 | entities['185'] = '¹'; |
560 | entities['185'] = '¹'; |
554 | entities['186'] = 'º'; |
561 | entities['186'] = 'º'; |
555 | entities['187'] = '»'; |
562 | entities['187'] = '»'; |
556 | entities['188'] = '¼'; |
563 | entities['188'] = '¼'; |
557 | entities['189'] = '½'; |
564 | entities['189'] = '½'; |
558 | entities['190'] = '¾'; |
565 | entities['190'] = '¾'; |
559 | entities['191'] = '¿'; |
566 | entities['191'] = '¿'; |
560 | entities['192'] = 'À'; |
567 | entities['192'] = 'À'; |
561 | entities['193'] = 'Á'; |
568 | entities['193'] = 'Á'; |
562 | entities['194'] = 'Â'; |
569 | entities['194'] = 'Â'; |
563 | entities['195'] = 'Ã'; |
570 | entities['195'] = 'Ã'; |
564 | entities['196'] = 'Ä'; |
571 | entities['196'] = 'Ä'; |
565 | entities['197'] = 'Å'; |
572 | entities['197'] = 'Å'; |
566 | entities['198'] = 'Æ'; |
573 | entities['198'] = 'Æ'; |
567 | entities['199'] = 'Ç'; |
574 | entities['199'] = 'Ç'; |
568 | entities['200'] = 'È'; |
575 | entities['200'] = 'È'; |
569 | entities['201'] = 'É'; |
576 | entities['201'] = 'É'; |
570 | entities['202'] = 'Ê'; |
577 | entities['202'] = 'Ê'; |
571 | entities['203'] = 'Ë'; |
578 | entities['203'] = 'Ë'; |
572 | entities['204'] = 'Ì'; |
579 | entities['204'] = 'Ì'; |
573 | entities['205'] = 'Í'; |
580 | entities['205'] = 'Í'; |
574 | entities['206'] = 'Î'; |
581 | entities['206'] = 'Î'; |
575 | entities['207'] = 'Ï'; |
582 | entities['207'] = 'Ï'; |
576 | entities['208'] = 'Ð'; |
583 | entities['208'] = 'Ð'; |
577 | entities['209'] = 'Ñ'; |
584 | entities['209'] = 'Ñ'; |
578 | entities['210'] = 'Ò'; |
585 | entities['210'] = 'Ò'; |
579 | entities['211'] = 'Ó'; |
586 | entities['211'] = 'Ó'; |
580 | entities['212'] = 'Ô'; |
587 | entities['212'] = 'Ô'; |
581 | entities['213'] = 'Õ'; |
588 | entities['213'] = 'Õ'; |
582 | entities['214'] = 'Ö'; |
589 | entities['214'] = 'Ö'; |
583 | entities['215'] = '×'; |
590 | entities['215'] = '×'; |
584 | entities['216'] = 'Ø'; |
591 | entities['216'] = 'Ø'; |
585 | entities['217'] = 'Ù'; |
592 | entities['217'] = 'Ù'; |
586 | entities['218'] = 'Ú'; |
593 | entities['218'] = 'Ú'; |
587 | entities['219'] = 'Û'; |
594 | entities['219'] = 'Û'; |
588 | entities['220'] = 'Ü'; |
595 | entities['220'] = 'Ü'; |
589 | entities['221'] = 'Ý'; |
596 | entities['221'] = 'Ý'; |
590 | entities['222'] = 'Þ'; |
597 | entities['222'] = 'Þ'; |
591 | entities['223'] = 'ß'; |
598 | entities['223'] = 'ß'; |
592 | entities['224'] = 'à'; |
599 | entities['224'] = 'à'; |
593 | entities['225'] = 'á'; |
600 | entities['225'] = 'á'; |
594 | entities['226'] = 'â'; |
601 | entities['226'] = 'â'; |
595 | entities['227'] = 'ã'; |
602 | entities['227'] = 'ã'; |
596 | entities['228'] = 'ä'; |
603 | entities['228'] = 'ä'; |
597 | entities['229'] = 'å'; |
604 | entities['229'] = 'å'; |
598 | entities['230'] = 'æ'; |
605 | entities['230'] = 'æ'; |
599 | entities['231'] = 'ç'; |
606 | entities['231'] = 'ç'; |
600 | entities['232'] = 'è'; |
607 | entities['232'] = 'è'; |
601 | entities['233'] = 'é'; |
608 | entities['233'] = 'é'; |
602 | entities['234'] = 'ê'; |
609 | entities['234'] = 'ê'; |
603 | entities['235'] = 'ë'; |
610 | entities['235'] = 'ë'; |
604 | entities['236'] = 'ì'; |
611 | entities['236'] = 'ì'; |
605 | entities['237'] = 'í'; |
612 | entities['237'] = 'í'; |
606 | entities['238'] = 'î'; |
613 | entities['238'] = 'î'; |
607 | entities['239'] = 'ï'; |
614 | entities['239'] = 'ï'; |
608 | entities['240'] = 'ð'; |
615 | entities['240'] = 'ð'; |
609 | entities['241'] = 'ñ'; |
616 | entities['241'] = 'ñ'; |
610 | entities['242'] = 'ò'; |
617 | entities['242'] = 'ò'; |
611 | entities['243'] = 'ó'; |
618 | entities['243'] = 'ó'; |
612 | entities['244'] = 'ô'; |
619 | entities['244'] = 'ô'; |
613 | entities['245'] = 'õ'; |
620 | entities['245'] = 'õ'; |
614 | entities['246'] = 'ö'; |
621 | entities['246'] = 'ö'; |
615 | entities['247'] = '÷'; |
622 | entities['247'] = '÷'; |
616 | entities['248'] = 'ø'; |
623 | entities['248'] = 'ø'; |
617 | entities['249'] = 'ù'; |
624 | entities['249'] = 'ù'; |
618 | entities['250'] = 'ú'; |
625 | entities['250'] = 'ú'; |
619 | entities['251'] = 'û'; |
626 | entities['251'] = 'û'; |
620 | entities['252'] = 'ü'; |
627 | entities['252'] = 'ü'; |
621 | entities['253'] = 'ý'; |
628 | entities['253'] = 'ý'; |
622 | entities['254'] = 'þ'; |
629 | entities['254'] = 'þ'; |
623 | entities['255'] = 'ÿ'; |
630 | entities['255'] = 'ÿ'; |
624 | } |
631 | } |
625 | 632 | ||
626 | if (useQuoteStyle !== 'ENT_NOQUOTES') { |
633 | if (useQuoteStyle !== 'ENT_NOQUOTES') { |
627 | entities['34'] = '"'; |
634 | entities['34'] = '"'; |
628 | } |
635 | } |
629 | if (useQuoteStyle === 'ENT_QUOTES') { |
636 | if (useQuoteStyle === 'ENT_QUOTES') { |
630 | entities['39'] = '''; |
637 | entities['39'] = '''; |
631 | } |
638 | } |
632 | entities['60'] = '<'; |
639 | entities['60'] = '<'; |
633 | entities['62'] = '>'; |
640 | entities['62'] = '>'; |
634 | 641 | ||
635 | // ascii decimals to real symbols |
642 | // ascii decimals to real symbols |
636 | for (decimal in entities) { |
643 | for (decimal in entities) { |
637 | if (entities.hasOwnProperty(decimal)) { |
644 | if (entities.hasOwnProperty(decimal)) { |
638 | hash_map[String.fromCharCode(decimal)] = entities[decimal]; |
645 | hash_map[String.fromCharCode(decimal)] = entities[decimal]; |
639 | } |
646 | } |
640 | } |
647 | } |
641 | 648 | ||
642 | return hash_map; |
649 | return hash_map; |
643 | } |
650 | } |
644 | 651 | ||
645 | function htmlentities(string, quote_style, charset, double_encode) { |
652 | function htmlentities(string, quote_style, charset, double_encode) { |
646 | // From: http://phpjs.org/functions |
653 | // From: http://phpjs.org/functions |
647 | // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
654 | // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
648 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
655 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
649 | // + improved by: nobbler |
656 | // + improved by: nobbler |
650 | // + tweaked by: Jack |
657 | // + tweaked by: Jack |
651 | // + bugfixed by: Onno Marsman |
658 | // + bugfixed by: Onno Marsman |
652 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
659 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
653 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) |
660 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) |
654 | // + input by: Ratheous |
661 | // + input by: Ratheous |
655 | // + improved by: Rafa. Kukawski (http://blog.kukawski.pl) |
662 | // + improved by: Rafa. Kukawski (http://blog.kukawski.pl) |
656 | // + improved by: Dj (http://phpjs.org/functions/htmlentities:425#comment_134018) |
663 | // + improved by: Dj (http://phpjs.org/functions/htmlentities:425#comment_134018) |
657 | // - depends on: get_html_translation_table |
664 | // - depends on: get_html_translation_table |
658 | // * example 1: htmlentities('Kevin & van Zonneveld'); |
665 | // * example 1: htmlentities('Kevin & van Zonneveld'); |
659 | // * returns 1: 'Kevin & van Zonneveld' |
666 | // * returns 1: 'Kevin & van Zonneveld' |
660 | // * example 2: htmlentities("foo'bar","ENT_QUOTES"); |
667 | // * example 2: htmlentities("foo'bar","ENT_QUOTES"); |
661 | // * returns 2: 'foo'bar' |
668 | // * returns 2: 'foo'bar' |
662 | var hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style), |
669 | var hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style), |
663 | symbol = ''; |
670 | symbol = ''; |
664 | string = string == null ? '' : string + ''; |
671 | string = string == null ? '' : string + ''; |
665 | 672 | ||
666 | if (!hash_map) { |
673 | if (!hash_map) { |
667 | return false; |
674 | return false; |
668 | } |
675 | } |
669 | 676 | ||
670 | if (quote_style && quote_style === 'ENT_QUOTES') { |
677 | if (quote_style && quote_style === 'ENT_QUOTES') { |
671 | hash_map["'"] = '''; |
678 | hash_map["'"] = '''; |
672 | } |
679 | } |
673 | 680 | ||
674 | if (!!double_encode || double_encode == null) { // TODO: korrekt "== null"? nicht "!= null"? |
681 | if (!!double_encode || double_encode == null) { // TODO: korrekt "== null"? nicht "!= null"? |
675 | for (symbol in hash_map) { |
682 | for (symbol in hash_map) { |
676 | if (hash_map.hasOwnProperty(symbol)) { |
683 | if (hash_map.hasOwnProperty(symbol)) { |
677 | string = string.split(symbol).join(hash_map[symbol]); |
684 | string = string.split(symbol).join(hash_map[symbol]); |
678 | } |
685 | } |
679 | } |
686 | } |
680 | } else { |
687 | } else { |
681 | string = string.replace(/([\s\S]*?)(&(?:#\d+|#x[\da-f]+|[a-zA-Z][\da-z]*);|$)/g, function (ignore, text, entity) { |
688 | string = string.replace(/([\s\S]*?)(&(?:#\d+|#x[\da-f]+|[a-zA-Z][\da-z]*);|$)/g, function (ignore, text, entity) { |
682 | for (symbol in hash_map) { |
689 | for (symbol in hash_map) { |
683 | if (hash_map.hasOwnProperty(symbol)) { |
690 | if (hash_map.hasOwnProperty(symbol)) { |
684 | text = text.split(symbol).join(hash_map[symbol]); |
691 | text = text.split(symbol).join(hash_map[symbol]); |
685 | } |
692 | } |
686 | } |
693 | } |
687 | 694 | ||
688 | return text + entity; |
695 | return text + entity; |
689 | }); |
696 | }); |
690 | } |
697 | } |
691 | 698 | ||
692 | return string; |
699 | return string; |
693 | } |
700 | } |
694 | 701 | ||
695 | /* accepts parameters |
702 | /* accepts parameters |
696 | * h Object = {h:x, s:y, v:z} |
703 | * h Object = {h:x, s:y, v:z} |
697 | * OR |
704 | * OR |
698 | * h, s, v |
705 | * h, s, v |
699 | * This code expects 0 <= h, s, v <= 1 |
706 | * This code expects 0 <= h, s, v <= 1 |
700 | * http://stackoverflow.com/a/17243070 |
707 | * http://stackoverflow.com/a/17243070 |
701 | */ |
708 | */ |
702 | function HSVtoRGB(h, s, v) { |
709 | function HSVtoRGB(h, s, v) { |
703 | var r, g, b, i, f, p, q, t; |
710 | var r, g, b, i, f, p, q, t; |
704 | if (h && s === undefined && v === undefined) { |
711 | if (h && s === undefined && v === undefined) { |
705 | s = h.s, v = h.v, h = h.h; |
712 | s = h.s, v = h.v, h = h.h; |
706 | } |
713 | } |
707 | i = Math.floor(h * 6); |
714 | i = Math.floor(h * 6); |
708 | f = h * 6 - i; |
715 | f = h * 6 - i; |
709 | p = v * (1 - s); |
716 | p = v * (1 - s); |
710 | q = v * (1 - f * s); |
717 | q = v * (1 - f * s); |
711 | t = v * (1 - (1 - f) * s); |
718 | t = v * (1 - (1 - f) * s); |
712 | switch (i % 6) { |
719 | switch (i % 6) { |
713 | case 0: r = v, g = t, b = p; break; |
720 | case 0: r = v, g = t, b = p; break; |
714 | case 1: r = q, g = v, b = p; break; |
721 | case 1: r = q, g = v, b = p; break; |
715 | case 2: r = p, g = v, b = t; break; |
722 | case 2: r = p, g = v, b = t; break; |
716 | case 3: r = p, g = q, b = v; break; |
723 | case 3: r = p, g = q, b = v; break; |
717 | case 4: r = t, g = p, b = v; break; |
724 | case 4: r = t, g = p, b = v; break; |
718 | case 5: r = v, g = p, b = q; break; |
725 | case 5: r = v, g = p, b = q; break; |
719 | } |
726 | } |
720 | return { |
727 | return { |
721 | r: Math.floor(r * 255), |
728 | r: Math.floor(r * 255), |
722 | g: Math.floor(g * 255), |
729 | g: Math.floor(g * 255), |
723 | b: Math.floor(b * 255) |
730 | b: Math.floor(b * 255) |
724 | }; |
731 | }; |
725 | } |
732 | } |
726 | 733 | ||
727 | // Returns something between 0..255 |
734 | // Returns something between 0..255 |
728 | function crc8(message) { |
735 | function crc8(message) { |
729 | // return parseInt(CryptoJS.SHA256(message).toString().substr(0,2), 16); |
736 | // return parseInt(CryptoJS.SHA256(message).toString().substr(0,2), 16); |
730 | // return parseInt(CryptoJS.MD5(message).toString().substr(0,2), 16); |
737 | // return parseInt(CryptoJS.MD5(message).toString().substr(0,2), 16); |
731 | return crc32(message)%256; |
738 | return crc32(message)%256; |
732 | } |
739 | } |
733 | 740 | ||
734 | // http://stackoverflow.com/a/2998822 |
741 | // http://stackoverflow.com/a/2998822 |
735 | function pad(num, size) { |
742 | function pad(num, size) { |
736 | var s = num+""; |
743 | var s = num+""; |
737 | while (s.length < size) s = "0" + s; |
744 | while (s.length < size) s = "0" + s; |
738 | return s; |
745 | return s; |
739 | } |
746 | } |
740 | 747 | ||
741 | function html_rgb(r, g, b) { |
748 | function html_rgb(r, g, b) { |
742 | return "#" + pad(r.toString(16), 2) + pad(g.toString(16), 2) + pad(b.toString(16), 2); |
749 | return "#" + pad(r.toString(16), 2) + pad(g.toString(16), 2) + pad(b.toString(16), 2); |
743 | 750 | ||
744 | } |
751 | } |
745 | 752 | ||
746 | function getToken() { |
753 | function getToken() { |
747 | return new Date().getTime() + randNum(0, 999999); |
754 | return new Date().getTime() + randNum(0, 999999); |
748 | } |
755 | } |
749 | 756 | ||
750 | function replaceURLWithHTMLLinks(text) { |
757 | function replaceURLWithHTMLLinks(text) { |
751 | // TODO: deferer? |
758 | // TODO: deferer? |
752 | 759 | ||
753 | // test@example.com |
760 | // test@example.com |
754 | text = text.replace(/mailto:(.+?)@/ig, "mailto:$1#"); |
761 | text = text.replace(/mailto:(.+?)@/ig, "mailto:$1#"); |
755 | text = text.replace(/(([^>" ]+?)@([^<" ]+))/ig, "<a href=\"mailto:$1\" target=\"_blank\">$1</a>"); |
762 | text = text.replace(/(([^>" ]+?)@([^<" ]+))/ig, "<a href=\"mailto:$1\" target=\"_blank\">$1</a>"); |
756 | text = text.replace(/mailto:(.+?)#/ig, "mailto:$1@"); |
763 | text = text.replace(/mailto:(.+?)#/ig, "mailto:$1@"); |
757 | 764 | ||
758 | // www.example.com |
765 | // www.example.com |
759 | text = text.replace(/:\/\/www\./ig, "://###."); |
766 | text = text.replace(/:\/\/www\./ig, "://###."); |
760 | text = text.replace(/\b(www\.(.+?))\b/ig, "http://$1"); |
767 | text = text.replace(/\b(www\.(.+?))\b/ig, "http://$1"); |
761 | text = text.replace(/:\/\/###\./ig, "://www."); |
768 | text = text.replace(/:\/\/###\./ig, "://www."); |
762 | 769 | ||
763 | // http://www.google.com |
770 | // http://www.google.com |
764 | // https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links |
771 | // https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links |
765 | var exp = /(\b(https?|ftp|file|mailto):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; |
772 | var exp = /(\b(https?|ftp|file|mailto):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; |
766 | text = text.replace(/href="(.{1,5}?):/ig, "href=\"$1#"); |
773 | text = text.replace(/href="(.{1,5}?):/ig, "href=\"$1#"); |
767 | text = text.replace(exp, "<a href=\"$1\" target=\"_blank\">$1</a>"); |
774 | text = text.replace(exp, "<a href=\"$1\" target=\"_blank\">$1</a>"); |
768 | text = text.replace(/href="(.{1,5}?)#/ig, "href=\"$1:"); |
775 | text = text.replace(/href="(.{1,5}?)#/ig, "href=\"$1:"); |
769 | 776 | ||
770 | return text; |
777 | return text; |
771 | } |
778 | } |
772 | 779 | ||
773 | function luminance_perceived(r, g, b) { |
780 | function luminance_perceived(r, g, b) { |
774 | // Luminance (standard, objective) |
781 | // Luminance (standard, objective) |
775 | // http://stackoverflow.com/a/596243 |
782 | // http://stackoverflow.com/a/596243 |
776 | // return (0.2126*r) + (0.7152*g) + (0.0722*b); |
783 | // return (0.2126*r) + (0.7152*g) + (0.0722*b); |
777 | 784 | ||
778 | // Luminance (perceived option 1) |
785 | // Luminance (perceived option 1) |
779 | // http://stackoverflow.com/a/596243 |
786 | // http://stackoverflow.com/a/596243 |
780 | // return (0.299*r) + (0.587*g) + (0.114*b); |
787 | // return (0.299*r) + (0.587*g) + (0.114*b); |
781 | 788 | ||
782 | // Luminance (perceived option 2, slower to calculate) |
789 | // Luminance (perceived option 2, slower to calculate) |
783 | // http://alienryderflex.com/hsp.html |
790 | // http://alienryderflex.com/hsp.html |
784 | // return Math.sqrt(0.299*Math.pow(r,2) + 0.587*Math.pow(g,2) + 0.114*Math.pow(b,2)) |
791 | // return Math.sqrt(0.299*Math.pow(r,2) + 0.587*Math.pow(g,2) + 0.114*Math.pow(b,2)) |
785 | 792 | ||
786 | // TODO: Is this the correct formula? |
793 | // TODO: Is this the correct formula? |
787 | return Math.sqrt(0.241*Math.pow(r,2) + 0.691*Math.pow(g,2) + 0.068*Math.pow(b,2)) |
794 | return Math.sqrt(0.241*Math.pow(r,2) + 0.691*Math.pow(g,2) + 0.068*Math.pow(b,2)) |
788 | } |
795 | } |
789 | 796 | ||
790 | function colorizeUsername(username, salt) { |
797 | function colorizeUsername(username, salt) { |
791 | if (salt === undefined) salt = ""; |
798 | if (salt === undefined) salt = ""; |
792 | 799 | ||
793 | // return "#" + CryptoJS.SHA256(username).toString().substr(0,6); |
800 | // return "#" + CryptoJS.SHA256(username).toString().substr(0,6); |
794 | 801 | ||
795 | // TODO: is there any method obtaining the background color in runtime? instead of using THEME_DARK |
802 | // TODO: is there any method obtaining the background color in runtime? instead of using THEME_DARK |
796 | 803 | ||
797 | var x = crc8(username+salt); |
804 | var x = crc8(username+salt); |
798 | var rgb = HSVtoRGB(x/255, 1, 0.75); |
805 | var rgb = HSVtoRGB(x/255, 1, 0.75); |
799 | 806 | ||
800 | var bg_lum = <?php if (THEME_DARK) echo 0 /* assume #000011 */; else echo 255 /* assume white */; ?>; |
807 | var bg_lum = <?php if (THEME_DARK) echo 0 /* assume #000011 */; else echo 255 /* assume white */; ?>; |
801 | var fg_lum = luminance_perceived(rgb.r, rgb.g, rgb.b); |
808 | var fg_lum = luminance_perceived(rgb.r, rgb.g, rgb.b); |
802 | 809 | ||
803 | var lum_dif = Math.floor(Math.abs(bg_lum-fg_lum)); |
810 | var lum_dif = Math.floor(Math.abs(bg_lum-fg_lum)); |
804 | 811 | ||
805 | if (lum_dif < 128) { |
812 | if (lum_dif < 128) { |
806 | <?php if (THEME_DARK) { ?> |
813 | <?php if (THEME_DARK) { ?> |
807 | rgb.r = (rgb.r + (128-lum_dif)); if (rgb.r > 255) rgb.r = 255; |
814 | rgb.r = (rgb.r + (128-lum_dif)); if (rgb.r > 255) rgb.r = 255; |
808 | rgb.g = (rgb.g + (128-lum_dif)); if (rgb.g > 255) rgb.g = 255; |
815 | rgb.g = (rgb.g + (128-lum_dif)); if (rgb.g > 255) rgb.g = 255; |
809 | rgb.b = (rgb.b + (128-lum_dif)); if (rgb.b > 255) rgb.b = 255; |
816 | rgb.b = (rgb.b + (128-lum_dif)); if (rgb.b > 255) rgb.b = 255; |
810 | <?php } else { ?> |
817 | <?php } else { ?> |
811 | rgb.r = (rgb.r - (128-lum_dif)); if (rgb.r < 0) rgb.r = 0; |
818 | rgb.r = (rgb.r - (128-lum_dif)); if (rgb.r < 0) rgb.r = 0; |
812 | rgb.g = (rgb.g - (128-lum_dif)); if (rgb.g < 0) rgb.g = 0; |
819 | rgb.g = (rgb.g - (128-lum_dif)); if (rgb.g < 0) rgb.g = 0; |
813 | rgb.b = (rgb.b - (128-lum_dif)); if (rgb.b < 0) rgb.b = 0; |
820 | rgb.b = (rgb.b - (128-lum_dif)); if (rgb.b < 0) rgb.b = 0; |
814 | <?php } ?> |
821 | <?php } ?> |
815 | } |
822 | } |
816 | 823 | ||
817 | return html_rgb(rgb.r, rgb.g, rgb.b); |
824 | return html_rgb(rgb.r, rgb.g, rgb.b); |
818 | } |
825 | } |
819 | 826 | ||
820 | // --- Getter and setter |
827 | // --- Getter and setter |
821 | 828 | ||
822 | function encryptionEnabled() { |
829 | function encryptionEnabled() { |
823 | return document.getElementById("doencrypt").checked == 1; |
830 | return document.getElementById("doencrypt").checked == 1; |
824 | } |
831 | } |
825 | 832 | ||
826 | function getPassword() { |
833 | function getPassword() { |
827 | return document.getElementById("pwd").value.trim(); |
834 | return document.getElementById("pwd").value.trim(); |
828 | } |
835 | } |
829 | 836 | ||
830 | function autoscrollEnabled() { |
837 | function autoscrollEnabled() { |
831 | return document.getElementById("autoscroll").checked == 1; |
838 | return document.getElementById("autoscroll").checked == 1; |
832 | } |
839 | } |
833 | 840 | ||
834 | // --- MD5 |
841 | // --- MD5 |
835 | 842 | ||
836 | var selftest_md5_finished = false; |
843 | var selftest_md5_finished = false; |
837 | function selftest_md5() { |
844 | function selftest_md5() { |
838 | if (selftest_md5_finished) return; |
845 | if (selftest_md5_finished) return; |
839 | selftest_md5_finished = true; |
846 | selftest_md5_finished = true; |
840 | 847 | ||
841 | var errors = ""; |
848 | var errors = ""; |
842 | cmp_a = CryptoJS.enc.Base64.stringify(CryptoJS.MD5("Message")); |
849 | cmp_a = CryptoJS.enc.Base64.stringify(CryptoJS.MD5("Message")); |
843 | cmp_b = "TCqP5+ryRyHMep8BdRFb1A=="; |
850 | cmp_b = "TCqP5+ryRyHMep8BdRFb1A=="; |
844 | if (cmp_a !== cmp_b) { |
851 | if (cmp_a !== cmp_b) { |
845 | errors += "MD5 self test failed!\n"; |
852 | errors += "MD5 self test failed!\n"; |
846 | console.error("MD5 self test failed: '" + cmp_a + "' vs. '" + cmp_b + "'"); |
853 | console.error("MD5 self test failed: '" + cmp_a + "' vs. '" + cmp_b + "'"); |
847 | } |
854 | } |
848 | 855 | ||
849 | if (errors) { |
856 | if (errors) { |
850 | alert(errors+"\nYour browser seems to be buggy. Decryption of particular messages might fail."); |
857 | alert(errors+"\nYour browser seems to be buggy. Decryption of particular messages might fail."); |
851 | } else { |
858 | } else { |
852 | console.info("MD5 self test passed"); |
859 | console.info("MD5 self test passed"); |
853 | } |
860 | } |
854 | } |
861 | } |
855 | 862 | ||
856 | function md5(message) { |
863 | function md5(message) { |
857 | selftest_md5(); |
864 | selftest_md5(); |
858 | var hash = CryptoJS.MD5(message); |
865 | var hash = CryptoJS.MD5(message); |
859 | hash = CryptoJS.enc.Base64.stringify(hash); |
866 | hash = CryptoJS.enc.Base64.stringify(hash); |
860 | return hash; |
867 | return hash; |
861 | } |
868 | } |
862 | 869 | ||
863 | // --- SHA256 |
870 | // --- SHA256 |
864 | 871 | ||
865 | var selftest_sha256_finished = false; |
872 | var selftest_sha256_finished = false; |
866 | function selftest_sha256() { |
873 | function selftest_sha256() { |
867 | if (selftest_sha256_finished) return; |
874 | if (selftest_sha256_finished) return; |
868 | selftest_sha256_finished = true; |
875 | selftest_sha256_finished = true; |
869 | 876 | ||
870 | var errors = ""; |
877 | var errors = ""; |
871 | cmp_a = CryptoJS.enc.Base64.stringify(CryptoJS.SHA256("Message")); |
878 | cmp_a = CryptoJS.enc.Base64.stringify(CryptoJS.SHA256("Message")); |
872 | cmp_b = "L3dmip37+NWEi57rSnFFypTG7ZI25Kdz9tyvpRMrL5E="; |
879 | cmp_b = "L3dmip37+NWEi57rSnFFypTG7ZI25Kdz9tyvpRMrL5E="; |
873 | if (cmp_a !== cmp_b) { |
880 | if (cmp_a !== cmp_b) { |
874 | errors += "SHA256 self test failed!\n"; |
881 | errors += "SHA256 self test failed!\n"; |
875 | console.error("SHA256 self test failed: '" + cmp_a + "' vs. '" + cmp_b + "'"); |
882 | console.error("SHA256 self test failed: '" + cmp_a + "' vs. '" + cmp_b + "'"); |
876 | } |
883 | } |
877 | 884 | ||
878 | if (errors) { |
885 | if (errors) { |
879 | alert(errors+"\nYour browser seems to be buggy. Decryption of particular messages might fail."); |
886 | alert(errors+"\nYour browser seems to be buggy. Decryption of particular messages might fail."); |
880 | } else { |
887 | } else { |
881 | console.info("SHA256 self test passed"); |
888 | console.info("SHA256 self test passed"); |
882 | } |
889 | } |
883 | } |
890 | } |
884 | 891 | ||
885 | function sha256(message) { |
892 | function sha256(message) { |
886 | selftest_sha256(); |
893 | selftest_sha256(); |
887 | var hash = CryptoJS.SHA256(message); |
894 | var hash = CryptoJS.SHA256(message); |
888 | hash = CryptoJS.enc.Base64.stringify(hash); |
895 | hash = CryptoJS.enc.Base64.stringify(hash); |
889 | return hash; |
896 | return hash; |
890 | } |
897 | } |
891 | 898 | ||
892 | // --- AES |
899 | // --- AES |
893 | 900 | ||
894 | var selftest_aes_finished = false; |
901 | var selftest_aes_finished = false; |
895 | function selftest_aes() { |
902 | function selftest_aes() { |
896 | if (selftest_aes_finished) return; |
903 | if (selftest_aes_finished) return; |
897 | selftest_aes_finished = true; |
904 | selftest_aes_finished = true; |
898 | 905 | ||
899 | var errors = ""; |
906 | var errors = ""; |
900 | var cmp_a = CryptoJS.AES.decrypt("U2FsdGVkX19kJJkA0NL7WJRdXKrdqDcf6A2yDODaL2g=", "Secret Passphrase").toString(CryptoJS.enc.Utf8); |
907 | var cmp_a = CryptoJS.AES.decrypt("U2FsdGVkX19kJJkA0NL7WJRdXKrdqDcf6A2yDODaL2g=", "Secret Passphrase").toString(CryptoJS.enc.Utf8); |
901 | var cmp_b = "Message"; |
908 | var cmp_b = "Message"; |
902 | if ((cmp_a !== cmp_b) || (aes_dec(aes_enc("Message")) !== "Message")) { |
909 | if ((cmp_a !== cmp_b) || (aes_dec(aes_enc("Message")) !== "Message")) { |
903 | errors += "AES self test failed!\n"; |
910 | errors += "AES self test failed!\n"; |
904 | console.error("AES self test failed: '" + cmp_a + "' vs. '" + cmp_b + "'"); |
911 | console.error("AES self test failed: '" + cmp_a + "' vs. '" + cmp_b + "'"); |
905 | } |
912 | } |
906 | 913 | ||
907 | if (errors) { |
914 | if (errors) { |
908 | alert(errors+"\nYour browser seems to be buggy. Decryption of particular messages might fail."); |
915 | alert(errors+"\nYour browser seems to be buggy. Decryption of particular messages might fail."); |
909 | } else { |
916 | } else { |
910 | console.info("AES self test passed"); |
917 | console.info("AES self test passed"); |
911 | } |
918 | } |
912 | } |
919 | } |
913 | 920 | ||
914 | function aes_enc(msg, ver) { |
921 | function aes_enc(msg, ver) { |
915 | // ver is currently not used |
922 | // ver is currently not used |
916 | selftest_aes(); |
923 | selftest_aes(); |
917 | var passwd = getPassword(); |
924 | var passwd = getPassword(); |
918 | return CryptoJS.AES.encrypt(msg, roomSalt+passwd); |
925 | return CryptoJS.AES.encrypt(msg, roomSalt+passwd); |
919 | } |
926 | } |
920 | 927 | ||
921 | function aes_dec(msg, ver) { |
928 | function aes_dec(msg, ver) { |
922 | // ver is currently not used |
929 | // ver is currently not used |
923 | selftest_aes(); |
930 | selftest_aes(); |
924 | var passwd = getPassword(); |
931 | var passwd = getPassword(); |
925 | try { |
932 | try { |
926 | return CryptoJS.AES.decrypt(msg, roomSalt+passwd).toString(CryptoJS.enc.Utf8); |
933 | return CryptoJS.AES.decrypt(msg, roomSalt+passwd).toString(CryptoJS.enc.Utf8); |
927 | } catch (e) { |
934 | } catch (e) { |
928 | return null; |
935 | return null; |
929 | } |
936 | } |
930 | } |
937 | } |
931 | 938 | ||
932 | // --- Decrypting stuff |
939 | // --- Decrypting stuff |
933 | 940 | ||
934 | // Alternative hash digest for compatibility issues |
941 | // Alternative hash digest for compatibility issues |
935 | // Some browsers like Jumanji have problems using SHA1 or SHA2, but work with MD5, e.g. |
942 | // Some browsers like Jumanji have problems using SHA1 or SHA2, but work with MD5, e.g. |
936 | // Jumanji 1.1.2.4 (versionsangabe nicht sicher) ist inkompatibel mit CryptoJS 3.1.2 |
943 | // Jumanji 1.1.2.4 (versionsangabe nicht sicher) ist inkompatibel mit CryptoJS 3.1.2 |
937 | // UA = "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/534.26+ (KHTML, like Gecko) Version/5.0 Safari/534.26+ jumanji/0.0" |
944 | // UA = "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/534.26+ (KHTML, like Gecko) Version/5.0 Safari/534.26+ jumanji/0.0" |
938 | // CryptoJS.MD5("Message"): |
945 | // CryptoJS.MD5("Message"): |
939 | // - Normal: 4c2a8fe7eaf24721cc7a9f0175115bd4 |
946 | // - Normal: 4c2a8fe7eaf24721cc7a9f0175115bd4 |
940 | // - Jumanji: 4c2a8fe7eaf24721cc7a9f0175115bd4 (OK) |
947 | // - Jumanji: 4c2a8fe7eaf24721cc7a9f0175115bd4 (OK) |
941 | // CryptoJS.SHA1("Message"): |
948 | // CryptoJS.SHA1("Message"): |
942 | // - Normal: 68f4145fee7dde76afceb910165924ad14cf0d00 |
949 | // - Normal: 68f4145fee7dde76afceb910165924ad14cf0d00 |
943 | // - Jumanji: 5a5aa74ecae1d696900b034d5f1b71497c170ea0 (Error) |
950 | // - Jumanji: 5a5aa74ecae1d696900b034d5f1b71497c170ea0 (Error) |
944 | // CryptoJS.SHA256("Message"): |
951 | // CryptoJS.SHA256("Message"): |
945 | // - Normal: 2f77668a9dfbf8d5848b9eeb4a7145ca94c6ed9236e4a773f6dcafa5132b2f91 |
952 | // - Normal: 2f77668a9dfbf8d5848b9eeb4a7145ca94c6ed9236e4a773f6dcafa5132b2f91 |
946 | // - Jumanji: 5a28f8b8778c15a166f7f17ebb89ce8e8381fbb5e39ddc2511239793119a649e (Error) |
953 | // - Jumanji: 5a28f8b8778c15a166f7f17ebb89ce8e8381fbb5e39ddc2511239793119a649e (Error) |
947 | // CryptoJS.AES.encrypt("Message", "Secret Passphrase"): |
954 | // CryptoJS.AES.encrypt("Message", "Secret Passphrase"): |
948 | // - Normal: U2FsdGVkX19zlzNcfljComkcU0A7XfZ+gzZbI+GyFm0= |
955 | // - Normal: U2FsdGVkX19zlzNcfljComkcU0A7XfZ+gzZbI+GyFm0= |
949 | // - Jumanji: U2FsdGVkX19kJJkA0NL7WJRdXKrdqDcf6A2yDODaL2g= (OK) |
956 | // - Jumanji: U2FsdGVkX19kJJkA0NL7WJRdXKrdqDcf6A2yDODaL2g= (OK) |
950 | // This is a fast version (4x MD5) necessary for slow computers with ARM architecture |
957 | // This is a fast version (4x MD5) necessary for slow computers with ARM architecture |
951 | function specialMD5fast4(message) { |
958 | function specialMD5fast4(message) { |
952 | var a = md5("IuErOmVyPeL2ek6e16vkjTWjssgLmd" + message); |
959 | var a = md5("IuErOmVyPeL2ek6e16vkjTWjssgLmd" + message); |
953 | var b = md5("8wxdm3mVi8UQXdboJvCctYwm8ZxTyX" + message); |
960 | var b = md5("8wxdm3mVi8UQXdboJvCctYwm8ZxTyX" + message); |
954 | return md5(a+b) + md5(b+a); |
961 | return md5(a+b) + md5(b+a); |
955 | } |
962 | } |
956 | 963 | ||
957 | function specialHash(val, entrySalt, version) { |
964 | function specialHash(val, entrySalt, version) { |
958 | var hash = null; |
965 | var hash = null; |
959 | if (version == 1) { |
966 | if (version == 1) { |
960 | hash = sha256(roomSalt+entrySalt+val); |
967 | hash = sha256(roomSalt+entrySalt+val); |
961 | } else if (version == 2) { |
968 | } else if (version == 2) { |
962 | hash = specialMD5fast4(roomSalt+entrySalt+val); |
969 | hash = specialMD5fast4(roomSalt+entrySalt+val); |
963 | } else { |
970 | } else { |
964 | console.error("Version " + version + " is unknown at specialHash()"); |
971 | console.error("Version " + version + " is unknown at specialHash()"); |
965 | return null; |
972 | return null; |
966 | } |
973 | } |
967 | return entrySalt + "_" + hash + "_ver" + version; |
974 | return entrySalt + "_" + hash + "_ver" + version; |
968 | } |
975 | } |
969 | 976 | ||
970 | function cbDecrypt(str, pEntrySalt, pHash, pVer, pMesg, offset, s) { |
977 | function cbDecrypt(str, pEntrySalt, pHash, pVer, pMesg, offset, s) { |
971 | var passwd = getPassword(); |
978 | var passwd = getPassword(); |
972 | 979 | ||
973 | var hash_client = specialHash(passwd, pEntrySalt, pVer); |
980 | var hash_client = specialHash(passwd, pEntrySalt, pVer); |
974 | var hash_server = pEntrySalt + "_" + pHash + "_ver" + pVer; |
981 | var hash_server = pEntrySalt + "_" + pHash + "_ver" + pVer; |
975 | if (hash_client == hash_server) { |
982 | if (hash_client == hash_server) { |
976 | var msg = aes_dec(pMesg, pVer); |
983 | var msg = aes_dec(pMesg, pVer); |
977 | msg = htmlentities(msg, "ENT_NOQUOTES", "UTF-8"); |
984 | msg = htmlentities(msg, "ENT_NOQUOTES", "UTF-8"); |
978 | return "<span class=\"encrypted\">" + msg + "</span>"; |
985 | return "<span class=\"encrypted\">" + msg + "</span>"; |
979 | } else { |
986 | } else { |
980 | // TODO: maybe we can still make the string invisible or something, so it becomes more user comfortable? |
987 | // TODO: maybe we can still make the string invisible or something, so it becomes more user comfortable? |
981 | // TODO: maybe we can still save the decode-information so we can revert decryption as soon as the password is changed again? |
988 | // TODO: maybe we can still save the decode-information so we can revert decryption as soon as the password is changed again? |
982 | return str; // can be encrypted later |
989 | return str; // can be encrypted later |
983 | } |
990 | } |
984 | } |
991 | } |
985 | 992 | ||
986 | function cbUsername(str, pUsername, offset, s) { |
993 | function cbUsername(str, pUsername, offset, s) { |
987 | var color = colorizeUsername(pUsername); |
994 | var color = colorizeUsername(pUsername); |
988 | 995 | ||
989 | return "<font color=\"" + color + "\"><b>[" + pUsername + "]</b></font>: "; |
996 | return "<font color=\"" + color + "\"><b>[" + pUsername + "]</b></font>: "; |
990 | } |
997 | } |
991 | 998 | ||
992 | function cbDate(str, pDateTime, offset, s) { |
999 | function cbDate(str, pDateTime, offset, s) { |
993 | return pDateTime + " - "; |
1000 | return pDateTime + " - "; |
994 | } |
1001 | } |
995 | 1002 | ||
996 | var formatAndDecryptIsRunning = false; // to prevent that two instances of formatAndDecrypt() run simultaneously |
1003 | var formatAndDecryptIsRunning = false; // to prevent that two instances of formatAndDecrypt() run simultaneously |
997 | function formatAndDecrypt() { |
1004 | function formatAndDecrypt() { |
998 | // Set the mutex and write a "Decrypting..." message |
1005 | // Set the mutex and write a "Decrypting..." message |
999 | //while (formatAndDecryptIsRunning); |
1006 | //while (formatAndDecryptIsRunning); |
1000 | if (formatAndDecryptIsRunning) return; // If there is already a decrypt process, we will exit instead of waiting (because we don't need 2 subsequent decryptions) |
1007 | if (formatAndDecryptIsRunning) return; // If there is already a decrypt process, we will exit instead of waiting (because we don't need 2 subsequent decryptions) |
1001 | formatAndDecryptIsRunning = true; |
1008 | formatAndDecryptIsRunning = true; |
1002 | changeStatusLabel(); // Add "Decrypting..." message |
1009 | changeStatusLabel(); // Add "Decrypting..." message |
1003 | try { |
1010 | try { |
1004 | var message = document.getElementById("chatmessages").innerHTML; |
1011 | var message = document.getElementById("chatmessages").innerHTML; |
1005 | 1012 | ||
1006 | // TODO: man darf kein ")" im benutzernamen haben!!!! |
1013 | // TODO: man darf kein ")" im benutzernamen haben!!!! |
1007 | 1014 | ||
1008 | // first decode |
1015 | // first decode |
1009 | message = message.replace(/\(dec_(.*?)_(.+?)_ver(.+?): (.+?)\)/g, cbDecrypt); |
1016 | message = message.replace(/\(dec_(.*?)_(.+?)_ver(.+?): (.+?)\)/g, cbDecrypt); |
1010 | 1017 | ||
1011 | // then do formating |
1018 | // then do formating |
1012 | message = message.replace(/\(date: (.+?)\)/g, cbDate); |
1019 | message = message.replace(/\(date: (.+?)\)/g, cbDate); |
1013 | message = message.replace(/\(user: (.+?)\)/g, cbUsername); |
1020 | message = message.replace(/\(user: (.+?)\)/g, cbUsername); |
1014 | 1021 | ||
1015 | // make links clickable |
1022 | // make links clickable |
1016 | message = replaceURLWithHTMLLinks(message); |
1023 | message = replaceURLWithHTMLLinks(message); |
1017 | 1024 | ||
1018 | // Only refresh if something has been changed. otherwise we could not copy-paste when it is permanently refreshing |
1025 | // Only refresh if something has been changed. otherwise we could not copy-paste when it is permanently refreshing |
1019 | if (document.getElementById("chatmessages").innerHTML != message) { |
1026 | if (document.getElementById("chatmessages").innerHTML != message) { |
1020 | document.getElementById("chatmessages").innerHTML = message; |
1027 | document.getElementById("chatmessages").innerHTML = message; |
1021 | } |
1028 | } |
1022 | } finally { |
1029 | } finally { |
1023 | // Release the mutex and remove the "Decrypting..." message |
1030 | // Release the mutex and remove the "Decrypting..." message |
1024 | formatAndDecryptIsRunning = false; |
1031 | formatAndDecryptIsRunning = false; |
1025 | changeStatusLabel(); // Remove "Decrypting..." message |
1032 | changeStatusLabel(); // Remove "Decrypting..." message |
1026 | } |
1033 | } |
1027 | } |
1034 | } |
1028 | 1035 | ||
1029 | // --- Status label function |
1036 | // --- Status label function |
1030 | 1037 | ||
1031 | function alertUser() { |
1038 | function alertUser() { |
1032 | // TODO: play sound? |
1039 | // TODO: play sound? |
1033 | } |
1040 | } |
1034 | 1041 | ||
1035 | function changeStatusLabel() { |
1042 | function changeStatusLabel() { |
1036 | var status = ""; |
1043 | var status = ""; |
1037 | 1044 | ||
1038 | if (addMessagePendingQueue.length > 0) { |
1045 | if (addMessagePendingQueue.length > 0) { |
1039 | status = "Sending (" + addMessagePendingQueue.length + ")..."; |
1046 | status = "Sending (" + addMessagePendingQueue.length + ")..."; |
1040 | } else if (formatAndDecryptIsRunning) { |
1047 | } else if (formatAndDecryptIsRunning) { |
1041 | status = "Decrypting..."; |
1048 | status = "Decrypting..."; |
1042 | } else if (refreshFailedTimerSet) { |
1049 | } else if (refreshFailedTimerSet) { |
1043 | status = "Checking..."; |
1050 | status = "Checking..."; |
1044 | } else { |
1051 | } else { |
1045 | status = curseq + " posts"; |
1052 | status = curseq + " posts"; |
1046 | } |
1053 | } |
1047 | 1054 | ||
1048 | document.getElementById("status").innerHTML = status; |
1055 | document.getElementById("status").innerHTML = status; |
1049 | 1056 | ||
1050 | var title = ""; // document.title; |
1057 | var title = ""; // document.title; |
1051 | 1058 | ||
1052 | // If tab is not in focus, we might want to alert the user when new posts arrived |
1059 | // If tab is not in focus, we might want to alert the user when new posts arrived |
1053 | if ((curseq > 0) && (!curVisible) && (backgroundSeqCount != curseq)) { |
1060 | if ((curseq > 0) && (!curVisible) && (backgroundSeqCount != curseq)) { |
1054 | blink = 1 - blink; |
1061 | blink = 1 - blink; |
1055 | title = "(" + (curseq-backgroundSeqCount) + ") " + htmlentities(room); |
1062 | title = "(" + (curseq-backgroundSeqCount) + ") " + htmlentities(room); |
1056 | if (blink == 1) { |
1063 | if (blink == 1) { |
1057 | title = title.toUpperCase(); |
1064 | title = title.toUpperCase(); |
1058 | } else { |
1065 | } else { |
1059 | title = title.toLowerCase(); |
1066 | title = title.toLowerCase(); |
1060 | } |
1067 | } |
1061 | } else { |
1068 | } else { |
1062 | title = htmlentities(room); |
1069 | title = htmlentities(room); |
1063 | } |
1070 | } |
1064 | title += " - " + htmlentities(product); |
1071 | title += " - " + htmlentities(product); |
1065 | 1072 | ||
1066 | if (document.title != title) document.title = title; |
1073 | if (document.title != title) document.title = title; |
1067 | } |
1074 | } |
1068 | 1075 | ||
1069 | // --- Refresh |
1076 | // --- Refresh |
1070 | 1077 | ||
1071 | var refreshTimer = null; |
1078 | var refreshTimer = null; |
1072 | var refreshTimerSet = false; |
1079 | var refreshTimerSet = false; |
1073 | var refreshFailedTimer = null; |
1080 | var refreshFailedTimer = null; |
1074 | var refreshFailedTimerSet = false; |
1081 | var refreshFailedTimerSet = false; |
1075 | 1082 | ||
1076 | function refresh_failed(id) { |
1083 | function refresh_failed(id) { |
1077 | sajax_cancel(id); |
1084 | sajax_cancel(id); |
1078 | 1085 | ||
1079 | // var refreshFailedTimerSet = (typeof refreshFailedTimer !== 'undefined'); |
1086 | // var refreshFailedTimerSet = (typeof refreshFailedTimer !== 'undefined'); |
1080 | // var refreshFailedTimerSet = (refreshFailedTimer !== null); |
1087 | // var refreshFailedTimerSet = (refreshFailedTimer !== null); |
1081 | if (refreshFailedTimerSet) { |
1088 | if (refreshFailedTimerSet) { |
1082 | clearTimeout(refreshFailedTimer); |
1089 | clearTimeout(refreshFailedTimer); |
1083 | refreshFailedTimer = null; |
1090 | refreshFailedTimer = null; |
1084 | refreshFailedTimerSet = false; |
1091 | refreshFailedTimerSet = false; |
1085 | } |
1092 | } |
1086 | 1093 | ||
1087 | // Remove "Checking..." message |
1094 | // Remove "Checking..." message |
1088 | // changeStatusLabel(); |
1095 | // changeStatusLabel(); |
1089 | 1096 | ||
1090 | // TODO: die meldung kommt viel zu oft, auch nachdem alles wieder geht. warum? |
1097 | // TODO: die meldung kommt viel zu oft, auch nachdem alles wieder geht. warum? |
1091 | // alert("Refresh failed. Will try again."); |
1098 | // alert("Refresh failed. Will try again."); |
1092 | refresh(); |
1099 | refresh(); |
1093 | } |
1100 | } |
1094 | 1101 | ||
1095 | function refresh_cb(data) { |
1102 | function refresh_cb(data) { |
1096 | // var refreshFailedTimerSet = (typeof refreshFailedTimer !== 'undefined'); |
1103 | // var refreshFailedTimerSet = (typeof refreshFailedTimer !== 'undefined'); |
1097 | // var refreshFailedTimerSet = (refreshFailedTimer !== null); |
1104 | // var refreshFailedTimerSet = (refreshFailedTimer !== null); |
1098 | if (refreshFailedTimerSet) { |
1105 | if (refreshFailedTimerSet) { |
1099 | clearTimeout(refreshFailedTimer); |
1106 | clearTimeout(refreshFailedTimer); |
1100 | refreshFailedTimer = null; |
1107 | refreshFailedTimer = null; |
1101 | refreshFailedTimerSet = false; |
1108 | refreshFailedTimerSet = false; |
1102 | } |
1109 | } |
1103 | 1110 | ||
1104 | var newcurseq = data[0]; |
1111 | var newcurseq = data[0]; |
1105 | var new_data = data[1]; |
1112 | var new_data = data[1]; |
1106 | var userstats = data[2]; |
1113 | var userstats = data[2]; |
1107 | 1114 | ||
1108 | // User list |
1115 | // User list |
1109 | document.getElementById("activeusers").innerHTML = '<b>ACTIVE USERS:</b><br><br>'; |
1116 | document.getElementById("activeusers").innerHTML = '<b>ACTIVE USERS:</b><br><br>'; |
1110 | for (var i = 0; i < userstats.length; ++i ) { |
1117 | for (var i = 0; i < userstats.length; ++i ) { |
1111 | var username = userstats[i]['USERNAME']; |
1118 | var username = userstats[i]['USERNAME']; |
1112 | var color = colorizeUsername(username); |
1119 | var color = colorizeUsername(username); |
1113 | var postfix = (userstats[i]['UNIQUEID'] == uniqueID) ? ' (you)' : ''; |
1120 | var postfix = (userstats[i]['UNIQUEID'] == uniqueID) ? ' (you)' : ''; |
1114 | document.getElementById("activeusers").innerHTML += '<font color="'+color+'"><b>'+htmlentities(username)+'</b></font>'+postfix+'<br>'; |
1121 | document.getElementById("activeusers").innerHTML += '<font color="'+color+'"><b>'+htmlentities(username)+'</b></font>'+postfix+'<br>'; |
1115 | } |
1122 | } |
1116 | 1123 | ||
1117 | var timeout = <?php echo TIMER_1; ?>; // default timeout |
1124 | var timeout = <?php echo TIMER_1; ?>; // default timeout |
1118 | if (newcurseq != curseq) { |
1125 | if (newcurseq != curseq) { |
1119 | curseq = newcurseq; |
1126 | curseq = newcurseq; |
1120 | 1127 | ||
1121 | document.getElementById("chatmessages").innerHTML += new_data; |
1128 | document.getElementById("chatmessages").innerHTML += new_data; |
1122 | 1129 | ||
1123 | formatAndDecrypt(); |
1130 | formatAndDecrypt(); |
1124 | 1131 | ||
1125 | if (autoscrollEnabled()) { |
1132 | if (autoscrollEnabled()) { |
1126 | scrollToBottom(); |
1133 | scrollToBottom(); |
1127 | } |
1134 | } |
1128 | 1135 | ||
1129 | if (!curVisible) { |
1136 | if (!curVisible) { |
1130 | // blink = 0; |
1137 | // blink = 0; |
1131 | alertUser(); |
1138 | alertUser(); |
1132 | } |
1139 | } |
1133 | 1140 | ||
1134 | timeout = <?php echo TIMER_2; ?>; // shorter timeout when a new message has just arrived |
1141 | timeout = <?php echo TIMER_2; ?>; // shorter timeout when a new message has just arrived |
1135 | } |
1142 | } |
1136 | 1143 | ||
1137 | // Remove "Checking..." message |
1144 | // Remove "Checking..." message |
1138 | changeStatusLabel(); |
1145 | changeStatusLabel(); |
1139 | 1146 | ||
1140 | if (!refreshTimerSet) { |
1147 | if (!refreshTimerSet) { |
1141 | refreshTimer = setTimeout("refresh()", timeout); |
1148 | refreshTimer = setTimeout("refresh()", timeout); |
1142 | refreshTimerSet = true; |
1149 | refreshTimerSet = true; |
1143 | } |
1150 | } |
1144 | } |
1151 | } |
1145 | 1152 | ||
1146 | function refresh() { |
1153 | function refresh() { |
1147 | // Only run the timer once (since we also call refresh() when a new post is added) |
1154 | // Only run the timer once (since we also call refresh() when a new post is added) |
1148 | // var refreshTimerSet = (typeof refreshTimer !== 'undefined'); |
1155 | // var refreshTimerSet = (typeof refreshTimer !== 'undefined'); |
1149 | // var refreshTimerSet = (refreshTimer !== null); |
1156 | // var refreshTimerSet = (refreshTimer !== null); |
1150 | if (refreshTimerSet) { |
1157 | if (refreshTimerSet) { |
1151 | clearTimeout(refreshTimer); |
1158 | clearTimeout(refreshTimer); |
1152 | refreshTimer = null; |
1159 | refreshTimer = null; |
1153 | refreshTimerSet = false; |
1160 | refreshTimerSet = false; |
1154 | } |
1161 | } |
1155 | 1162 | ||
1156 | var sajax_id = x_refresh(room, curseq, user, uniqueID, refresh_cb); |
1163 | var sajax_id = x_refresh(room, curseq, user, uniqueID, refresh_cb); |
1157 | 1164 | ||
1158 | // TODO: gibt es eine bessere möglichkeit festzustellen, ob der request gestorben ist? ein negativer callback z.B.? |
1165 | // TODO: gibt es eine bessere möglichkeit festzustellen, ob der request gestorben ist? ein negativer callback z.B.? |
1159 | if (!refreshFailedTimerSet) { |
1166 | if (!refreshFailedTimerSet) { |
1160 | refreshFailedTimer = setTimeout(function() { refresh_failed(sajax_id); }, <?php echo TIMER_DEAD; ?>); |
1167 | refreshFailedTimer = setTimeout(function() { refresh_failed(sajax_id); }, <?php echo TIMER_DEAD; ?>); |
1161 | refreshFailedTimerSet = true; |
1168 | refreshFailedTimerSet = true; |
1162 | } |
1169 | } |
1163 | 1170 | ||
1164 | // Add "Checking..." message |
1171 | // Add "Checking..." message |
1165 | changeStatusLabel(); |
1172 | changeStatusLabel(); |
1166 | } |
1173 | } |
1167 | 1174 | ||
1168 | // --- Add |
1175 | // --- Add |
1169 | 1176 | ||
1170 | var addMessagePendingQueue = []; |
1177 | var addMessagePendingQueue = []; |
1171 | 1178 | ||
1172 | function do_encrypt(msg) { // NG: "Encrypting" status message |
1179 | function do_encrypt(msg) { // NG: "Encrypting" status message |
1173 | var msgToServer = ""; |
1180 | var msgToServer = ""; |
1174 | 1181 | ||
1175 | var lines = msg.split("\n"); |
1182 | var lines = msg.split("\n"); |
1176 | for (var i = 0; i < lines.length; ++i ) { |
1183 | for (var i = 0; i < lines.length; ++i ) { |
1177 | var line = lines[i]; |
1184 | var line = lines[i]; |
1178 | 1185 | ||
1179 | // encrypt message |
1186 | // encrypt message |
1180 | if (encryptionEnabled()) { |
1187 | if (encryptionEnabled()) { |
1181 | var version = <?php echo CFG_CIPHERSUITE; ?>; |
1188 | var version = <?php echo CFG_CIPHERSUITE; ?>; |
1182 | var passwd = getPassword(); |
1189 | var passwd = getPassword(); |
1183 | var entrySalt = randomString(20); |
1190 | var entrySalt = randomString(20); |
1184 | var hash = specialHash(passwd, entrySalt, version); |
1191 | var hash = specialHash(passwd, entrySalt, version); |
1185 | line = "(dec_" + hash + ": " + aes_enc(line, version) + ")"; |
1192 | line = "(dec_" + hash + ": " + aes_enc(line, version) + ")"; |
1186 | } |
1193 | } |
1187 | 1194 | ||
1188 | msgToServer += line + "\n"; |
1195 | msgToServer += line + "\n"; |
1189 | } |
1196 | } |
1190 | 1197 | ||
1191 | return msgToServer.trim(); |
1198 | return msgToServer.trim(); |
1192 | } |
1199 | } |
1193 | 1200 | ||
1194 | function add_get_index_of(token) { |
1201 | function add_get_index_of(token) { |
1195 | for (var i = 0; i < addMessagePendingQueue.length; ++i ) { |
1202 | for (var i = 0; i < addMessagePendingQueue.length; ++i ) { |
1196 | if (addMessagePendingQueue[i].token == token) return i; |
1203 | if (addMessagePendingQueue[i].token == token) return i; |
1197 | } |
1204 | } |
1198 | return -1; |
1205 | return -1; |
1199 | } |
1206 | } |
1200 | 1207 | ||
1201 | function add_failed(token) { |
1208 | function add_failed(token) { |
1202 | var i = add_get_index_of(token); |
1209 | var i = add_get_index_of(token); |
1203 | if (i == -1) return; |
1210 | if (i == -1) return; |
1204 | 1211 | ||
1205 | sajax_cancel(addMessagePendingQueue[i].sajax_id); |
1212 | sajax_cancel(addMessagePendingQueue[i].sajax_id); |
1206 | 1213 | ||
1207 | var failedMsg = addMessagePendingQueue[i].message; |
1214 | var failedMsg = addMessagePendingQueue[i].message; |
1208 | addMessagePendingQueue.remove(i); |
1215 | addMessagePendingQueue.remove(i); |
1209 | changeStatusLabel(); // Remove "Sending..." message |
1216 | changeStatusLabel(); // Remove "Sending..." message |
1210 | 1217 | ||
1211 | // Nachricht ausgeben. |
1218 | // Nachricht ausgeben. |
1212 | // QUE TODO: automatisch neu versuchen? |
1219 | // QUE TODO: automatisch neu versuchen? |
1213 | // TODO: reihenfolge der nachrichten stimmt nicht. |
1220 | // TODO: reihenfolge der nachrichten stimmt nicht. |
1214 | alert("Following message could not be sent. Please try again:" + failedMsg); |
1221 | alert("Following message could not be sent. Please try again:" + failedMsg); |
1215 | 1222 | ||
1216 | // Add message back to the input box, so the user can send it again |
1223 | // Add message back to the input box, so the user can send it again |
1217 | if (document.getElementById("line").value.trim() == initMsg.trim()) { |
1224 | if (document.getElementById("line").value.trim() == initMsg.trim()) { |
1218 | // If login message fails, then clear box first before append |
1225 | // If login message fails, then clear box first before append |
1219 | document.getElementById("line").value = ""; |
1226 | document.getElementById("line").value = ""; |
1220 | } |
1227 | } |
1221 | var newMsgCont = document.getElementById("line").value.trim() + "\n" + failedMsg.trim(); |
1228 | var newMsgCont = document.getElementById("line").value.trim() + "\n" + failedMsg.trim(); |
1222 | document.getElementById("line").value = newMsgCont.trim(); |
1229 | document.getElementById("line").value = newMsgCont.trim(); |
1223 | } |
1230 | } |
1224 | 1231 | ||
1225 | function add_cb(data) { |
1232 | function add_cb(data) { |
1226 | var token = data[0]; |
1233 | var token = data[0]; |
1227 | 1234 | ||
1228 | var i = add_get_index_of(token); |
1235 | var i = add_get_index_of(token); |
1229 | if (i == -1) return; |
1236 | if (i == -1) return; |
1230 | 1237 | ||
1231 | clearTimeout(addMessagePendingQueue[i].failTimer); |
1238 | clearTimeout(addMessagePendingQueue[i].failTimer); |
1232 | addMessagePendingQueue.remove(i); |
1239 | addMessagePendingQueue.remove(i); |
1233 | changeStatusLabel(); // Remove "Sending..." message |
1240 | changeStatusLabel(); // Remove "Sending..." message |
1234 | 1241 | ||
1235 | // Refresh now, so that the new post quickly appears! |
1242 | // Refresh now, so that the new post quickly appears! |
1236 | refresh(); |
1243 | refresh(); |
1237 | } |
1244 | } |
1238 | 1245 | ||
1239 | var addIsRunning = false; // to prevent that two instances of add() run simultaneously |
1246 | var addIsRunning = false; // to prevent that two instances of add() run simultaneously |
1240 | function add() { |
1247 | function add() { |
1241 | // Set the mutex |
1248 | // Set the mutex |
1242 | while (addIsRunning); |
1249 | while (addIsRunning); |
1243 | addIsRunning = true; |
1250 | addIsRunning = true; |
1244 | try { |
1251 | try { |
1245 | var msg = document.getElementById("line").value.trim(); |
1252 | var msg = document.getElementById("line").value.trim(); |
1246 | if ((msg == "") || (msg == initMsg.trim())) { |
1253 | if ((msg == "") || (msg == initMsg.trim())) { |
1247 | return; |
1254 | return; |
1248 | } else { |
1255 | } else { |
1249 | document.getElementById("line").value = ""; |
1256 | document.getElementById("line").value = ""; |
1250 | } |
1257 | } |
1251 | 1258 | ||
1252 | send_msg(msg, 1); |
1259 | send_msg(msg, 1); |
1253 | 1260 | ||
1254 | sleep(100); // damit es zu keiner racecondition (= posts in falscher reihenfolge) kommen kann |
1261 | sleep(100); // damit es zu keiner racecondition (= posts in falscher reihenfolge) kommen kann |
1255 | } finally { |
1262 | } finally { |
1256 | // Release the mutex |
1263 | // Release the mutex |
1257 | addIsRunning = false; |
1264 | addIsRunning = false; |
1258 | } |
1265 | } |
1259 | } |
1266 | } |
1260 | 1267 | ||
1261 | function send_msg(msg, encrypt) { |
1268 | function send_msg(msg, encrypt) { |
1262 | var msgToServer = msg; |
1269 | var msgToServer = msg; |
1263 | 1270 | ||
1264 | if (encrypt) { |
1271 | if (encrypt) { |
1265 | msgToServer = do_encrypt(msgToServer); |
1272 | msgToServer = do_encrypt(msgToServer); |
1266 | } |
1273 | } |
1267 | 1274 | ||
1268 | // We need a token which is sent back by the server, so our succeed-callback can find and remove the entry from the queue. |
1275 | // We need a token which is sent back by the server, so our succeed-callback can find and remove the entry from the queue. |
1269 | // We cannot use the sajax_id generated by SAJAX, because we need to send it to the server. |
1276 | // We cannot use the sajax_id generated by SAJAX, because we need to send it to the server. |
1270 | var token = getToken(); |
1277 | var token = getToken(); |
1271 | var sajax_id = x_add_line(token, room, user, msgToServer, add_cb); |
1278 | var sajax_id = x_add_line(token, room, user, msgToServer, add_cb); |
1272 | 1279 | ||
1273 | // Backup the text, so we can back it up if it fails |
1280 | // Backup the text, so we can back it up if it fails |
1274 | // TODO: gibt es eine bessere möglichkeit festzustellen, ob der request gestorben ist? ein negativer callback z.B.? |
1281 | // TODO: gibt es eine bessere möglichkeit festzustellen, ob der request gestorben ist? ein negativer callback z.B.? |
1275 | var t = setTimeout(function() { add_failed(token); }, <?php echo TIMER_DEAD; ?>); |
1282 | var t = setTimeout(function() { add_failed(token); }, <?php echo TIMER_DEAD; ?>); |
1276 | addMessagePendingQueue.push({ |
1283 | addMessagePendingQueue.push({ |
1277 | "token": token, |
1284 | "token": token, |
1278 | "sajax_id": sajax_id, |
1285 | "sajax_id": sajax_id, |
1279 | "message": msg, |
1286 | "message": msg, |
1280 | "failTimer": t |
1287 | "failTimer": t |
1281 | } ); |
1288 | } ); |
1282 | changeStatusLabel(); // Add "Sending..." message |
1289 | changeStatusLabel(); // Add "Sending..." message |
1283 | 1290 | ||
1284 | return sajax_id; |
1291 | return sajax_id; |
1285 | } |
1292 | } |
1286 | 1293 | ||
1287 | // --- Logoff |
1294 | // --- Logoff |
1288 | 1295 | ||
1289 | var logoffEventFired = 0; |
1296 | var logoffEventFired = 0; |
1290 | function send_logoff() { |
1297 | function send_logoff() { |
1291 | if (logoffEventFired == 1) return; |
1298 | if (logoffEventFired == 1) return; |
1292 | logoffEventFired = 1; |
1299 | logoffEventFired = 1; |
1293 | 1300 | ||
1294 | send_msg("(Logging off)", 0); |
1301 | send_msg("(Logging off)", 0); |
1295 | 1302 | ||
1296 | document.getElementById("pwd").value = ""; |
1303 | document.getElementById("pwd").value = ""; |
1297 | 1304 | ||
1298 | // TODO: go back to login form |
1305 | // TODO: go back to login form |
1299 | } |
1306 | } |
1300 | 1307 | ||
1301 | var logonEventFired = 0; |
1308 | var logonEventFired = 0; |
1302 | function send_logon() { |
1309 | function send_logon() { |
1303 | if (logonEventFired == 1) return; |
1310 | if (logonEventFired == 1) return; |
1304 | logonEventFired = 1; |
1311 | logonEventFired = 1; |
1305 | 1312 | ||
1306 | send_msg("(Logging in)", 0); |
1313 | send_msg("(Logging in)", 0); |
1307 | } |
1314 | } |
1308 | 1315 | ||
1309 | // --- Initialization stuff |
1316 | // --- Initialization stuff |
1310 | 1317 | ||
1311 | var initMsg = "(enter your message here)"; /* const */ |
1318 | var initMsg = "(enter your message here)"; /* const */ |
1312 | 1319 | ||
1313 | function initShowHideHandlers() { |
1320 | function initShowHideHandlers() { |
1314 | // https://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active |
1321 | // https://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active |
1315 | 1322 | ||
1316 | var hidden = "hidden"; |
1323 | var hidden = "hidden"; |
1317 | 1324 | ||
1318 | // Standards: |
1325 | // Standards: |
1319 | if (hidden in document) |
1326 | if (hidden in document) |
1320 | document.addEventListener("visibilitychange", onchange); |
1327 | document.addEventListener("visibilitychange", onchange); |
1321 | else if ((hidden = "mozHidden") in document) |
1328 | else if ((hidden = "mozHidden") in document) |
1322 | document.addEventListener("mozvisibilitychange", onchange); |
1329 | document.addEventListener("mozvisibilitychange", onchange); |
1323 | else if ((hidden = "webkitHidden") in document) |
1330 | else if ((hidden = "webkitHidden") in document) |
1324 | document.addEventListener("webkitvisibilitychange", onchange); |
1331 | document.addEventListener("webkitvisibilitychange", onchange); |
1325 | else if ((hidden = "msHidden") in document) |
1332 | else if ((hidden = "msHidden") in document) |
1326 | document.addEventListener("msvisibilitychange", onchange); |
1333 | document.addEventListener("msvisibilitychange", onchange); |
1327 | // IE 9 and lower: |
1334 | // IE 9 and lower: |
1328 | else if ('onfocusin' in document) |
1335 | else if ('onfocusin' in document) |
1329 | document.onfocusin = document.onfocusout = onchange; |
1336 | document.onfocusin = document.onfocusout = onchange; |
1330 | // All others: |
1337 | // All others: |
1331 | else |
1338 | else |
1332 | window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange; |
1339 | window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange; |
1333 | 1340 | ||
1334 | function onchange(evt) { |
1341 | function onchange(evt) { |
1335 | var v = true, h = false, |
1342 | var v = true, h = false, |
1336 | evtMap = { |
1343 | evtMap = { |
1337 | focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h |
1344 | focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h |
1338 | }; |
1345 | }; |
1339 | 1346 | ||
1340 | evt = evt || window.event; |
1347 | evt = evt || window.event; |
1341 | var res; |
1348 | var res; |
1342 | if (evt.type in evtMap) { |
1349 | if (evt.type in evtMap) { |
1343 | res = evtMap[evt.type]; |
1350 | res = evtMap[evt.type]; |
1344 | } else { |
1351 | } else { |
1345 | res = !this[hidden]; |
1352 | res = !this[hidden]; |
1346 | } |
1353 | } |
1347 | 1354 | ||
1348 | if (res) { |
1355 | if (res) { |
1349 | onChatShow(); |
1356 | onChatShow(); |
1350 | } else { |
1357 | } else { |
1351 | onChatHide(); |
1358 | onChatHide(); |
1352 | } |
1359 | } |
1353 | } |
1360 | } |
1354 | } |
1361 | } |
1355 | 1362 | ||
1356 | function initReturnKeyHandler() { |
1363 | function initReturnKeyHandler() { |
1357 | var wage = document.getElementById("line"); |
1364 | var wage = document.getElementById("line"); |
1358 | wage.addEventListener("keydown", function (e) { |
1365 | wage.addEventListener("keydown", function (e) { |
1359 | if (e.which == 13 || e.keyCode === 13) { //checks whether the pressed key is "Enter" |
1366 | if (e.which == 13 || e.keyCode === 13) { //checks whether the pressed key is "Enter" |
1360 | add(); |
1367 | add(); |
1361 | } |
1368 | } |
1362 | }); |
1369 | }); |
1363 | // important for <textarea> otherwise we'll have a #13 in the box after it |
1370 | // important for <textarea> otherwise we'll have a #13 in the box after it |
1364 | wage.addEventListener("keyup", function (e) { |
1371 | wage.addEventListener("keyup", function (e) { |
1365 | if (e.which == 13 || e.keyCode === 13) { //checks whether the pressed key is "Enter" |
1372 | if (e.which == 13 || e.keyCode === 13) { //checks whether the pressed key is "Enter" |
1366 | document.getElementById("line").value = ""; |
1373 | document.getElementById("line").value = ""; |
1367 | } |
1374 | } |
1368 | }); |
1375 | }); |
1369 | } |
1376 | } |
1370 | 1377 | ||
1371 | function initUnloadHandler() { |
1378 | function initUnloadHandler() { |
1372 | // TODO: does not work when following a link or when clicking "back button" |
1379 | // TODO: does not work when following a link or when clicking "back button" |
1373 | window.onbeforeunload = function() { |
1380 | window.onbeforeunload = function() { |
1374 | send_logoff(); |
1381 | send_logoff(); |
1375 | } |
1382 | } |
1376 | window.addEventListener("beforeunload", function(e) { |
1383 | window.addEventListener("beforeunload", function(e) { |
1377 | send_logoff(); |
1384 | send_logoff(); |
1378 | }, false); |
1385 | }, false); |
1379 | } |
1386 | } |
1380 | 1387 | ||
1381 | // Delayed (single run) onkeydown event for the password field |
1388 | // Delayed (single run) onkeydown event for the password field |
1382 | var delayedKeyChangeTimer = null; |
1389 | var delayedKeyChangeTimer = null; |
1383 | var delayedKeyChangeTimerSet = false; |
1390 | var delayedKeyChangeTimerSet = false; |
1384 | function initKeyChangeHandler() { |
1391 | function initKeyChangeHandler() { |
1385 | // We use onkeydown instead of onkeypress, otherwise "del" or "ctrl+v" won't work on Chrome |
1392 | // We use onkeydown instead of onkeypress, otherwise "del" or "ctrl+v" won't work on Chrome |
1386 | // See http://help.dottoro.com/ljlwfxum.php |
1393 | // See http://help.dottoro.com/ljlwfxum.php |
1387 | 1394 | ||
1388 | document.getElementById("pwd").onkeydown = function(evt) { |
1395 | document.getElementById("pwd").onkeydown = function(evt) { |
1389 | var enterPressed = (evt.which == 13 || evt.keyCode === 13); |
1396 | var enterPressed = (evt.which == 13 || evt.keyCode === 13); |
1390 | 1397 | ||
1391 | // var delayedKeyChangeTimerSet = (typeof delayedKeyChangeTimer !== 'undefined'); |
1398 | // var delayedKeyChangeTimerSet = (typeof delayedKeyChangeTimer !== 'undefined'); |
1392 | // var delayedKeyChangeTimerSet = (delayedKeyChangeTimer !== null); |
1399 | // var delayedKeyChangeTimerSet = (delayedKeyChangeTimer !== null); |
1393 | if (delayedKeyChangeTimerSet) { |
1400 | if (delayedKeyChangeTimerSet) { |
1394 | clearTimeout(delayedKeyChangeTimer); |
1401 | clearTimeout(delayedKeyChangeTimer); |
1395 | delayedKeyChangeTimer = null; |
1402 | delayedKeyChangeTimer = null; |
1396 | delayedKeyChangeTimerSet = false; |
1403 | delayedKeyChangeTimerSet = false; |
1397 | } |
1404 | } |
1398 | 1405 | ||
1399 | var timeout = enterPressed ? 0 : <?php echo TIMER_KEYCHANGE; ?>; |
1406 | var timeout = enterPressed ? 0 : <?php echo TIMER_KEYCHANGE; ?>; |
1400 | if (!delayedKeyChangeTimerSet) { |
1407 | if (!delayedKeyChangeTimerSet) { |
1401 | delayedKeyChangeTimer = setTimeout("formatAndDecrypt()", <?php echo TIMER_KEYCHANGE; ?>); |
1408 | delayedKeyChangeTimer = setTimeout("formatAndDecrypt()", <?php echo TIMER_KEYCHANGE; ?>); |
1402 | delayedKeyChangeTimerSet = true; |
1409 | delayedKeyChangeTimerSet = true; |
1403 | } |
1410 | } |
1404 | }; |
1411 | }; |
1405 | } |
1412 | } |
1406 | 1413 | ||
1407 | function initPage() { |
1414 | function initPage() { |
1408 | uniqueID=randomString(20); |
1415 | uniqueID=randomString(20); |
1409 | // document.getElementById("pwd").value = ""; |
1416 | // document.getElementById("pwd").value = ""; |
1410 | document.getElementById("line").value = initMsg; |
1417 | document.getElementById("line").value = initMsg; |
1411 | changeStatusLabel(); |
1418 | changeStatusLabel(); |
1412 | initUnloadHandler(); |
1419 | initUnloadHandler(); |
1413 | initReturnKeyHandler(); |
1420 | initReturnKeyHandler(); |
1414 | initShowHideHandlers(); |
1421 | initShowHideHandlers(); |
1415 | initKeyChangeHandler(); |
1422 | initKeyChangeHandler(); |
1416 | refresh(); |
1423 | refresh(); |
1417 | // We do it at last, otherwise we will have the problem that the chat will not be scrolled to the very bottom in the beginning |
1424 | // We do it at last, otherwise we will have the problem that the chat will not be scrolled to the very bottom in the beginning |
1418 | send_logon(); |
1425 | send_logon(); |
1419 | } |
1426 | } |
1420 | 1427 | ||
1421 | // --- Misc event handlers |
1428 | // --- Misc event handlers |
1422 | 1429 | ||
1423 | function onChatShow() { |
1430 | function onChatShow() { |
1424 | curVisible = true; |
1431 | curVisible = true; |
1425 | changeStatusLabel(); |
1432 | changeStatusLabel(); |
1426 | } |
1433 | } |
1427 | 1434 | ||
1428 | function onChatHide() { |
1435 | function onChatHide() { |
1429 | curVisible = false; |
1436 | curVisible = false; |
1430 | blink = 1; |
1437 | blink = 1; |
1431 | backgroundSeqCount = curseq; |
1438 | backgroundSeqCount = curseq; |
1432 | // changeStatusLabel(); |
1439 | // changeStatusLabel(); |
1433 | } |
1440 | } |
1434 | 1441 | ||
1435 | function onFocusChatField() { |
1442 | function onFocusChatField() { |
1436 | if (document.getElementById("line").value == initMsg) { |
1443 | if (document.getElementById("line").value == initMsg) { |
1437 | document.getElementById("line").value = ""; |
1444 | document.getElementById("line").value = ""; |
1438 | } |
1445 | } |
1439 | } |
1446 | } |
1440 | 1447 | ||
1441 | </script> |
1448 | </script> |
1442 | 1449 | ||
1443 | </head> |
1450 | </head> |
1444 | 1451 | ||
1445 | <body class="chatroom" onload="initPage();"> |
1452 | <body class="chatroom" onload="initPage();"> |
1446 | 1453 | ||
1447 | <div id="header"> |
1454 | <div id="header"> |
1448 | <font size="+2"><font color="orange"><?php echo htmlentities(PRODUCT_NAME); ?> <font size="-1"><?php |
1455 | <font size="+2"><font color="orange"><?php echo htmlentities(PRODUCT_NAME); ?> <font size="-1"><?php |
1449 | echo htmlentities(MCC_VER); |
1456 | echo htmlentities(MCC_VER); |
1450 | ?></font></font> <span class="userid">[<?php |
1457 | ?></font></font> <span class="userid">[<?php |
1451 | echo htmlentities("$ses_user@$ses_room"); |
1458 | echo htmlentities("$ses_user@$ses_room"); |
1452 | ?>]</span></font><br> |
1459 | ?>]</span></font><br> |
1453 | <span class="pwdenter">Password for client-side encryption of messages: <input type="password" name="pwd" id="pwd" value=""></span> |
1460 | <span class="pwdenter">Password for client-side encryption of messages: <input type="password" name="pwd" id="pwd" value=""></span> |
1454 | <span class="logout"><a href="<?php echo $_SERVER['SCRIPT_NAME']; ?>" onClick="send_logoff();return true;">Back to login form</a></span> |
1461 | <span class="logout"><a href="<?php echo $_SERVER['SCRIPT_NAME']; ?>" onClick="send_logoff();return true;">Back to login form</a></span> |
1455 | </div> |
1462 | </div> |
1456 | <div id="status"><em>Loading...</em></div> |
1463 | <div id="status"><em>Loading...</em></div> |
1457 | <div id="activeusers"></div> |
1464 | <div id="activeusers"></div> |
1458 | <div id="chatmessages"></div> |
1465 | <div id="chatmessages"></div> |
1459 | <form name="f" action="#" onsubmit="add();return false;"> |
1466 | <form name="f" action="#" onsubmit="add();return false;"> |
1460 | <div id="postarea"> |
1467 | <div id="postarea"> |
1461 | <textarea cols="70" rows="3" name="line" id="line" value="" onfocus="onFocusChatField()"></textarea> |
1468 | <textarea cols="70" rows="3" name="line" id="line" value="" onfocus="onFocusChatField()"></textarea> |
1462 | <input type="button" name="check" value="Post message" onclick="add();return false;" class="button"> |
1469 | <input type="button" name="check" value="Post message" onclick="add();return false;" class="button"> |
1463 | <table width="100%" border="0" cellspacing="0" cellpadding="0"> |
1470 | <table width="100%" border="0" cellspacing="0" cellpadding="0"> |
1464 | <tr> |
1471 | <tr> |
1465 | <td align="left" width="50%"><label><input type="checkbox" id="autoscroll" name="autoscroll" value="yes" checked> Scroll down when new message arrives</label><br></td> |
1472 | <td align="left" width="50%"><label><input type="checkbox" id="autoscroll" name="autoscroll" value="yes" checked> Scroll down when new message arrives</label><br></td> |
1466 | <td align="left" width="50%"><label><input type="checkbox" id="doencrypt" name="doencrypt" value="yes" checked> Encrypt all messages</label><br></td> |
1473 | <td align="left" width="50%"><label><input type="checkbox" id="doencrypt" name="doencrypt" value="yes" checked> Encrypt all messages</label><br></td> |
1467 | </tr> |
1474 | </tr> |
1468 | </table> |
1475 | </table> |
1469 | </div> |
1476 | </div> |
1470 | </form> |
1477 | </form> |
1471 | 1478 | ||
1472 | </body> |
1479 | </body> |
1473 | 1480 | ||
1474 | <?php |
1481 | <?php |
1475 | 1482 | ||
1476 | } else if ($show_list) { |
1483 | } else if ($show_list) { |
1477 | 1484 | ||
1478 | ?> |
1485 | ?> |
1479 | 1486 | ||
1480 | </head> |
1487 | </head> |
1481 | 1488 | ||
1482 | <body> |
1489 | <body> |
1483 | 1490 | ||
1484 | <h1><?php echo htmlentities(PRODUCT_NAME); ?> <?php echo htmlentities(MCC_VER); ?></h1> |
1491 | <h1><?php echo htmlentities(PRODUCT_NAME); ?> <?php echo htmlentities(MCC_VER); ?></h1> |
1485 | 1492 | ||
1486 | <h2>List chat rooms</h2> |
1493 | <h2>List chat rooms</h2> |
1487 | 1494 | ||
1488 | <table border="1" cellpadding="4" cellspacing="0"> |
1495 | <table border="1" cellpadding="4" cellspacing="0"> |
1489 | <thead><tr> |
1496 | <thead><tr> |
1490 | <td>Chat room</td> |
1497 | <td>Chat room</td> |
1491 | <td>Messages</td> |
1498 | <td>Messages</td> |
1492 | <td>Users</td> |
1499 | <td>Users</td> |
1493 | <td>Last activity</td> |
1500 | <td>Last activity</td> |
1494 | </tr></thead> |
1501 | </tr></thead> |
1495 | <tbody> |
1502 | <tbody> |
1496 | <?php |
1503 | <?php |
1497 | 1504 | ||
1498 | $rooms = list_rooms(); |
1505 | $rooms = list_rooms(); |
1499 | foreach ($rooms as &$room) { |
1506 | foreach ($rooms as &$room) { |
1500 | $room_file = chatroom_file($room); |
1507 | $room_file = chatroom_file($room); |
1501 | $messages = count_messages($room); |
1508 | $messages = count_messages($room); |
1502 | $users = count_users($room); |
1509 | $users = count_users($room); |
1503 | $last_activity = date('Y-m-d H:i:s o', filemtime($room_file)); |
1510 | $last_activity = date('Y-m-d H:i:s o', filemtime($room_file)); |
1504 | echo '<tr>'; |
1511 | echo '<tr>'; |
1505 | echo '<td>'.htmlentities($room).'</td>'; |
1512 | echo '<td>'.htmlentities($room).'</td>'; |
1506 | echo '<td>'.htmlentities($messages).'</td>'; |
1513 | echo '<td>'.htmlentities($messages).'</td>'; |
1507 | echo '<td>'.htmlentities($users).'</td>'; |
1514 | echo '<td>'.htmlentities($users).'</td>'; |
1508 | echo '<td>'.htmlentities($last_activity).'</td>'; |
1515 | echo '<td>'.htmlentities($last_activity).'</td>'; |
1509 | echo '</tr>'; |
1516 | echo '</tr>'; |
1510 | } |
1517 | } |
1511 | 1518 | ||
1512 | ?> |
1519 | ?> |
1513 | </tbody> |
1520 | </tbody> |
1514 | </table> |
1521 | </table> |
1515 | 1522 | ||
1516 | <p><a href="<?php echo $_SERVER['SCRIPT_NAME']; ?>">Back to login</a></p> |
1523 | <p><a href="<?php echo $_SERVER['SCRIPT_NAME']; ?>">Back to login</a></p> |
1517 | 1524 | ||
1518 | <?php |
1525 | <?php |
1519 | 1526 | ||
1520 | } else { |
1527 | } else { |
1521 | 1528 | ||
1522 | ?> |
1529 | ?> |
1523 | 1530 | ||
1524 | </head> |
1531 | </head> |
1525 | 1532 | ||
1526 | <body> |
1533 | <body> |
1527 | 1534 | ||
1528 | <h1><?php echo htmlentities(PRODUCT_NAME); ?> <?php echo htmlentities(MCC_VER); ?></h1> |
1535 | <h1><?php echo htmlentities(PRODUCT_NAME); ?> <?php echo htmlentities(MCC_VER); ?></h1> |
1529 | 1536 | ||
1530 | <h2>Login</h2> |
1537 | <h2>Login</h2> |
1531 | 1538 | ||
1532 | <form action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="POST"> |
1539 | <form action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="POST"> |
1533 | <input type="hidden" name="sent" value="1"> |
1540 | <input type="hidden" name="sent" value="1"> |
1534 | 1541 | ||
1535 | <?php |
1542 | <?php |
1536 | 1543 | ||
1537 | if (isset($_POST['sent'])) { |
1544 | if (isset($_POST['sent'])) { |
1538 | if ($ses_room == '') { |
1545 | if ($ses_room == '') { |
1539 | echo "<p><span class=\"error\"><b>Error:</b> You must enter a chat room name.</span></p>"; |
1546 | echo "<p><span class=\"error\"><b>Error:</b> You must enter a chat room name.</span></p>"; |
1540 | } elseif (!$chat_exists) { |
1547 | } elseif (!$chat_exists) { |
1541 | echo "<p><span class=\"error\"><b>Error:</b> Chat room <b>".htmlentities($ses_room)."</b> does not exist. Please ask an administrator to create one.</span></p>"; |
1548 | echo "<p><span class=\"error\"><b>Error:</b> Chat room <b>".htmlentities($ses_room)."</b> does not exist. Please ask an administrator to create one.</span></p>"; |
1542 | } |
1549 | } |
1543 | } |
1550 | } |
1544 | 1551 | ||
1545 | ?> |
1552 | ?> |
1546 | 1553 | ||
1547 | Room<?php if (CHAT_MUST_EXIST) echo " (chat room must exist)"; ?>:<br> |
1554 | Room<?php if (CHAT_MUST_EXIST) echo " (chat room must exist)"; ?>:<br> |
1548 | <input type="text" name="ses_room" value="<?php echo $ses_room; ?>"><br><br> |
1555 | <input type="text" name="ses_room" value="<?php echo $ses_room; ?>"><br><br> |
1549 | 1556 | ||
1550 | <?php |
1557 | <?php |
1551 | 1558 | ||
1552 | if (isset($_POST['sent'])) { |
1559 | if (isset($_POST['sent'])) { |
1553 | if ($ses_user == '') { |
1560 | if ($ses_user == '') { |
1554 | echo "<p><span class=\"error\"><b>Error:</b> You must enter an username.</span></p>"; |
1561 | echo "<p><span class=\"error\"><b>Error:</b> You must enter an username.</span></p>"; |
1555 | } |
1562 | } |
1556 | } |
1563 | } |
1557 | 1564 | ||
1558 | ?> |
1565 | ?> |
1559 | 1566 | ||
1560 | Username (you can freely choose it yourself):<br> |
1567 | Username (you can freely choose it yourself):<br> |
1561 | <input type="text" name="ses_user" value="<?php echo $ses_user; ?>"><br><br> |
1568 | <input type="text" name="ses_user" value="<?php echo $ses_user; ?>"><br><br> |
1562 | 1569 | ||
1563 | <input type="submit" value="Login" class="button"> |
1570 | <input type="submit" value="Login" class="button"> |
1564 | 1571 | ||
1565 | <?php |
1572 | <?php |
1566 | if (CHAT_MUST_EXIST) { |
1573 | if (CHAT_MUST_EXIST) { |
1567 | if (isset($_POST['sent'])) { |
1574 | if (isset($_POST['sent'])) { |
1568 | if (($admin_pwd != '') && (!adminAuth($admin_pwd, ACTION_CREATE_ROOM))) { |
1575 | if (($admin_pwd != '') && (!adminAuth($admin_pwd, ACTION_CREATE_ROOM))) { |
1569 | echo "<p><span class=\"error\"><b>Error:</b> This is not the correct administrator password.</span></p>"; |
1576 | echo "<p><span class=\"error\"><b>Error:</b> This is not the correct administrator password.</span></p>"; |
1570 | } |
1577 | } |
1571 | } |
1578 | } |
1572 | ?> |
1579 | ?> |
1573 | <br><br><br>Optional: Admin-Password for creating a new chat:<br> |
1580 | <br><br><br>Optional: Admin-Password for creating a new chat:<br> |
1574 | <input type="password" name="admin_pwd" value=""><br><br> |
1581 | <input type="password" name="admin_pwd" value=""><br><br> |
1575 | <?php |
1582 | <?php |
1576 | } |
1583 | } |
1577 | ?> |
1584 | ?> |
1578 | 1585 | ||
1579 | </form> |
1586 | </form> |
1580 | 1587 | ||
1581 | <hr> |
1588 | <hr> |
1582 | 1589 | ||
1583 | <h2>List chat rooms</h2> |
1590 | <h2>List chat rooms</h2> |
1584 | 1591 | ||
1585 | <form action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="POST"> |
1592 | <form action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="POST"> |
1586 | <input type="hidden" name="list_chatrooms" value="1"> |
1593 | <input type="hidden" name="list_chatrooms" value="1"> |
1587 | 1594 | ||
1588 | <?php |
1595 | <?php |
1589 | if (LIST_CHATS_REQUIRE_PWD) { |
1596 | if (LIST_CHATS_REQUIRE_PWD) { |
1590 | if (isset($_POST['list_chatrooms'])) { |
1597 | if (isset($_POST['list_chatrooms'])) { |
1591 | echo "<p><span class=\"error\"><b>Error:</b> Wrong password.</span></p>"; |
1598 | echo "<p><span class=\"error\"><b>Error:</b> Wrong password.</span></p>"; |
1592 | } |
1599 | } |
1593 | ?> |
1600 | ?> |
1594 | Admin-Password for listing chats:<br> |
1601 | Admin-Password for listing chats:<br> |
1595 | <input type="password" name="admin_pwd" value=""><br><br> |
1602 | <input type="password" name="admin_pwd" value=""><br><br> |
1596 | <?php |
1603 | <?php |
1597 | } |
1604 | } |
1598 | ?> |
1605 | ?> |
1599 | 1606 | ||
1600 | <input type="submit" value="List chat rooms" class="button"><br><br> |
1607 | <input type="submit" value="List chat rooms" class="button"><br><br> |
1601 | 1608 | ||
1602 | </form> |
1609 | </form> |
1603 | 1610 | ||
1604 | <hr> |
1611 | <hr> |
1605 | 1612 | ||
1606 | <p><a href="http://www.viathinksoft.com/redir.php?id=324897" target="_blank"><?php echo htmlentities(PRODUCT_NAME); ?> <?php echo htmlentities(MCC_VER); ?></a> © 2014-2018 <a href="http://www.viathinksoft.com/" target="_blank">ViaThinkSoft</a>.</a></p> |
1613 | <p><a href="http://www.viathinksoft.com/redir.php?id=324897" target="_blank"><?php echo htmlentities(PRODUCT_NAME); ?> <?php echo htmlentities(MCC_VER); ?></a> © 2014-2018 <a href="http://www.viathinksoft.com/" target="_blank">ViaThinkSoft</a>.</a></p> |
1607 | 1614 | ||
1608 | </body> |
1615 | </body> |
1609 | 1616 | ||
1610 | <?php |
1617 | <?php |
1611 | } |
1618 | } |
1612 | ?> |
1619 | ?> |
1613 | 1620 | ||
1614 | </html> |
1621 | </html> |
1615 | 1622 |