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