1<?php 2/** 3 * phpfreechat.class.php 4 * 5 * Copyright © 2006 Stephane Gully <stephane.gully@gmail.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the 19 * Free Software Foundation, 51 Franklin St, Fifth Floor, 20 * Boston, MA 02110-1301 USA 21 */ 22 23require_once dirname(__FILE__).'/pfccommand.class.php'; 24require_once dirname(__FILE__).'/pfcglobalconfig.class.php'; 25require_once dirname(__FILE__).'/pfcuserconfig.class.php'; 26require_once dirname(__FILE__).'/pfctemplate.class.php'; 27require_once dirname(__FILE__).'/../lib/utf8/utf8_substr.php'; 28require_once dirname(__FILE__).'/pfcresponse.class.php'; 29 30/** 31 * phpFreeChat is the entry point for developpers 32 * 33 * @example ../demo/demo1_simple.php 34 * @author Stephane Gully <stephane.gully@gmail.com> 35 */ 36class phpFreeChat 37{ 38 function phpFreeChat( &$params ) 39 { 40 if (!is_array($params)) 41 die('phpFreeChat parameters must be an array'); 42 43 // initialize the global config object 44 $c =& pfcGlobalConfig::Instance( $params ); 45 46 // need to initiate the user config object here because it uses sessions 47 $u =& pfcUserConfig::Instance(); 48 49 // this code is used to handle the AJAX call and build the response 50 if (isset($_REQUEST['pfc_ajax'])) 51 { 52 $function = isset($_REQUEST['f']) ? $_REQUEST['f'] : ''; 53 $cmd = isset($_REQUEST['cmd']) ? stripslashes($_REQUEST['cmd']) : ''; 54 $r = null; 55 if ($function && method_exists($this,$function)) 56 { 57 require_once dirname(__FILE__).'/pfcresponse.class.php'; 58 $r =& $this->$function($cmd); 59 } 60 echo $r->getOutput(); 61 die(); 62 } 63 } 64 65 /** 66 * depreciated 67 */ 68 function printJavaScript( $return = false ) 69 { 70 $output = ''; 71 if ($return) 72 return $output; 73 else 74 echo $output; 75 } 76 77 /** 78 * depreciated 79 */ 80 function printStyle( $return = false ) 81 { 82 $output = ''; 83 if($return) 84 return $output; 85 else 86 echo $output; 87 } 88 89 /** 90 * printChat must be called somewhere in the page 91 * it inserts necessary html which will receive chat's data 92 * usage: 93 * <code> 94 * <?php $chat->printChat(); ?> 95 * </code> 96 */ 97 function printChat( $return = false ) 98 { 99 $c =& pfcGlobalConfig::Instance(); 100 $u =& pfcUserConfig::Instance(); 101 102 $output = ''; 103 104 if (count($c->getErrors()) > 0) 105 { 106 $output .= "<p>phpFreeChat cannot be initialized, please correct these errors:</p><ul>"; 107 foreach( $c->getErrors() as $e ) $output .= "<li>".$e."</li>"; $output .= "</ul>"; 108 } 109 else 110 { 111 pfcI18N::SwitchOutputEncoding($c->output_encoding); 112 113 $path = $c->getFilePathFromTheme('chat.js.tpl.php'); 114 $t = new pfcTemplate($path); 115 $t->assignObject($u,"u"); 116 $t->assignObject($c,"c"); 117 $output .= $t->getOutput(); 118 119 pfcI18N::SwitchOutputEncoding(); 120 } 121 122 if($return) 123 return $output; 124 else 125 echo $output; 126 } 127 128 /** 129 * Encode special caracteres and remove extra slashes 130 */ 131 function FilterSpecialChar($str) 132 { 133 return htmlspecialchars($str, ENT_NOQUOTES); 134 } 135 136 /** 137 * Just check the nicknames doesn't start with spaces and is not longer than the max_nick_len 138 */ 139 function FilterNickname($nickname) 140 { 141 $c =& pfcGlobalConfig::Instance(); 142 //$nickname = str_replace("\\", "", $nickname); // '\' is a forbidden charactere for nicknames 143 $nickname = trim($nickname); 144 $nickname = utf8_substr($nickname, 0, $c->max_nick_len); 145 return $nickname; 146 } 147 148 /** 149 * search/replace smileys 150 */ 151 function FilterSmiley($msg) 152 { 153 $c =& pfcGlobalConfig::Instance(); 154 // build a preg_replace array 155 $search = array(); 156 $replace = array(); 157 $query = "/("; 158 foreach($c->smileys as $s_file => $s_strs) 159 { 160 foreach ($s_strs as $s_str) 161 { 162 $s_str = stripslashes($s_str); /* the :'( smileys needs this filter */ 163 $query .= preg_quote($s_str,'/')."|"; 164 $search[] = "/".preg_quote($s_str,'/')."/"; 165 $replace[] = '<img src="'.$s_file.'" alt="'.$s_str.'" title="'.$s_str.'" />'; 166 } 167 } 168 $query = substr($query, 0, strlen($query)-1); 169 $query .= ")/i"; 170 171 $split_words = preg_split($query, $msg, -1, PREG_SPLIT_DELIM_CAPTURE); 172 $msg = ""; 173 foreach($split_words as $word) 174 $msg .= preg_replace($search, $replace, $word); 175 return $msg; 176 } 177 178 179 /** 180 * Filter messages before they are sent to container 181 */ 182 function PreFilterMsg($msg) 183 { 184 $c =& pfcGlobalConfig::Instance(); 185 if (preg_match("/^\[/i",$msg)) 186 // add 25 characteres if the message starts with [ : means there is a bbcode 187 $msg = utf8_substr($msg, 0, $c->max_text_len+25); 188 else 189 $msg = utf8_substr($msg, 0, $c->max_text_len); 190 $msg = phpFreeChat::FilterSpecialChar($msg); 191 192 // $msg = phpFreeChat::FilterSmiley($msg); 193 194 /* if ($msg[0] == "\n") $msg = substr($msg, 1); */ // delete the first \n generated by FF 195 /* if (strpos($msg,"\n") > 0) $msg = "<br/>".$msg; 196 $msg = str_replace("\r\n", "<br/>", $msg); 197 $msg = str_replace("\n", "<br/>", $msg); 198 $msg = str_replace("\t", " ", $msg);*/ 199 //$msg = str_replace(" ", " ", $msg); 200 // $msg = preg_replace('/(http\:\/\/[^\s]*)/i', "<a href=\"$1\">$1</a>", $msg ); 201 return $msg; 202 } 203 204 /** 205 * Filter messages when they are recived from container 206 */ 207 function PostFilterMsg($msg) 208 { 209 //$c =& pfcGlobalConfig::Instance(); 210 // $msg = preg_replace('/('.preg_quote($c->nick,'/').')/i', "<strong>$1</strong>", $msg ); 211 $msg = preg_replace('/\n/i', "", $msg ); 212 return $msg; 213 } 214 215 function &handleRequest($request) 216 { 217 $c =& pfcGlobalConfig::Instance(); 218 $u =& pfcUserConfig::Instance(); 219 220 if ($c->debug) ob_start(); // capture output 221 222 $xml_reponse = new pfcResponse(); 223 224 // check the command 225 $cmdstr = ""; 226 $cmdname = ""; 227 $clientid = ""; 228 $recipient = ""; 229 $recipientid = ""; 230 $param = ""; 231 $sender = ""; 232 233 $res = pfcCommand::ParseCommand($request); 234 235 $cmdstr = isset($res['cmdstr']) ? $res['cmdstr'] : $request; 236 $cmdname = strtolower(isset($res['cmdname']) ? $res['cmdname'] : ''); 237 $clientid = isset($res['params'][0]) ? $res['params'][0] : ''; 238 $recipientid = isset($res['params'][1]) ? $res['params'][1] : ""; 239 $params = array_slice(is_array($res['params']) ? $res['params'] : array() ,2); 240 $param = implode(" ",$params); // to keep compatibility (will be removed) 241 $sender = $u->getNickname(); 242 243 // translate the recipientid to the channel name 244 if (isset($u->channels[$recipientid])) 245 { 246 $recipient = $u->channels[$recipientid]["recipient"]; 247 } 248 if (isset($u->privmsg[$recipientid])) 249 { 250 $recipient = $u->privmsg[$recipientid]["recipient"]; 251 252 253 // @todo: move this code in a proxy 254 if ($cmdname != "update" && 255 $cmdname != "leave" && // do not open the pv tab when other user close the tab 256 $cmdname != "quit" && 257 $cmdname != "privmsg2") 258 { 259 // alert the other from the new pv 260 // (warn other user that someone talk to him) 261 262 $ct =& pfcContainer::Instance(); 263 $nickidtopv = $u->privmsg[$recipientid]["pvnickid"]; 264 $cmdstr = 'privmsg2'; 265 $cmdp = array(); 266 $cmdp['param'] = $u->nickid;//$sender; 267 $cmdp['params'][] = $u->nickid;//$sender; 268 pfcCommand::AppendCmdToPlay($nickidtopv, $cmdstr, $cmdp); 269 } 270 271 } 272 273 274 $cmdp = array(); 275 $cmdp["clientid"] = $clientid; 276 $cmdp["sender"] = $sender; 277 $cmdp["recipient"] = $recipient; 278 $cmdp["recipientid"] = $recipientid; 279 // before playing the wanted command 280 // play the found commands into the meta 'cmdtoplay' 281 pfcCommand::RunPendingCmdToPlay($u->nickid, $cmdp, $xml_reponse); 282 // play the wanted command 283 $cmd =& pfcCommand::Factory($cmdname); 284 $cmdp["param"] = $param; 285 $cmdp["params"] = $params; 286 if ($cmd != NULL) 287 { 288 // call the command 289 if ($c->debug) 290 $cmd->run($xml_reponse, $cmdp); 291 else 292 @$cmd->run($xml_reponse, $cmdp); 293 } 294 else 295 { 296 $cmd =& pfcCommand::Factory("error"); 297 $cmdp = array(); 298 $cmdp["clientid"] = $clientid; 299 $cmdp["param"] = _pfc("Unknown command [%s]",stripslashes("/".$cmdname." ".$param)); 300 $cmdp["sender"] = $sender; 301 $cmdp["recipient"] = $recipient; 302 $cmdp["recipientid"] = $recipientid; 303 if ($c->debug) 304 $cmd->run($xml_reponse, $cmdp); 305 else 306 @$cmd->run($xml_reponse, $cmdp); 307 } 308 309 // do not update twice 310 // do not update when the user just quit 311 if ($cmdname != "update" && 312 $cmdname != "quit" && 313 $u->nickid != '') 314 { 315 // force an update just after a command is sent 316 // thus the message user just poster is really fastly displayed 317 $cmd =& pfcCommand::Factory("update"); 318 $cmdp = array(); 319 $cmdp["clientid"] = $clientid; 320 $cmdp["param"] = $param; 321 $cmdp["sender"] = $sender; 322 $cmdp["recipient"] = $recipient; 323 $cmdp["recipientid"] = $recipientid; 324 if ($c->debug) 325 $cmd->run($xml_reponse, $cmdp); 326 else 327 @$cmd->run($xml_reponse, $cmdp); 328 } 329 330 if ($c->debug) 331 { 332 // capture echoed content 333 // if a content not empty is captured it is a php error in the code 334 $data = ob_get_contents(); 335 if ($data != "") 336 { 337 // todo : display the $data somewhere to warn the user 338 } 339 ob_end_clean(); 340 } 341 342 // do nothing else if the xml response is empty in order to save bandwidth 343 if ($xml_reponse->getCommandCount() == 0) die(); 344 345 return $xml_reponse; 346 } 347 348 349 function &loadStyles($theme = 'default', &$xml_reponse) 350 { 351 if ($xml_reponse == null) $xml_reponse = new pfcResponse(); 352 353 $c =& pfcGlobalConfig::Instance(); 354 355 // do not overload the theme parameter as long as 356 // the ajax request do not give the correct one 357 // $c->theme = $theme; 358 359 $u =& pfcUserConfig::Instance(); 360 361 $js = '';//file_get_contents(dirname(__FILE__).'/client/createstylerule.js'); 362 $js .= 'var c = $H();'; 363 $path = $c->getFilePathFromTheme('style.css.php'); 364 require_once dirname(__FILE__).'/../lib/ctype/ctype.php'; // to keep compatibility for php without ctype support 365 require_once dirname(__FILE__).'/../lib/csstidy-1.2/class.csstidy.php'; 366 $css = new csstidy(); 367 $css->set_cfg('preserve_css',false); 368 369 370 $css_code = ''; 371 $t = new pfcTemplate(); 372 $t->assignObject($u,"u"); 373 $t->assignObject($c,"c"); 374 if (!$c->isDefaultFile('style.css.php')) 375 { 376 $t->setTemplate($c->theme_default_path.'/default/style.css.php'); 377 $css_code .= $t->getOutput(); 378 } 379 $t->setTemplate($c->getFilePathFromTheme('style.css.php')); 380 $css_code .= $t->getOutput(); 381 382 $css->parse($css_code); 383 foreach($css->css as $k => $v) 384 { 385 foreach($v as $k2 => $v2) 386 { 387 $rules = ''; 388 foreach($v2 as $k3 => $v3) 389 $rules .= $k3.':'.$v3.';'; 390 $js .= "c.set('".$k2."', '".str_replace("\n", "", $rules)."');\n"; 391 } 392 } 393 $js .= "var pfccss = new pfcCSS(); var k = c.keys(); c.each(function (a) { pfccss.applyRule(a[0],a[1]); });"; 394 $xml_reponse->script($js); 395 396 return $xml_reponse; 397 } 398 399 function &loadScripts($theme, &$xml_reponse) 400 { 401 if ($xml_reponse == null) $xml_reponse = new pfcResponse(); 402 403 $c =& pfcGlobalConfig::Instance(); 404 405 $js = ''; 406 407 // load customize.js.php 408 $path = $c->getFilePathFromTheme('customize.js.php'); 409 $t = new pfcTemplate($path); 410 $t->assignObject($c,"c"); 411 $js .= $t->getOutput(); 412 413 // load translations 414 require_once dirname(__FILE__).'/pfcjson.class.php'; 415 $json = new pfcJSON(); 416 417 $labels_to_load = 418 array( "Do you really want to leave this room ?", // _pfc 419 "Are you sure you want to close this tab ?", // _pfc 420 "Hide nickname marker", // _pfc 421 "Show nickname marker", // _pfc 422 "Hide dates and hours", // _pfc 423 "Show dates and hours", // _pfc 424 "Disconnect", // _pfc 425 "Connect", // _pfc 426 "Magnify", // _pfc 427 "Cut down", // _pfc 428 "Hide smiley box", // _pfc 429 "Show smiley box", // _pfc 430 "Hide online users box", // _pfc 431 "Show online users box", // _pfc 432 "Please enter your nickname", // _pfc 433 "Private message", // _pfc 434 "Close this tab", // _pfc 435 "Enter your message here", // _pfc 436 "Enter your nickname here", // _pfc 437 "Bold", // _pfc 438 "Italics", // _pfc 439 "Underline", // _pfc 440 "Delete", // _pfc 441 "Mail", // _pfc 442 "Color", // _pfc 443 "PHP FREE CHAT [powered by phpFreeChat-%s]", // _pfc 444 "Enter the text to format", // _pfc 445 "Configuration has been rehashed", // _pfc 446 "A problem occurs during rehash", // _pfc 447 "Chosen nickname is already used", // _pfc 448 "phpfreechat current version is %s", // _pfc 449 "Maximum number of joined channels has been reached", // _pfc 450 "Maximum number of private chat has been reached", // _pfc 451 "Click here to send your message", // _pfc 452 "Send", // _pfc 453 "You are not allowed to speak to yourself", // _pfc 454 "Close", // _pfc 455 "Chosen nickname is not allowed", // _pfc 456 "Enable sound notifications", // _pfc 457 "Disable sound notifications", // _pfc 458 "Input Required", // _pfc 459 "OK", // _pfc 460 "Cancel", // _pfc 461 "You are trying to speak to a unknown (or not connected) user", // _pfc 462 ); 463 foreach($labels_to_load as $l) 464 { 465 $js .= "pfc.res.setLabel(".$json->encode($l).",".$json->encode(_pfc2($l)).");\n"; 466 } 467 468 // load ressources 469 $fileurl_to_load = 470 array( 'images/ch.gif', 471 'images/pv.gif', 472 'images/tab_remove.gif', 473 'images/ch-active.gif', 474 'images/pv-active.gif', 475 'images/user.gif', 476 'images/user-me.gif', 477 'images/user_female.gif', 478 'images/user_female-me.gif', 479 'images/color-on.gif', 480 'images/color-off.gif', 481 'images/clock-on.gif', 482 'images/clock-off.gif', 483 'images/logout.gif', 484 'images/login.gif', 485 'images/maximize.gif', 486 'images/minimize.gif', 487 'images/smiley-on.gif', 488 'images/smiley-off.gif', 489 'images/online-on.gif', 490 'images/online-off.gif', 491 'images/bt_strong.gif', 492 'images/bt_em.gif', 493 'images/bt_ins.gif', 494 'images/bt_del.gif', 495 'images/bt_mail.gif', 496 'images/bt_color.gif', 497 'images/color_transparent.gif', 498 'images/close-whoisbox.gif', 499 'images/openpv.gif', 500 'images/user-admin.gif', 501 'images/sound-on.gif', 502 'images/sound-off.gif', 503 'sound.swf', 504 ); 505 506 foreach($fileurl_to_load as $f) 507 { 508 $js .= "pfc.res.setFileUrl(".$json->encode($f).",\"".$c->getFileUrlFromTheme($f)."\");\n"; 509 } 510 511 foreach($c->smileys as $s_file => $s_str) { 512 for($j = 0; $j<count($s_str) ; $j++) { 513 $js .= "pfc.res.setSmiley(".$json->encode($s_str[$j]).",\"".$c->getFileUrlFromTheme($s_file)."\");\n"; 514 } 515 } 516 517 $js .= ' 518pfc.gui.loadSmileyBox(); 519pfc.gui.loadBBCodeColorList(); 520pfc.connectListener(); 521pfc.refreshGUI(); 522if (pfc_connect_at_startup) pfc.connect_disconnect(); 523'; 524 525 $xml_reponse->script($js); 526 return $xml_reponse; 527 } 528 529 530 function loadInterface($theme = 'default', &$xml_reponse) 531 { 532 if ($xml_reponse == null) $xml_reponse = new pfcResponse(); 533 534 $c =& pfcGlobalConfig::Instance(); 535 536 // do not overload the theme parameter as long as 537 // the ajax request do not give the correct one 538 // $c->theme = $theme; 539 540 $u =& pfcUserConfig::Instance(); 541 542 $html = ''; 543 544 // pfcI18N::SwitchOutputEncoding($c->output_encoding); 545 546 $path = $c->getFilePathFromTheme('chat.html.tpl.php'); 547 $t = new pfcTemplate($path); 548 $t->assignObject($u,"u"); 549 $t->assignObject($c,"c"); 550 $html .= $t->getOutput(); 551 552 // pfcI18N::SwitchOutputEncoding(); 553 554 $xml_reponse->remove('pfc_loader'); // to hide the loading box 555 $xml_reponse->update('pfc_container', $html); 556 557 return $xml_reponse; 558 } 559 560 function &loadChat($theme = 'default') 561 { 562 $xml_reponse = new pfcResponse(); 563 564 $this->loadInterface($theme,$xml_reponse); 565 $this->loadStyles($theme,$xml_reponse); 566 $this->loadScripts($theme,$xml_reponse); 567 568 return $xml_reponse; 569 } 570 571} 572 573?> 574