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