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