initial translation by Koen Roggemans
[moodle.git] / mod / chat / chatd.php
CommitLineData
8e7eec60 1#!./php -q
2<?php
3
4echo "Moodle chat daemon v1.0\n\n";
5
6
7
8/// Set up all the variables we need /////////////////////////////////////
9
10/// $CFG variables are now defined in database by chat/lib.php
11
12$_SERVER['PHP_SELF'] = "dummy";
13$_SERVER['SERVER_NAME'] = "dummy";
14
15include('../../config.php');
16include('lib.php');
17
18$_SERVER['SERVER_NAME'] = $CFG->chat_serverhost;
19$_SERVER['PHP_SELF'] = "http://$CFG->chat_serverhost:$CFG->chat_serverport/mod/chat/chatd.php";
20
21$safemode = ini_get('safe_mode');
22
23if(!empty($safemode)) {
24 die("Error: Cannot run with PHP safe_mode = On. Turn off safe_mode.\n");
25}
26
27@set_time_limit (0);
28set_magic_quotes_runtime(0);
29
30error_reporting(E_ALL);
31
32function chat_empty_connection() {
33 return array('sid' => NULL, 'handle' => NULL, 'ip' => NULL, 'port' => NULL, 'groupid' => NULL);
34}
35
36class ChatConnection {
37 var $sid = NULL;
38 var $handle = NULL;
39 var $ip = NULL;
40 var $port = NULL;
41 var $groupid = NULL;
42 var $lastmessages = array();
43 var $lastmsgindex = 0;
44 var $type = NULL;
45}
46
47class ChatMessage {
48 var $chatid = NULL;
49 var $userid = NULL;
50 var $groupid = NULL;
51 var $system = NULL;
52 var $message = NULL;
53 var $timestamp = NULL;
54
55 var $text_ = '';
56 var $html_ = '';
57 var $beep_ = false;
58
59}
60
61class ChatDaemon {
62 var $conn_ufo = array(); // Connections not identified yet
63 var $conn_side = array(); // Sessions with sidekicks waiting for the main connection to be processed
64 var $conn_half = array(); // Sessions that have valid connections but not all of them
65 var $conn_sets = array(); // Sessions with complete connection sets sets
66 var $sets_info = array(); // Keyed by sessionid exactly like conn_sets, one of these for each of those
67
68 var $message_queue = array(); // Holds messages that we haven't committed to the DB yet
69
b5de723d 70 function get_user_window($sessionid) {
71 ob_start();
72 echo "<html>\n<head>\n";
73
74 // <head>
75 /*
76 echo "<script type='text/javascript'>\n";
77 echo " function reloadme() {\n";
78 //echo " return false;\n";
79 echo " window.location.reload();\n";
80 echo " }\n";
81 echo "</script>";
82 */
83 // </head>
84
85 //echo "</head>\n<body onload='window.setTimeout(\"reloadme()\", 2000);'>\n";
86 echo "</head>\n<body>\n";
87
88 // <body>
89 echo '<p style="font-size: 0.75em;">User window for session '.$sessionid.'</p>';
90 echo '<p style="font-size: 0.75em;">The time is '.time().'</p>';
91 // </body>
92
93 echo "</body>\n</html>\n";
94 return ob_get_clean();
95
96 }
97
8e7eec60 98 function new_ufo_id() {
99 static $id = 0;
100 if($id++ === 0x1000000) { // Cycling very very slowly to prevent overflow
101 $id = 0;
102 }
103 return $id;
104 }
105
106 function process_sidekicks($sessionid) {
107 if(empty($this->conn_side[$sessionid])) {
108 return true;
109 }
110 foreach($this->conn_side[$sessionid] as $sideid => $sidekick) {
111 $this->dispatch_sidekick($sidekick['handle'], $sidekick['type'], $sessionid, $sidekick['customdata']);
112 unset($this->conn_side[$sessionid][$sideid]);
113 }
114 return true;
115 }
116
117 function dispatch_sidekick($handle, $type, $sessionid, $customdata) {
118 global $CFG;
119
120 switch($type) {
121 case CHAT_SIDEKICK_USERS:
b5de723d 122 /*
123 //$x = pusers($this->sets_info[$sessionid]['chatid'], $this->sets_info[$sessionid]['groupid']);
8e7eec60 124 //$x = "<html>Lalalala! ".time()."</html>";
125
126 $header = "HTTP/1.1 200 OK\n";
127 $header .= "Connection: close\n";
128 $header .= "Date: ".date('r')."\n";
129 $header .= "Server: Moodle\n";
130 $header .= "Content-Type: text/html\n";
131 $header .= "Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT\n";
132 $header .= "Cache-Control: no-cache, must-revalidate\n";
133 $header .= "Expires: Wed, 4 Oct 1978 09:32:45 GMT\n";
b5de723d 134 //$header .= "Refresh: 3; url=http://$CFG->chat_serverhost:$CFG->chat_serverport?win=users&".
135 // "chat_sid=".$sessionid."&groupid=".$this->sets_info[$sessionid]['groupid']."\n";
8e7eec60 136 $header .= "\n";
137
138 $x = $header.$x;
b5de723d 139*/
8e7eec60 140
b5de723d 141/*
8e7eec60 142 trace('Outputting user list('.strlen($x).' chars)');
143 //trace($x);
144
145 chat_socket_write($handle, $x);
b5de723d 146*/
147
148 $content = $this->get_user_window($sessionid);
149
150 $header = "HTTP/1.1 200 OK\n";
151 $header .= "Connection: close\n";
152 $header .= "Date: ".date('r')."\n";
153 $header .= "Server: Moodle\n";
154 $header .= "Content-Type: text/html\n";
155 $header .= "Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT\n";
156 $header .= "Cache-Control: no-cache, must-revalidate\n";
157 $header .= "Expires: Wed, 4 Oct 1978 09:32:45 GMT\n";
158 $header .= "Content-Length: ".strlen($content)."\n";
159 $header .= "Refresh: 3; url=http://$CFG->chat_serverhost:$CFG->chat_serverport/?win=users&".
160 "chat_sid=".$sessionid."&groupid=".$this->sets_info[$sessionid]['groupid']."\n";
161 $header .= "\n";
162
163 // That's enough headers for one lousy dummy response
164 chat_socket_write($handle, $header . $content);
165
8e7eec60 166 break;
167 case CHAT_SIDEKICK_MESSAGE:
168 // Incoming message
169 $msg = &New ChatMessage;
170 $msg->chatid = $this->sets_info[$sessionid]['chatid'];
171 $msg->userid = $this->sets_info[$sessionid]['userid'];
172 $msg->groupid = $this->sets_info[$sessionid]['groupid'];
173 $msg->system = 0;
174 $msg->message = urldecode($customdata['message']); // have to undo the browser's encoding
175 $msg->timestamp = time();
176
177 if(empty($msg->message)) {
178 // Someone just hit ENTER, send them on their way
179 break;
180 }
181
b5de723d 182 // Commit to DB
183 insert_record('chat_messages', $msg);
8e7eec60 184
185 // OK, now push it out to all users
b5de723d 186 $this->message_broadcast($msg, $this->sets_info[$sessionid]['user']);
8e7eec60 187
188 // Update that user's lastmessageping
189 // TODO: this can and should be written as a single UPDATE query
190 $user = get_record('chat_users', 'sid', $sessionid);
191 if($user !== false) {
192 $user->lastmessageping = $msg->timestamp;
193 update_record('chat_users', $user);
194 }
195
b5de723d 196 // We did our work, but before slamming the door on the poor browser
197 // show the courtesy of responding to the HTTP request. Otherwise, some
198 // browsers decide to get vengeance by flooding us with repeat requests.
199
200 $header = "HTTP/1.1 200 OK\n";
201 $header .= "Connection: close\n";
202 $header .= "Date: ".date('r')."\n";
203 $header .= "Server: Moodle\n";
204 $header .= "Content-Type: text/html\n";
205 $header .= "Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT\n";
206 $header .= "Cache-Control: no-cache, must-revalidate\n";
207 $header .= "Expires: Wed, 4 Oct 1978 09:32:45 GMT\n";
208 $header .= "\n";
209
210 // That's enough headers for one lousy dummy response
211 chat_socket_write($handle, $header);
212
8e7eec60 213 // All done
214 break;
215 }
216
217 socket_shutdown($handle);
218 socket_close($handle);
219 }
220
221 function promote_final($sessionid, $groupid) {
222 if(isset($this->conn_sets[$sessionid])) {
223 trace('Set cannot be finalized: Session '.$sessionid.' is already active');
224 return false;
225 }
226
227 $chatuser = get_record('chat_users', 'sid', $sessionid);
228 if($chatuser === false) {
229 $this->dismiss_half($sessionid);
230 return false;
231 }
232 $chat = get_record('chat', 'id', $chatuser->chatid);
233 if($chat === false) {
234 $this->dismiss_half($sessionid);
235 return false;
236 }
b5de723d 237 $user = get_record('user', 'id', $chatuser->userid);
238 if($user === false) {
239 $this->dismiss_half($sessionid);
240 return false;
241 }
242 $course = get_record('course', 'id', $chat->course); {
243 if($course === false) {
244 $this->dismiss_half($sessionid);
245 return false;
246 }
247 }
8e7eec60 248
b5de723d 249 global $CHAT_HTMLHEAD_JS, $CFG;
250
251 // A really sad thing, to have to do this by hand.... :-(
252 $lang = NULL;
253 if(empty($lang) && !empty($course->lang)) {
254 $lang = $course->lang;
255 }
256 if(empty($lang) && !empty($user->lang)) {
257 $lang = $user->lang;
258 }
259 if(empty($lang)) {
260 $lang = $CFG->lang;
261 }
8e7eec60 262
263 $this->conn_sets[$sessionid] = $this->conn_half[$sessionid];
b5de723d 264 $this->sets_info[$sessionid] = array(
265 'chatid' => $chatuser->chatid,
266 'user' => $user,
267 'userid' => $chatuser->userid,
268 'groupid' => $groupid,
269 'lang' => $lang
270 );
271
8e7eec60 272 $this->dismiss_half($sessionid, false);
273 chat_socket_write($this->conn_sets[$sessionid][CHAT_CONNECTION_CHANNEL], $CHAT_HTMLHEAD_JS);
274 trace('Finalized client: sid: '.$sessionid.' uid: '.$chatuser->userid.' gid: '.intval($groupid));
275
276 // Finally, broadcast the "entered the chat" message
277
278 $msg = &New ChatMessage;
279 $msg->chatid = $chatuser->chatid;
280 $msg->userid = $chatuser->userid;
281 $msg->groupid = 0;
282 $msg->system = 1;
283 $msg->message = 'enter';
284 $msg->timestamp = time();
285
286 insert_record('chat_messages', $msg);
b5de723d 287 $this->message_broadcast($msg, $this->sets_info[$sessionid]['user']);
8e7eec60 288
289 return true;
290 }
291
292 function promote_ufo($handle, $type, $sessionid, $groupid, $customdata) {
293 if(empty($this->conn_ufo)) {
294 return false;
295 }
296 foreach($this->conn_ufo as $id => $ufo) {
297 if($ufo == $handle) {
298 // OK, got the id of the UFO, but what is it?
299
300 if($type & CHAT_SIDEKICK) {
301 // Is the main connection ready?
302 if(isset($this->conn_sets[$sessionid])) {
303 // Yes, so dispatch this sidekick now and be done with it
304 trace('Dispatching sidekick immediately');
305 $this->dispatch_sidekick($handle, $type, $sessionid, $customdata);
306 $this->dismiss_ufo($handle, false);
307 }
308 else {
309 // No, so put it in the waiting list
310 trace('sidekick waiting');
311 $this->conn_side[$sessionid][] = array('type' => $type, 'handle' => $handle, 'customdata' => $customdata);
312 }
313 return true;
314 }
315
316 // If it's not a sidekick, at this point it can only be da man
317
318 if($type & CHAT_CONNECTION) {
319 // This forces a new connection right now...
320
321 // Do we have such a connection active?
322 if(isset($this->conn_sets[$sessionid])) {
323 // Yes, so regrettably we cannot promote you
324 trace('UFO #'.$id.' with '.$ufo.' cannot be promoted: session '.$sessionid.' is already final');
325 $this->dismiss_ufo($handle);
326 return false;
327 }
328
329 // Join this with what we may have already
330 $this->conn_half[$sessionid][$type] = $handle;
331
332 // Do the bookkeeping
333 $this->promote_final($sessionid, $groupid);
334
335 // It's not an UFO anymore
336 $this->dismiss_ufo($handle, false);
337
338 // Dispatch waiting sidekicks
339 $this->process_sidekicks($sessionid);
340
341 return true;
342 }
343
344 /*
345 // It's the first one for that sessionid, so it will start an incomplete connection
346 $this->conn_half[$sessionid] = array($type => $handle);
347 unset($this->conn_ufo[$id]);
348 trace('UFO #'.$id.': identified session '.$sessionid.' wintype '.$type);
349 return true;
350 */
351 }
352 }
353 return false;
354 }
355
356 function dismiss_half($sessionid, $disconnect = true) {
357 if(!isset($this->conn_half[$sessionid])) {
358 return false;
359 }
360 if($disconnect) {
361 foreach($this->conn_half[$sessionid] as $handle) {
362 socket_shutdown($handle);
363 socket_close($handle);
364 }
365 }
366 unset($this->conn_half[$sessionid]);
367 return true;
368 }
369
370 function dismiss_set($sessionid) {
371 if(!isset($this->conn_sets[$sessionid])) {
372 return false;
373 }
374 foreach($this->conn_sets[$sessionid] as $handle) {
b5de723d 375 // Since we want to dismiss this, don't generate any errors if it's dead already
376 @socket_shutdown($handle);
377 @socket_close($handle);
8e7eec60 378 }
379 unset($this->conn_sets[$sessionid]);
380 unset($this->sets_info[$sessionid]);
381 return true;
382 }
383
384
385 function dismiss_ufo($handle, $disconnect = true) {
386 if(empty($this->conn_ufo)) {
387 return false;
388 }
389 foreach($this->conn_ufo as $id => $ufo) {
390 if($ufo == $handle) {
391 unset($this->conn_ufo[$id]);
392 if($disconnect) {
393 chat_socket_write($handle, "You don't seem to be a valid client.\n");
394 socket_shutdown($handle);
395 socket_close($handle);
396 }
397 return true;
398 }
399 }
400 return false;
401 }
402
403 function conn_accept() {
404 $handle = @socket_accept($this->listen_socket);
405 if(!$handle) {
406 return false;
407 }
408
409 $newconn = &New ChatConnection;
410 $newconn->handle = $handle;
411 socket_getpeername($newconn->handle, &$newconn->ip, &$newconn->port);
412
413 $id = $this->new_ufo_id();
414 trace('UFO #'.$id.': connection from '.$newconn->ip.' on port '.$newconn->port.', '.$newconn->handle);
415
416 $this->conn_ufo[$id] = $newconn->handle;
417 }
418
419 function conn_activity_ufo (&$handles) {
420 $monitor = array();
421 if(!empty($this->conn_ufo)) {
422 foreach($this->conn_ufo as $ufoid => $ufo) {
423 $monitor[$ufoid] = $ufo;
424 }
425 }
426
427 if(empty($monitor)) {
428 $handles = array();
429 return 0;
430 }
431
432 $retval = socket_select($monitor, $a = NULL, $b = NULL, NULL);
433 $handles = $monitor;
434
435 return $retval;
436 }
437
b5de723d 438 function message_broadcast($message, $sender) {
8e7eec60 439 if(empty($this->conn_sets)) {
440 return true;
441 }
442
8e7eec60 443 foreach($this->sets_info as $sessionid => $info) {
444 // We need to get handles from users that are in the same chatroom, same group
445 if($info['chatid'] == $message->chatid &&
446 ($info['groupid'] == $message->groupid || $message->groupid == 0))
447 {
448
449 // Simply give them the message
b5de723d 450 $output = chat_format_message_manually($message, 0, $sender, $info['userid'], $info['lang']);
8e7eec60 451
b5de723d 452 if($output->beep) {
453 chat_socket_write($this->conn_sets[$sessionid][CHAT_CONNECTION_CHANNEL], '<embed src="'.$this->beepsoundsrc.'" autostart="true" hidden="true" />');
454 }
455
456 if(!chat_socket_write($this->conn_sets[$sessionid][CHAT_CONNECTION_CHANNEL], $output->html)) {
8e7eec60 457
458 // Send failed! We must now disconnect/forget about the user FIRST
459 // and THEN broadcast a message to all others... otherwise, infinite recursion.
460
461 delete_records('chat_users', 'sid', $sessionid);
462 $msg = &New ChatMessage;
463 $msg->chatid = $info['chatid'];
464 $msg->userid = $info['userid'];
465 $msg->groupid = 0;
466 $msg->system = 1;
467 $msg->message = 'exit';
468 $msg->timestamp = time();
469
470 trace('Client socket write failed, destroying uid '.$info['userid'].' with SID '.$sessionid);
471 insert_record('chat_messages', $msg);
472
b5de723d 473 // *************************** IMPORTANT
474 //
475 // Kill him BEFORE broadcasting, otherwise we 'll get infinite recursion!
476 //
477 // **********************************************************************
478 $latesender = $this->sets_info[$sessionid]['user'];
8e7eec60 479 $this->dismiss_set($sessionid);
b5de723d 480 $this->message_broadcast($msg, $latesender);
8e7eec60 481 }
482 //trace('Sent to UID '.$this->sets_info[$sessionid]['userid'].': '.$message->text_);
483 }
484 }
485 }
486
487 function message_commit() {
488 }
489
490}
491
492// Connection telltale
493define('CHAT_CONNECTION', 0x10);
494// Connections: Incrementing sequence, 0x10 to 0x1f
495define('CHAT_CONNECTION_CHANNEL', 0x11);
496
497// Sidekick telltale
498define('CHAT_SIDEKICK', 0x20);
499// Sidekicks: Incrementing sequence, 0x21 to 0x2f
500define('CHAT_SIDEKICK_USERS', 0x21);
501define('CHAT_SIDEKICK_MESSAGE', 0x22);
502
503
504$DAEMON = New ChatDaemon;
505$DAEMON->socket_active = false;
506$DAEMON->trace_level = E_ALL;
507$DAEMON->socketserver_refresh = 10;
508$DAEMON->rememberlast = 10;
509$DAEMON->can_daemonize = function_exists('pcntl_fork');
b5de723d 510$DAEMON->beepsoundsrc = $CFG->wwwroot.'/mod/chat/beep.wav';
8e7eec60 511
512/// Check the parameters //////////////////////////////////////////////////////
513
514 $param = trim(strtolower($argv[1]));
515
516 if (empty($param) || eregi('^(\-\-help|\-h)$', $param)) {
517 echo 'Starts the Moodle chat socket server on port '.$CFG->chat_serverport;
518 echo "\n\n";
519 echo "Usage: chatd.php [-h|--start]\n\n";
520 echo "Example:\n";
521 echo " chatd.php --start\n\n";
522 echo "Options:\n";
523 echo " --start Starts the daemon\n";
524 echo " -h, --help Show this help\n";
525 echo "\n";
526 die();
527 }
528
529
b5de723d 530$logfile = fopen('chatd.log', 'a+');
8e7eec60 531
532/// Try to set up all the sockets ////////////////////////////////////////////////
533
534trace('Setting up sockets');
535
536if (!function_exists('socket_set_option')) {
537 // PHP < 4.3
538 if (!function_exists('socket_setopt')) {
539 // No socket_setopt!
540 echo "Error: Neither socket_setopt() nor socket_set_option() exists.\n";
541 echo "Possibly PHP has not been compiled with --enable-sockets.\n\n";
542 die();
543 }
544 function socket_set_option($socket, $level, $name, $val) {
545 return socket_setopt($socket, $level, $name, $val);
546 }
547}
548
549// Creating socket
550
551if(false === ($DAEMON->listen_socket = socket_create(AF_INET, SOCK_STREAM, 0))) {
552 // Failed to create socket
553 $DAEMON->last_error = socket_last_error();
554 echo "Error: socket_create() failed: ". socket_strerror(socket_last_error($DAEMON->last_error)).' ['.$DAEMON->last_error."]\n";
555 die();
556}
557
558//socket_close($DAEMON->listen_socket);
559//die();
560
561if(!socket_bind($DAEMON->listen_socket, $CFG->chat_serverip, $CFG->chat_serverport)) {
562 // Failed to bind socket
563 $DAEMON->last_error = socket_last_error();
564 echo "Error: socket_bind() failed: ". socket_strerror(socket_last_error($DAEMON->last_error)).' ['.$DAEMON->last_error."]\n";
b5de723d 565
566 // If $DAEMON->last_error == 98 (Success), maybe we need to try cleaning up a bit before dying
567 // This is EXPERIMENTAL code!
568 if($DAEMON->last_error == 98) {
569 trace('experimental fix kicks in');
570 socket_close($DAEMON->listen_socket);
571 }
8e7eec60 572 die();
573}
574if(!socket_listen($DAEMON->listen_socket, $CFG->chat_servermax)) {
575 // Failed to get socket to listen
576 $DAEMON->last_error = socket_last_error();
577 echo "Error: socket_listen() failed: ". socket_strerror(socket_last_error($DAEMON->last_error)).' ['.$DAEMON->last_error."]\n";
578 die();
579}
580
581// Socket has been initialized and is ready
582trace('Socket opened on port '.$CFG->chat_serverport);
583$DAEMON->socket_active = true;
584
585// [pj]: I really must have a good read on sockets. What exactly does this do?
586// http://www.unixguide.net/network/socketfaq/4.5.shtml is still not enlightening enough for me.
587socket_set_option($DAEMON->listen_socket, SOL_SOCKET, SO_REUSEADDR, 1);
588socket_set_nonblock($DAEMON->listen_socket);
589
590/// Sockets all set up! Now we loop and process incoming data.
591/*
592declare(ticks=1);
593
594$pid = pcntl_fork();
595if ($pid == -1) {
596 die("could not fork");
597} else if ($pid) {
598 exit(); // we are the parent
599} else {
600 // we are the child
601}
602
603// detatch from the controlling terminal
604if (!posix_setsid()) {
605 die("could not detach from terminal");
606}
607
608// setup signal handlers
609pcntl_signal(SIGTERM, "sig_handler");
610pcntl_signal(SIGHUP, "sig_handler");
611*/
612
613if($DAEMON->can_daemonize) {
614 trace('Unholy spirit possession: daemonizing');
615 $DAEMON->pid = pcntl_fork();
616 if($pid == -1) {
617 trace('Process fork failed, terminating');
618 die();
619 }
620 else if($pid) {
621 // We are the parent
622 trace('Successfully forked the daemon with PID '.$pid);
623 die();
624 }
625 else {
626 // We are the daemon! :P
627 }
628
629 // FROM NOW ON, IT'S THE DAEMON THAT'S RUNNING!
630
631 // Detach from controlling terminal
632 if(!posix_setsid()) {
633 trace('Could not detach daemon process from terminal!');
634 }
635}
636else {
637 // Cannot go demonic
638 trace('Unholy spirit possession failed: PHP is not compiled with --enable-pcntl');
639}
640
641trace('Started Moodle chatd on port '.$CFG->chat_serverport.', listening socket '.$DAEMON->listen_socket, E_USER_WARNING);
642
643while(true) {
644 $active = array();
645
646 // First of all, let's see if any of our UFOs has identified itself
647 if($DAEMON->conn_activity_ufo($active)) {
648 foreach($active as $handle) {
649 $read_socket = array($handle);
650 $changed = socket_select($read_socket, $write = NULL, $except = NULL, 0, 0);
651
652 if($changed > 0) {
653 // Let's see what it has to say
654
655 $data = socket_read($handle, 512);
656 if(empty($data)) {
657 continue;
658 }
659
660 if(!ereg('win=(chat|users|message).*&chat_sid=([a-zA-Z0-9]*)&groupid=([0-9]*) HTTP', $data, $info)) {
661 // Malformed data
662 trace('UFO with '.$handle.': Request with malformed data; connection closed', E_USER_WARNING);
663 $DAEMON->dismiss_ufo($handle);
664 continue;
665 }
666
667 $type = $info[1];
668 $sessionid = $info[2];
669 $groupid = $info[3];
670
671 $customdata = array();
672
673 switch($type) {
674 case 'chat':
675 $type = CHAT_CONNECTION_CHANNEL;
676 break;
677 case 'users':
678 $type = CHAT_SIDEKICK_USERS;
679 break;
680 case 'message':
681 $type = CHAT_SIDEKICK_MESSAGE;
682 if(!ereg('chat_message=([^&]*)[& ]', $data, $info)) {
683 trace('Message sidekick did not contain a valid message', E_USER_WARNING);
684 $DAEMON->dismiss_ufo($handle);
685 continue;
686 }
687 else {
688 $customdata = array('message' => $info[1]);
689 }
690 break;
691 default:
692 trace('UFO with '.$handle.': Request with unknown type; connection closed', E_USER_WARNING);
693 $DAEMON->dismiss_ufo($handle);
694 continue;
695 break;
696 }
697
698 // OK, now we know it's something good... promote it and pass it all the data it needs
699 $DAEMON->promote_ufo($handle, $type, $sessionid, $groupid, $customdata);
700 continue;
701 }
702 }
703 }
704
705 // Finally, accept new connections
706 $DAEMON->conn_accept();
707
708 usleep($DAEMON->socketserver_refresh);
709}
710
711@socket_shutdown($DAEMON->listen_socket, 0);
712die("\n\n-- terminated --\n");
713
714
715function trace($message, $level = E_USER_NOTICE) {
716 global $DAEMON, $logfile;
717
718 $date = date('[Y-m-d H:i:s] ');
719 $severity = '';
720
721 switch($level) {
722 case E_USER_WARNING: $severity = '*IMPORTANT* '; break;
723 case E_USER_ERROR: $severity = ' *CRITICAL* '; break;
724 }
725
726 $message = $date.$severity.$message."\n";
727
728 if ($DAEMON->trace_level & $level) {
729 if($level & E_USER_ERROR) {
730 fwrite(STDERR, $message);
731 }
732 fwrite(STDOUT, $message);
733 fwrite($logfile, $message);
734 fflush($logfile);
735 }
736 flush();
737}
738
739function chat_socket_write($connection, $text) {
740 $check_socket = array($connection);
741 $socket_changed = socket_select($read = NULL, $check_socket, $except = NULL, 0, 0);
742 if($socket_changed > 0) {
b5de723d 743 $written = socket_write($connection, $text, strlen($text));
744 //trace('socket_write wrote '.$written.' of '.strlen($text).' bytes');
8e7eec60 745 return true;
746 }
747 return false;
748}
749
b5de723d 750function pusers($chatid, $groupid) {
8e7eec60 751/// Delete users who are using text version and are old
752
753global $CFG, $str;
754
b5de723d 755static $lastupdate = 0;
756static $outputcache = NULL;
757
8e7eec60 758//chat_delete_old_users();
759
760
761/// Print headers
762ob_start();
763
764//print_header();
765
766$timenow = time();
767
8e7eec60 768
b5de723d 769// WARNING!!!! THESE ARE HERE TO REMOVE NOTICES!!!
770// THEY SHOULD BE FIXED!
771$chat_sid = 0;
772$chat->course = 0;
773
774// Keep the results cached for 5 seconds
775if($timenow - 5 > $lastupdate || empty($outputcache)) {
776
777 if (empty($str)) {
778 $str->idle = get_string("idle", "chat");
779 $str->beep = get_string("beep", "chat");
780 $str->day = get_string("day");
781 $str->days = get_string("days");
782 $str->hour = get_string("hour");
783 $str->hours = get_string("hours");
784 $str->min = get_string("min");
785 $str->mins = get_string("mins");
786 $str->sec = get_string("sec");
787 $str->secs = get_string("secs");
788 }
789
790 /// Get list of users
8e7eec60 791
b5de723d 792 if (!$chatusers = chat_get_users($chatid, $groupid)) {
793 print_string("errornousers", "chat");
794 die('no users');
795 //return ob_get_clean();
796 //exit;
797 }
8e7eec60 798
b5de723d 799 echo "<html>\n";
800 echo "<head>\n";
801 echo "<script lang=\"Javascript\">\n";
802 echo " function reloadme() {\n";
803 echo " return false;\n";
804 echo " window.location.reload();\n";
805 echo " }\n";
806 echo "</script></head>\n";
807 //echo '<body onload="setTimeout(\'reloadme()\', 5000);">'."\n\n";
808 //for ($i = 0; $i < 100; ++$i) {
809 // echo "<!-- nix -->\n";
810 //}
811
812 echo "<body>\n<table width=\"100%\">\n";
813 foreach ($chatusers as $chatuser) {
814 $lastping = $timenow - $chatuser->lastmessageping;
815 echo "<tr>\n<td width=35>";
816 echo "<a target=\"_new\" onClick=\"return openpopup('/user/view.php?id=$chatuser->id&course=$chat->course','user$chatuser->id','');\" href=\"$CFG->wwwroot/user/view.php?id=$chatuser->id&course=$chat->course\">";
817 print_user_picture($chatuser->id, 0, $chatuser->picture, false, false, false);
818 echo "</a></td><td valign=center>";
819 echo "<p><font size=1>";
820 echo fullname($chatuser)."<br />";
821 echo "<font color=\"#888888\">$str->idle: ".format_time($lastping, $str)."</font>";
822 echo " <a href=\"users.php?chat_sid=$chat_sid&beep=$chatuser->id&groupid=$groupid\">$str->beep</a>";
823 echo "</font></p>";
824 echo "<td>\n</tr>\n";
825 }
826 echo "</table>\n";
8e7eec60 827
b5de723d 828 //window.setTimeout('reloadme', 1000)
829 echo "</body>\n</html>\n";
8e7eec60 830
b5de723d 831 $lastupdate = $timenow;
832 return $outputcache = ob_get_clean();
833}
834else {
835 // Return cached output
836 trace('User window cached return');
837 return $outputcache;
838}
8e7eec60 839
840}
841
842?>