initConfig(); // Initialize the DataBase connection: $this->initDataBaseConnection(); // Initialize request variables: $this->initRequestVars(); // Initialize the chat session: $this->initSession(); // Handle the browser request and send the response content: $this->handleRequest(); } function initConfig() { $config = null; if(!include_once AJAX_CHAT_PATH . '/config/config.php') { print('Error: Configuration file could not be loaded.'); exit; } $this->_config = &$config; // Initialize custom configuration settings: $this->initCustomConfig(); } function initRequestVars() { $this->_requestVars = array(); $this->_requestVars['ajax'] = isset($_REQUEST['ajax']) ? true : false; $this->_requestVars['userID'] = isset($_REQUEST['userID']) ? (int)$_REQUEST['userID'] : null; $this->_requestVars['userName'] = isset($_REQUEST['userName']) ? $_REQUEST['userName'] : null; $this->_requestVars['channelID'] = isset($_REQUEST['channelID']) ? (int)$_REQUEST['channelID'] : null; $this->_requestVars['channelName'] = isset($_REQUEST['channelName']) ? $_REQUEST['channelName'] : null; $this->_requestVars['text'] = isset($_POST['text']) ? $_POST['text'] : null; $this->_requestVars['lastID'] = isset($_REQUEST['lastID']) ? (int)$_REQUEST['lastID'] : 0; $this->_requestVars['login'] = isset($_REQUEST['login']) ? true : false; $this->_requestVars['logout'] = isset($_POST['logout']) ? true : false; $this->_requestVars['password'] = isset($_REQUEST['password']) ? $_REQUEST['password'] : null; $this->_requestVars['view'] = isset($_REQUEST['view']) ? $_REQUEST['view'] : null; $this->_requestVars['year'] = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : null; $this->_requestVars['month'] = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : null; $this->_requestVars['day'] = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : null; $this->_requestVars['hour'] = isset($_REQUEST['hour']) ? (int)$_REQUEST['hour'] : null; $this->_requestVars['search'] = isset($_REQUEST['search']) ? $_REQUEST['search'] : null; $this->_requestVars['shoutbox'] = isset($_REQUEST['shoutbox']) ? true : false; $this->_requestVars['getInfos'] = isset($_REQUEST['getInfos']) ? $_REQUEST['getInfos'] : null; $this->_requestVars['lang'] = isset($_REQUEST['lang']) ? $_REQUEST['lang'] : null; $this->_requestVars['delete'] = isset($_REQUEST['delete']) ? (int)$_REQUEST['delete'] : null; // Initialize custom request variables: $this->initCustomRequestVars(); // Remove slashes which have been added to user input strings if magic_quotes_gpc is On: /*if(get_magic_quotes_gpc()) { // It is safe to remove the slashes as we escape user data ourself array_walk( $this->_requestVars, create_function( '&$value, $key', 'if(is_string($value)) $value = stripslashes($value);' ) ); }*/ } function initDataBaseConnection() { // Create a new database object: $this->db = new AJAXChatDataBase( $this->_config['dbConnection'] ); // Use a new database connection if no existing is given: if(!$this->_config['dbConnection']['link']) { // Connect to the database server: $this->db->connect($this->_config['dbConnection']); if($this->db->error()) { echo $this->db->getError(); die(); } // Select the database: $this->db->select($this->_config['dbConnection']['name']); if($this->db->error()) { echo $this->db->getError(); die(); } } // Unset the dbConnection array for safety purposes: unset($this->_config['dbConnection']); } function getDataBaseTable($table) { return ($this->db->getName() ? $this->db->getName().'.'.$this->getConfig('dbTableNames',$table) : $this->getConfig('dbTableNames',$table)); } function initSession() { // Start the PHP session (if not already started): $this->startSession(); if($this->isLoggedIn()) { // Logout if we receive a logout request, the chat has been closed or the userID could not be revalidated: if($this->getRequestVar('logout') || !$this->isChatOpen() || !$this->revalidateUserID()) { $this->logout(); return; } // Logout if the Session IP is not the same when logged in and ipCheck is enabled: if($this->getConfig('ipCheck') && ($this->getSessionIP() === null || $this->getSessionIP() != $_SERVER['REMOTE_ADDR'])) { $this->logout('IP'); return; } } else if( // Login if auto-login enabled or a login, userName or shoutbox parameter is given: $this->getConfig('forceAutoLogin') || $this->getRequestVar('login') || $this->getRequestVar('userName') || $this->getRequestVar('shoutbox') ) { $this->login(); } // Initialize the view: $this->initView(); if($this->getView() == 'chat') { $this->initChatViewSession(); } else if($this->getView() == 'logs') { $this->initLogsViewSession(); } if(!$this->getRequestVar('ajax') && !headers_sent()) { // Set style cookie: $this->setStyle(); // Set langCode cookie: $this->setLangCodeCookie(); } $this->initCustomSession(); } function initLogsViewSession() { if($this->getConfig('socketServerEnabled')) { if(!$this->getSessionVar('logsViewSocketAuthenticated')) { $this->updateLogsViewSocketAuthentication(); $this->setSessionVar('logsViewSocketAuthenticated', true); } } } function updateLogsViewSocketAuthentication() { if($this->getUserRole() != AJAX_CHAT_ADMIN) { $channels = array(); foreach($this->getChannels() as $channel) { if($this->getConfig('logsUserAccessChannelList') && !in_array($channel, $this->getConfig('logsUserAccessChannelList'))) { continue; } array_push($channels, $channel); } array_push($channels, $this->getPrivateMessageID()); array_push($channels, $this->getPrivateChannelID()); } else { // The channelID "ALL" authenticates for all channels: $channels = array('ALL'); } $this->updateSocketAuthentication( $this->getUserID(), $this->getSocketRegistrationID(), $channels ); } function initChatViewSession() { // If channel is not null we are logged in to the chat view: if($this->getChannel() !== null) { // Check if the current user has been logged out due to inactivity: if(!$this->isUserOnline()) { $this->logout(); return; } if($this->getRequestVar('ajax')) { $this->initChannel(); $this->updateOnlineStatus(); $this->checkAndRemoveInactive(); } } else { if($this->getRequestVar('ajax')) { // Set channel, insert login messages and add to online list on first ajax request in chat view: $this->chatViewLogin(); } } } function isChatOpen() { if($this->getUserRole() == AJAX_CHAT_ADMIN) return true; if($this->getConfig('chatClosed')) return false; $time = time(); if($this->getConfig('timeZoneOffset') !== null) { // Subtract the server timezone offset and add the config timezone offset: $time -= date('Z', $time); $time += $this->getConfig('timeZoneOffset'); } // Check the opening hours: if($this->getConfig('openingHour') < $this->getConfig('closingHour')) { if(($this->getConfig('openingHour') > date('G', $time)) || ($this->getConfig('closingHour') <= date('G', $time))) return false; } else { if(($this->getConfig('openingHour') > date('G', $time)) && ($this->getConfig('closingHour') <= date('G', $time))) return false; } // Check the opening weekdays: if(!in_array(date('w', $time), $this->getConfig('openingWeekDays'))) return false; return true; } function handleRequest() { if($this->getRequestVar('ajax')) { if($this->isLoggedIn()) { // Parse info requests (for current userName, etc.): $this->parseInfoRequests(); // Parse command requests (e.g. message deletion): $this->parseCommandRequests(); // Parse message requests: $this->initMessageHandling(); } // Send chat messages and online user list in XML format: $this->sendXMLMessages(); } else { // Display XHTML content for non-ajax requests: $this->sendXHTMLContent(); } } function parseCommandRequests() { if($this->getRequestVar('delete') !== null) { $this->deleteMessage($this->getRequestVar('delete')); } } function parseInfoRequests() { if($this->getRequestVar('getInfos')) { $infoRequests = explode(',', $this->getRequestVar('getInfos')); foreach($infoRequests as $infoRequest) { $this->parseInfoRequest($infoRequest); } } } function parseInfoRequest($infoRequest) { switch($infoRequest) { case 'userID': $this->addInfoMessage($this->getUserID(), 'userID'); break; case 'userName': $this->addInfoMessage($this->getUserName(), 'userName'); break; case 'userRole': $this->addInfoMessage($this->getUserRole(), 'userRole'); break; case 'channelID': $this->addInfoMessage($this->getChannel(), 'channelID'); break; case 'channelName': $this->addInfoMessage($this->getChannelName(), 'channelName'); break; case 'socketRegistrationID': $this->addInfoMessage($this->getSocketRegistrationID(), 'socketRegistrationID'); break; default: $this->parseCustomInfoRequest($infoRequest); } } function sendXHTMLContent() { $httpHeader = new AJAXChatHTTPHeader($this->getConfig('contentEncoding'), $this->getConfig('contentType')); $template = new AJAXChatTemplate($this, $this->getTemplateFileName(), $httpHeader->getContentType()); // Send HTTP header: $httpHeader->send(); // Send parsed template content: echo $template->getParsedContent(); } function getTemplateFileName() { switch($this->getView()) { case 'chat': return AJAX_CHAT_PATH . '/template/loggedIn.html'; case 'logs': return AJAX_CHAT_PATH . '/template/logs.html'; case 'mobile': return AJAX_CHAT_PATH . '/template/mobile.html'; case 'legacy': return AJAX_CHAT_PATH . '/template/legacyLogin.html'; case 'banned': return AJAX_CHAT_PATH . '/template/banned.html'; case 'legacy1': return AJAX_CHAT_PATH . '/template/~loggedOut.html'; case 'legacy2': return AJAX_CHAT_PATH . '/template/loggedOut~.html'; case 'legacy3': return AJAX_CHAT_PATH . '/template/loggedOut.html'; default: return AJAX_CHAT_PATH . '/template/loggedOutFA.html'; } } function initView() { $this->_view = null; // "chat" is the default view: $view = ($this->getRequestVar('view') === null) ? 'chat' : $this->getRequestVar('view'); if($this->hasAccessTo($view)) { $this->_view = $view; } } function getView() { return $this->_view; } function hasAccessTo($view) { if(substr($view, 0, 6) === 'legacy') return !$this->isLoggedIn(); switch($view) { case 'legacy': if($this->isLoggedIn()) { return false; } return true; case 'banned': return true; case 'chat': case 'mobile': case 'teaser': if($this->isLoggedIn()) { return true; } return false; case 'logs': if($this->isLoggedIn() && ($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == CMOD || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == PURPLE || $this->getUserRole() == DONATOR || ($this->getConfig('logsUserAccess') && ($this->getUserRole() == AJAX_CHAT_USER)) )) { return true; } return false; default: return false; } } function login() { // Retrieve valid login user data (from request variables or session data): $userData = $this->getValidLoginUserData(); if(!$userData) { $this->addInfoMessage('errorInvalidUser'); return false; } // If the chat is closed, only the admin may login: if(!$this->isChatOpen() && $userData['userRole'] != AJAX_CHAT_ADMIN) { $this->addInfoMessage('errorChatClosed'); return false; } if(!$this->getConfig('allowGuestLogins') && $userData['userRole'] == AJAX_CHAT_GUEST) { return false; } // Check if userID or userName are already listed online: if($this->isUserOnline($userData['userID']) || $this->isUserNameInUse($userData['userName'])) { // Set the registered user inactive and remove the inactive users so the user can be logged in again: $this->setInactive($userData['userID'], $userData['userName']); $this->removeInactive(); } // Check if user is banned: if($userData['userRole'] != AJAX_CHAT_ADMIN && $this->isUserBanned($userData['userName'], $userData['userID'], $_SERVER['REMOTE_ADDR'])) { $this->addInfoMessage('errorBanned'); return false; } // Check if the max number of users is logged in (not affecting moderators or admins): if(!($userData['userRole'] == AJAX_CHAT_MODERATOR || $userData['userRole'] == PURPLE || $userData['userRole'] == AJAX_CHAT_ADMIN) && $this->isMaxUsersLoggedIn()) { $this->addInfoMessage('errorMaxUsersLoggedIn'); return false; } // Use a new session id (if session has been started by the chat): $this->regenerateSessionID(); // Log in: $this->setUserID($userData['userID']); $this->setUserName($userData['userName']); $this->setLoginUserName($userData['userName']); $this->setUserRole($userData['userRole']); $this->setLoggedIn(true); $this->setLoginTimeStamp(time()); // IP Security check variable: $this->setSessionIP($_SERVER['REMOTE_ADDR']); // The client authenticates to the socket server using a socketRegistrationID: if($this->getConfig('socketServerEnabled')) { $this->setSocketRegistrationID( md5(uniqid(rand(), true)) ); } // Add userID, userName and userRole to info messages: $this->addInfoMessage($this->getUserID(), 'userID'); $this->addInfoMessage($this->getUserName(), 'userName'); $this->addInfoMessage($this->getUserRole(), 'userRole'); // Purge logs: if($this->getConfig('logsPurgeLogs')) { $this->purgeLogs(); } // Report login to Satori: /*$boatcom = @fsockopen('marie.railgun.sh', 9064, $errno, $errstr, 5); if($boatcom) { $message = sprintf('{sock}[i][url=https://flashii.net/profile.php?u=%d][b]%s[/b][/url] logged into [url=https://flash.moe/chat/]Flashii Chat Legacy[/url].[/i]', $userData['userID'], $userData['userName']); $message = chr(0xF) . hash_hmac('sha256', $message, 'lol glad i caught this, that could\'ve been pretty funny if i forgot to remove this') . $message . chr(0xF); fwrite($boatcom, $message); fflush($boatcom); fclose($boatcom); }*/ return true; } function chatViewLogin() { $this->setChannel($this->getValidRequestChannelID()); $this->addToOnlineList(); // Add channelID and channelName to info messages: $this->addInfoMessage($this->getChannel(), 'channelID'); $this->addInfoMessage($this->getChannelName(), 'channelName'); // Login message: $text = '/login '.$this->getUserName(); $this->insertChatBotMessage( $this->getChannel(), $text, null, 1 ); } function getValidRequestChannelID() { $channelID = $this->getRequestVar('channelID'); $channelName = $this->getRequestVar('channelName'); // Check the given channelID, or get channelID from channelName: if($channelID === null) { if($channelName !== null) { $channelID = $this->getChannelIDFromChannelName($channelName); // channelName might need encoding conversion: if($channelID === null) { $channelID = $this->getChannelIDFromChannelName( $this->trimChannelName($channelName, $this->getConfig('contentEncoding')) ); } } } // Validate the resulting channelID: if(!$this->validateChannel($channelID)) { if($this->getChannel() !== null) { return $this->getChannel(); } return $this->getConfig('defaultChannelID'); } return $channelID; } function initChannel() { $channelID = $this->getRequestVar('channelID'); $channelName = $this->getRequestVar('channelName'); if($channelID !== null) { $this->switchChannel($this->getChannelNameFromChannelID($channelID)); } else if($channelName !== null) { if($this->getChannelIDFromChannelName($channelName) === null) { // channelName might need encoding conversion: $channelName = $this->trimChannelName($channelName, $this->getConfig('contentEncoding')); } $this->switchChannel($channelName); } } function logout($type=null) { // Update the socket server authentication for the user: if($this->getConfig('socketServerEnabled')) { $this->updateSocketAuthentication($this->getUserID()); } if($this->isUserOnline()) { $this->chatViewLogout($type); } $this->setLoggedIn(false); $this->destroySession(); // Re-initialize the view: $this->initView(); } function chatViewLogout($type) { $this->removeFromOnlineList(); if($type !== null) { $type = ' '.$type; } // Logout message $text = '/logout '.$this->getUserName().$type; $this->insertChatBotMessage( $this->getChannel(), $text, null, 1 ); } function switchChannel($channelName) { $channelID = $this->getChannelIDFromChannelName($channelName); if($channelID !== null && $this->getChannel() == $channelID) { // User is already in the given channel, return: return; } // Check if we have a valid channel: if(!$this->validateChannel($channelID)) { // Invalid channel: $text = '/error InvalidChannelName '.$channelName; $this->insertChatBotMessage( $this->getPrivateMessageID(), $text ); return; } $oldChannel = $this->getChannel(); $this->setChannel($channelID); $this->updateOnlineList(); // Channel leave message $text = '/channelLeave '.$this->getUserName(); $this->insertChatBotMessage( $oldChannel, $text, null, 1 ); // Channel enter message $text = '/channelEnter '.$this->getUserName(); $this->insertChatBotMessage( $this->getChannel(), $text, null, 1 ); $this->addInfoMessage($channelName, 'channelSwitch'); $this->addInfoMessage($channelID, 'channelID'); $this->_requestVars['lastID'] = 0; } function addToOnlineList() { $sql = 'INSERT INTO '.$this->getDataBaseTable('online').'( userID, userName, userRole, channel, dateTime, ip ) VALUES ( '.$this->db->makeSafe($this->getUserID()).', '.$this->db->makeSafe($this->getUserName()).', '.$this->db->makeSafe($this->getUserRole()).', '.$this->db->makeSafe($this->getChannel()).', NOW(), '.$this->db->makeSafe($this->ipToStorageFormat($_SERVER['REMOTE_ADDR'])).' );'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $this->resetOnlineUsersData(); } function removeFromOnlineList() { $sql = 'DELETE FROM '.$this->getDataBaseTable('online').' WHERE userID = '.$this->db->makeSafe($this->getUserID()).';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $this->removeUserFromOnlineUsersData(); } function updateOnlineList() { $sql = 'UPDATE '.$this->getDataBaseTable('online').' SET userName = '.$this->db->makeSafe($this->getUserName()).', channel = '.$this->db->makeSafe($this->getChannel()).', dateTime = NOW(), ip = '.$this->db->makeSafe($this->ipToStorageFormat($_SERVER['REMOTE_ADDR'])).' WHERE userID = '.$this->db->makeSafe($this->getUserID()).';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $this->resetOnlineUsersData(); } function initMessageHandling() { // Don't handle messages if we are not in chat view: if($this->getView() != 'chat') { return; } // Check if we have been uninvited from a private or restricted channel: if(!$this->validateChannel($this->getChannel())) { // Switch to the default channel: $this->switchChannel($this->getChannelNameFromChannelID($this->getConfig('defaultChannelID'))); return; } if($this->getRequestVar('text') !== null) { $this->insertMessage($this->getRequestVar('text')); } } function insertParsedMessage($text) { // Replace Unicode character $text = preg_replace("/\xEF\xBF\xBF/im", " ", $text); $gtParse = preg_replace('/\\[(?:\\/)?(\\w+)(?:=([^<>]*?))?\\]/ms', '', $text); // Auto greentext if(preg_match("/>(.*?)$/im", $gtParse)) { $text = preg_replace("/>(.*?)$/im", "[color=Green]>$1[/color]", $gtParse); } if(preg_match("/(.*?)<$/im", $gtParse)) { $text = preg_replace("/(.*?)<$/im", "[color=Red]$1<[/color]", $gtParse); } // Emoticon Limiting $emoticons = array(':happy:', ':lmao:', ':angry:', ':angrier:', ':evil:', ':glare:', ':eat:', ':lol:', ':dizzy:', ':yay:', ':wtf:', ':sigh:', ':omg:', ':ouch:', ':tired:', ':kiss:', ':love:', ':sweat:', ':suspicious:', ':crying:', ':blank:', ':puke:', ':ruse:', ':meow:'); $emoticonCount = 0; foreach($emoticons as $emoticon) { // Increase Emoticon count $emoticonCount += substr_count($text, $emoticon); // If higher than 10 replace then if($emoticonCount > 10) $text = str_replace($emoticon, '', $text); } if($this->getQueryUserName() !== null && strpos($text, '/') !== 0) { // If a queryUserName is set, sent all messages as private messages to this userName: $text = '/msg '.$this->getQueryUserName().' '.$text; } if(strpos($text, '/') === 0) { // Parse IRC-style commands: $textParts = explode(' ', $text); switch($textParts[0]) { // Channel switch: case '/join': $this->insertParsedMessageJoin($textParts); break; // Logout: case '/quit': case '/suicide': $this->logout(); break; // Private message: case '/msg': case '/describe': $this->insertParsedMessagePrivMsg($textParts); break; // Invitation: case '/invite': $this->insertParsedMessageInvite($textParts); break; // Uninvitation: case '/uninvite': $this->insertParsedMessageUninvite($textParts); break; // Private messaging: case '/query': $this->insertParsedMessageQuery($textParts); break; // Kicking offending users from the chat: case '/kick': $this->insertParsedMessageKick($textParts); break; // Listing banned users: case '/bans': $this->insertParsedMessageBans($textParts); break; // Unban user (remove from ban list): case '/unban': $this->insertParsedMessageUnban($textParts); break; // Describing actions: case '/me': case '/action': $this->insertParsedMessageAction($textParts); break; // Listing online Users: case '/who': $this->insertParsedMessageWho($textParts); break; // Listing available channels: case '/list': $this->insertParsedMessageList($textParts); break; // Retrieving the channel of a User: case '/whereis': $this->insertParsedMessageWhereis($textParts); break; // Listing information about a User: case '/whois': $this->insertParsedMessageWhois($textParts); break; // Rolling dice: case '/roll': $this->insertParsedMessageRoll($textParts); break; // Switching userName: case '/nick': $this->insertParsedMessageNick($textParts); break; // Switching userName: case '/saibatekuisagoodsite': $this->insertParsedMessageChangeNick($textParts); break; // Custom or unknown command: default: if(!$this->parseCustomCommands($text, $textParts)) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UnknownCommand '.$textParts[0] ); } } } else { // No command found, just insert the plain message: $this->insertCustomMessage( $this->getUserID(), $this->getUserName(), $this->getUserRole(), $this->getChannel(), $text ); } include_once 'satori.php'; } function insertParsedMessageJoin($textParts) { if(count($textParts) == 1) { // join with no arguments is the own private channel, if allowed: if($this->isAllowedToCreatePrivateChannel()) { // Private channels are identified by square brackets: $this->switchChannel($this->getChannelNameFromChannelID($this->getPrivateChannelID())); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingChannelName' ); } } else { $this->switchChannel($textParts[1]); } } function insertParsedMessagePrivMsg($textParts) { if($this->isAllowedToSendPrivateMessage()) { if(count($textParts) < 3) { if(count($textParts) == 2) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingText' ); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } } else { // Get UserID from UserName: $toUserID = $this->getIDFromName($textParts[1]); if($toUserID === null) { if($this->getQueryUserName() !== null) { // Close the current query: $this->insertMessage('/query'); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } } else { // Insert /privaction command if /describe is used: $command = ($textParts[0] == '/describe') ? '/privaction' : '/privmsg'; // Copy of private message to current User: $this->insertCustomMessage( $this->getUserID(), $this->getUserName(), $this->getUserRole(), $this->getPrivateMessageID(), $command.'to '.$textParts[1].' '.implode(' ', array_slice($textParts, 2)) ); // Private message to requested User: $this->insertCustomMessage( $this->getUserID(), $this->getUserName(), $this->getUserRole(), $this->getPrivateMessageID($toUserID), $command.' '.implode(' ', array_slice($textParts, 2)) ); } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error PrivateMessageNotAllowed' ); } } function insertParsedMessageInvite($textParts) { if($this->getChannel() == $this->getPrivateChannelID() || in_array($this->getChannel(), $this->getChannels())) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { $toUserID = $this->getIDFromName($textParts[1]); if($toUserID === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { // Add the invitation to the database: $this->addInvitation($toUserID); $invitationChannelName = $this->getChannelNameFromChannelID($this->getChannel()); // Copy of invitation to current User: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/inviteto '.$textParts[1].' '.$invitationChannelName ); // Invitation to requested User: $this->insertChatBotMessage( $this->getPrivateMessageID($toUserID), '/invite '.$this->getUserName().' '.$invitationChannelName ); } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error InviteNotAllowed' ); } } function insertParsedMessageUninvite($textParts) { if($this->getChannel() == $this->getPrivateChannelID() || in_array($this->getChannel(), $this->getChannels())) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { $toUserID = $this->getIDFromName($textParts[1]); if($toUserID === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { // Remove the invitation from the database: $this->removeInvitation($toUserID); $invitationChannelName = $this->getChannelNameFromChannelID($this->getChannel()); // Copy of uninvitation to current User: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/uninviteto '.$textParts[1].' '.$invitationChannelName ); // Uninvitation to requested User: $this->insertChatBotMessage( $this->getPrivateMessageID($toUserID), '/uninvite '.$this->getUserName().' '.$invitationChannelName ); } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UninviteNotAllowed' ); } } function insertParsedMessageQuery($textParts) { if($this->isAllowedToSendPrivateMessage()) { if(count($textParts) == 1) { if($this->getQueryUserName() !== null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/queryClose '.$this->getQueryUserName() ); // Close the current query: $this->setQueryUserName(null); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error NoOpenQuery' ); } } else { if($this->getIDFromName($textParts[1]) === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { if($this->getQueryUserName() !== null) { // Close the current query: $this->insertMessage('/query'); } // Open a query to the requested user: $this->setQueryUserName($textParts[1]); $this->insertChatBotMessage( $this->getPrivateMessageID(), '/queryOpen '.$textParts[1] ); } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error PrivateMessageNotAllowed' ); } } function insertParsedMessageKick($textParts) { // Only moderators/admins may kick users: if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == CMOD || $this->getUserID() == 11) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { // Get UserID from UserName: $kickUserID = $this->getIDFromName($textParts[1]); if($kickUserID === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { // Check the role of the user to kick: $kickUserRole = $this->getRoleFromID($kickUserID); if( $kickUserRole == AJAX_CHAT_ADMIN || ( ( $kickUserID == 11 && $this->getUserRole() != AJAX_CHAT_ADMIN && $this->getUserID() != 11 ) || ( $kickUserRole == CMOD && $this->getUserRole() != AJAX_CHAT_MODERATOR && $this->getUserRole() != PURPLE && $this->getUserRole() != AJAX_CHAT_ADMIN && $this->getUserID() != 11 ) || ( $kickUserRole == AJAX_CHAT_MODERATOR && $this->getUserRole() != PURPLE && $this->getUserRole() != AJAX_CHAT_ADMIN && $this->getUserID() != 11 ) ) ) { // Admins and moderators may not be kicked: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error KickNotAllowed '.$textParts[1] ); } else { // Kick user and insert message: $channel = $this->getChannelFromID($kickUserID); $banMinutes = (count($textParts) > 2) ? $textParts[2] : null; $this->kickUser($textParts[1], $banMinutes, $kickUserID); // If no channel found, user logged out before he could be kicked if($channel !== null) { $this->insertChatBotMessage( $channel, '/kick '.$textParts[1], null, 1 ); // Send a copy of the message to the current user, if not in the channel: if($channel != $this->getChannel()) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/kick '.$textParts[1], null, 1 ); } } } } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error CommandNotAllowed '.$textParts[0] ); } } function insertParsedMessageBans($textParts) { // Only moderators/admins may see the list of banned users: if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == CMOD) { $this->removeExpiredBans(); $bannedUsers = $this->getBannedUsers(); if(count($bannedUsers) > 0) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/bans '.implode(' ', $bannedUsers) ); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/bansEmpty -' ); } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error CommandNotAllowed '.$textParts[0] ); } } function insertParsedMessageUnban($textParts) { // Only moderators/admins may unban users: if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == CMOD) { $this->removeExpiredBans(); if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { if(!in_array($textParts[1], $this->getBannedUsers())) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { // Unban user and insert message: $this->unbanUser($textParts[1]); $this->insertChatBotMessage( $this->getPrivateMessageID(), '/unban '.$textParts[1] ); } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error CommandNotAllowed '.$textParts[0] ); } } function insertParsedMessageAction($textParts) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingText' ); } else { if($this->getQueryUserName() !== null) { // If we are in query mode, sent the action to the query user: $this->insertMessage('/describe '.$this->getQueryUserName().' '.implode(' ', array_slice($textParts, 1))); } else { $this->insertCustomMessage( $this->getUserID(), $this->getUserName(), $this->getUserRole(), $this->getChannel(), implode(' ', $textParts) ); } } } function insertParsedMessageWho($textParts) { if(count($textParts) == 1) { if($this->isAllowedToListHiddenUsers()) { // List online users from any channel: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/who '.implode(' ', $this->getOnlineUsers()) ); } else { // Get online users for all accessible channels: $channels = $this->getChannels(); // Add the own private channel if allowed: if($this->isAllowedToCreatePrivateChannel()) { array_push($channels, $this->getPrivateChannelID()); } // Add the invitation channels: foreach($this->getInvitations() as $channelID) { if(!in_array($channelID, $channels)) { array_push($channels, $channelID); } } $this->insertChatBotMessage( $this->getPrivateMessageID(), '/who '.implode(' ', $this->getOnlineUsers($channels)) ); } } else { $channelName = $textParts[1]; $channelID = $this->getChannelIDFromChannelName($channelName); if(!$this->validateChannel($channelID)) { // Invalid channel: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error InvalidChannelName '.$channelName ); } else { // Get online users for the given channel: $onlineUsers = $this->getOnlineUsers(array($channelID)); if(count($onlineUsers) > 0) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/whoChannel '.$channelName.' '.implode(' ', $onlineUsers) ); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/whoEmpty -' ); } } } } function insertParsedMessageList($textParts) { // Get the names of all accessible channels: $channelNames = $this->getChannelNames(); // Add the own private channel, if allowed: if($this->isAllowedToCreatePrivateChannel()) { array_push($channelNames, $this->getPrivateChannelName()); } // Add the invitation channels: foreach($this->getInvitations() as $channelID) { $channelName = $this->getChannelNameFromChannelID($channelID); if($channelName !== null && !in_array($channelName, $channelNames)) { array_push($channelNames, $channelName); } } $this->insertChatBotMessage( $this->getPrivateMessageID(), '/list '.implode(' ', $channelNames) ); } function insertParsedMessageWhereis($textParts) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { // Get UserID from UserName: $whereisUserID = $this->getIDFromName($textParts[1]); if($whereisUserID === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { $channelID = $this->getChannelFromID($whereisUserID); if($this->validateChannel($channelID)) { $channelName = $this->getChannelNameFromChannelID($channelID); } else { $channelName = null; } if($channelName === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { // List user information: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/whereis '.$textParts[1].' '.$channelName ); } } } } function insertParsedMessageWhois($textParts) { // Only moderators/admins: if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == PURPLE || $this->getUserRole() == CMOD) { // if($this->getUserRole() == AJAX_CHAT_ADMIN) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { // Get UserID from UserName: $whoisUserID = $this->getIDFromName($textParts[1]); if($whoisUserID === null) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameNotFound '.$textParts[1] ); } else { // List user information: $this->insertChatBotMessage( $this->getPrivateMessageID(), '/whois '.$textParts[1].' '.$this->getIPFromID($whoisUserID) ); } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error CommandNotAllowed '.$textParts[0] ); } } function insertParsedMessageRoll($textParts) { if(count($textParts) == 1) { // default is one d6: $text = '/roll '.$this->getUserName().' 1d6 '.$this->rollDice(6); } else { $diceParts = explode('d', $textParts[1]); if(count($diceParts) == 2) { $number = (int)$diceParts[0]; $sides = (int)$diceParts[1]; // Dice number must be an integer between 1 and 100, else roll only one: $number = ($number > -1 && $number < 100) ? $number : 1; // Sides must be an integer between 1 and 100, else take 6: $sides = ($sides > -1 && $sides < 100) ? $sides : 6; $text = '/roll '.$this->getUserName().' '.$number.'d'.$sides.' '; for($i=0; $i<$number; $i++) { if($i != 0) $text .= ','; switch($this->getUserID()) { // snowflake case 3://303: $text .= str_shuffle('bird'); break; case 0://230: $text .= str_shuffle('portugal'); break; case 15://21: $text .= str_shuffle('divorce'); break; case 2://8: $text .= str_shuffle('mewow'); break; default: $text .= $this->rollDice($sides); } } } else { // if dice syntax is invalid, roll one d6: $text = '/roll '.$this->getUserName().' 1d6 '.$this->rollDice(6); } } $this->insertChatBotMessage( $this->getChannel(), $text ); } function insertParsedMessageNick($textParts) { if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == PURPLE || $this->getUserRole() == CMOD || $this->getUserRole() == BOTS || $this->getUserRole() == DONATOR) { if(!$this->getConfig('allowNickChange') || (!$this->getConfig('allowGuestUserName') && $this->getUserRole() == AJAX_CHAT_GUEST)) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error CommandNotAllowed '.$textParts[0] ); } elseif(count($textParts) == 1) { $oldUserName = $this->getUserName(); $newUserName = $this->getLoginUserName(); if($oldUserName != $newUserName) { $this->setUserName($newUserName); $this->updateOnlineList(); // Add info message to update the client-side stored userName: $this->addInfoMessage($this->getUserName(), 'userName'); $this->insertChatBotMessage( $this->getChannel(), '/nick '.$oldUserName.' '.$newUserName, null, 2 ); } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } } else { $newUserName = implode(' ', array_slice($textParts, 1)); if($newUserName == $this->getLoginUserName()) { // Allow the user to regain the original login userName: $prefix = ''; $suffix = ''; } else if($this->getUserRole() == AJAX_CHAT_GUEST) { $prefix = $this->getConfig('guestUserPrefix'); $suffix = $this->getConfig('guestUserSuffix'); } else { $prefix = $this->getConfig('changedNickPrefix'); $suffix = $this->getConfig('changedNickSuffix'); } $maxLength = $this->getConfig('userNameMaxLength') - $this->stringLength($prefix) - $this->stringLength($suffix); $newUserName = $this->trimString($newUserName, 'UTF-8', $maxLength, true); if(!$newUserName) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error InvalidUserName' ); } else { $newUserName = $prefix.$newUserName.$suffix; if($this->isUserNameInUse($newUserName)) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error UserNameInUse' ); } else { $oldUserName = $this->getUserName(); $this->setUserName($newUserName); $this->updateOnlineList(); // Add info message to update the client-side stored userName: $this->addInfoMessage($this->getUserName(), 'userName'); $this->insertChatBotMessage( $this->getChannel(), '/nick '.$oldUserName.' '.$newUserName, null, 2 ); } } } } else { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error CommandNotAllowed '.$textParts[0] ); } } function insertParsedMessageChangeNick($textParts) { if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { if(count($textParts) == 1) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MissingUserName' ); } else { $newUserName = implode(' ', array_slice($textParts, 1)); $oldUserName = $this->getUserName(); $this->setUserName($newUserName); $this->updateOnlineList(); $this->addInfoMessage($this->getUserName(), 'userName'); } } } function insertMessage($text) { if(!$this->isAllowedToWriteMessage()) return; if(!$this->floodControl()) return; $text = $this->trimMessageText($text); if($text == '') return; if(!$this->onNewMessage($text)) return; $text = $this->replaceCustomText($text); $this->insertParsedMessage($text); } function deleteMessage($messageID) { // Retrieve the channel of the given message: $sql = 'SELECT channel FROM '.$this->getDataBaseTable('messages').' WHERE id='.$this->db->makeSafe($messageID).';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $row = $result->fetch(); if($row['channel'] !== null) { $channel = $row['channel']; if($this->getUserRole() == AJAX_CHAT_ADMIN) { $condition = ''; } else if($this->getUserRole() == CMOD || $this->getUserRole() == PURPLE) { $condition = ' AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_MODERATOR).') AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).')'; } else if($this->getUserRole() == AJAX_CHAT_MODERATOR) { $condition = ' AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).')'; } else if($this->getUserRole() == AJAX_CHAT_USER && $this->getConfig('allowUserMessageDelete')) { $condition = 'AND ( userID='.$this->db->makeSafe($this->getUserID()).' OR ( channel = '.$this->db->makeSafe($this->getPrivateMessageID()).' OR channel = '.$this->db->makeSafe($this->getPrivateChannelID()).' ) AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') AND NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).') )'; } else { return false; } // Remove given message from the database: $sql = 'DELETE FROM '.$this->getDataBaseTable('messages').' WHERE id='.$this->db->makeSafe($messageID).' '.$condition.';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } if($result->affectedRows() == 1) { // Insert a deletion command to remove the message from the clients chatlists: $this->insertChatBotMessage($channel, '/delete '.$messageID); return true; } } return false; } function floodControl() { // Admins can do whatever the fuck they want: if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserID() == 11) { return true; } $time = time(); // Check the time of the last inserted message: if($this->getInsertedMessagesRateTimeStamp()+60 < $time) { $this->setInsertedMessagesRateTimeStamp($time); $this->setInsertedMessagesRate(1); } else { // Increase the inserted messages rate: $rate = $this->getInsertedMessagesRate()+1; $this->setInsertedMessagesRate($rate); // Check if message rate is too high: if($rate > $this->getConfig('maxMessageRate')) { $this->insertChatBotMessage( $this->getPrivateMessageID(), '/error MaxMessageRate' ); $this->kickUser($this->getUserName(), 10, $this->getUserID()); $this->insertChatBotMessage($this->getChannel(), "[i][b]".$this->getUsername()."[/b] exceeded the message limit and will now be kicked for 10 minutes.[/i]"); $this->insertChatBotMessage($this->getChannel(), "/kick ". $this->getUsername()); // Return false so the message is not inserted: return false; } } return true; } function isAllowedToWriteMessage() { if($this->getUserRole() != AJAX_CHAT_GUEST) return true; if($this->getConfig('allowGuestWrite')) return true; return false; } function insertChatBotMessage($channelID, $messageText, $ip=null, $mode=0) { $this->insertCustomMessage( $this->getConfig('chatBotID'), $this->getConfig('chatBotName'), AJAX_CHAT_CHATBOT, $channelID, $messageText, $ip, $mode ); } function insertCustomMessage($userID, $userName, $userRole, $channelID, $text, $ip=null, $mode=0) { // The $mode parameter is used for socket updates: // 0 = normal messages // 1 = channel messages (e.g. login/logout, channel enter/leave, kick) // 2 = messages with online user updates (nick) $ip = $ip ? $ip : $_SERVER['REMOTE_ADDR']; $sql = 'INSERT INTO '.$this->getDataBaseTable('messages').'( userID, userName, userRole, channel, dateTime, ip, text ) VALUES ( '.$this->db->makeSafe($userID).', '.$this->db->makeSafe($userName).', '.$this->db->makeSafe($userRole).', '.$this->db->makeSafe($channelID).', NOW(), '.$this->db->makeSafe($this->ipToStorageFormat($ip)).', '.$this->db->makeSafe($text).' );'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } if($this->getConfig('socketServerEnabled')) { $this->sendSocketMessage( $this->getSocketBroadcastMessage( $this->db->getLastInsertedID(), time(), $userID, $userName, $userRole, $channelID, $text, $mode ) ); } } function getSocketBroadcastMessage( $messageID, $timeStamp, $userID, $userName, $userRole, $channelID, $text, $mode ) { // The $mode parameter: // 0 = normal messages // 1 = channel messages (e.g. login/logout, channel enter/leave, kick) // 2 = messages with online user updates (nick) // Get the message XML content: $xml = ''; if($mode) { // Add the list of online users if the user list has been updated ($mode > 0): $xml .= $this->getChatViewOnlineUsersXML(array($channelID)); } if($mode != 1 || $this->getConfig('showChannelMessages')) { $xml .= ''; $xml .= $this->getChatViewMessageXML( $messageID, $timeStamp, $userID, $userName, $userRole, $channelID, $text ); $xml .= ''; } $xml .= ''; return $xml; } function sendSocketMessage($message) { // Open a TCP socket connection to the socket server: if($socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) { if(@socket_connect($socket, $this->getConfig('socketServerIP'), $this->getConfig('socketServerPort'))) { // Append a null-byte to the string as EOL (End Of Line) character // which is required by Flash XML socket communication: $message .= "\0"; @socket_write( $socket, $message, strlen($message) // Using strlen to count the bytes instead of the number of UTF-8 characters ); } @socket_close($socket); } } function updateSocketAuthentication($userID, $socketRegistrationID=null, $channels=null) { // If no $socketRegistrationID or no $channels are given the authentication is removed for the given user: $authentication = ''; if($channels) { foreach($channels as $channelID) { $authentication .= ''; } } $authentication .= ''; $this->sendSocketMessage($authentication); } function setSocketRegistrationID($value) { $this->setSessionVar('SocketRegistrationID', $value); } function getSocketRegistrationID() { return $this->getSessionVar('SocketRegistrationID'); } function rollDice($sides) { // seed with microseconds since last "whole" second: //mt_srand((double)microtime()*1000000); return mt_rand(1, $sides); } function kickUser($userName, $banMinutes=null, $userID=null) { if($userID === null) { $userID = $this->getIDFromName($userName); } if($userID === null) { return; } $banMinutes = ($banMinutes !== null) ? $banMinutes : $this->getConfig('defaultBanTime'); if($banMinutes) { // Ban User for the given time in minutes: $this->banUser($userName, $banMinutes, $userID); } // Remove given User from online list: $sql = 'DELETE FROM '.$this->getDataBaseTable('online').' WHERE userID = '.$this->db->makeSafe($userID).';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } // Update the socket server authentication for the kicked user: if($this->getConfig('socketServerEnabled')) { $this->updateSocketAuthentication($userID); } $this->removeUserFromOnlineUsersData($userID); } function getBannedUsersData($key=null, $value=null) { if($this->_bannedUsersData === null) { $this->_bannedUsersData = array(); $sql = 'SELECT userID, userName, ip FROM '.$this->getDataBaseTable('bans').' WHERE NOW() < dateTime;'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } while($row = $result->fetch()) { $row['ip'] = $this->ipFromStorageFormat($row['ip']); array_push($this->_bannedUsersData, $row); } $result->free(); } if($key) { $bannedUsersData = array(); foreach($this->_bannedUsersData as $bannedUserData) { if(!isset($bannedUserData[$key])) { return $bannedUsersData; } if($value) { if($bannedUserData[$key] == $value) { array_push($bannedUsersData, $bannedUserData); } else { continue; } } else { array_push($bannedUsersData, $bannedUserData[$key]); } } return $bannedUsersData; } return $this->_bannedUsersData; } function getBannedUsers() { return $this->getBannedUsersData('userName'); } function banUser($userName, $banMinutes=null, $userID=null) { if($userID === null) { $userID = $this->getIDFromName($userName); } $ip = $this->getIPFromID($userID); if(!$ip || $userID === null) { return; } // Remove expired bans: $this->removeExpiredBans(); $banMinutes = (int)$banMinutes; if(!$banMinutes) { // If banMinutes is not a valid integer, use the defaultBanTime: $banMinutes = $this->getConfig('defaultBanTime'); } $sql = 'INSERT INTO '.$this->getDataBaseTable('bans').'( userID, userName, dateTime, ip ) VALUES ( '.$this->db->makeSafe($userID).', '.$this->db->makeSafe($userName).', DATE_ADD(NOW(), interval '.$this->db->makeSafe($banMinutes).' MINUTE), '.$this->db->makeSafe($this->ipToStorageFormat($ip)).' );'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function unbanUser($userName) { $sql = 'DELETE FROM '.$this->getDataBaseTable('bans').' WHERE userName = '.$this->db->makeSafe($userName).';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function removeExpiredBans() { $sql = 'DELETE FROM '.$this->getDataBaseTable('bans').' WHERE dateTime < NOW();'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function setInactive($userID, $userName=null) { $condition = 'userID='.$this->db->makeSafe($userID); if($userName !== null) { $condition .= ' OR userName='.$this->db->makeSafe($userName); } $sql = 'UPDATE '.$this->getDataBaseTable('online').' SET dateTime = DATE_SUB(NOW(), interval '.(intval($this->getConfig('inactiveTimeout'))+1).' MINUTE) WHERE '.$condition.';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $this->resetOnlineUsersData(); } function removeInactive() { $sql = 'SELECT userID, userName, channel FROM '.$this->getDataBaseTable('online').' WHERE NOW() > DATE_ADD(dateTime, interval '.$this->getConfig('inactiveTimeout').' MINUTE);'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } if($result->numRows() > 0) { $condition = ''; while($row = $result->fetch()) { if(!empty($condition)) $condition .= ' OR '; // Add userID to condition for removal: $condition .= 'userID='.$this->db->makeSafe($row['userID']); // Update the socket server authentication for the kicked user: if($this->getConfig('socketServerEnabled')) { $this->updateSocketAuthentication($row['userID']); } $this->removeUserFromOnlineUsersData($row['userID']); // Insert logout timeout message: $text = '/logout '.$row['userName'].' Timeout'; $this->insertChatBotMessage( $row['channel'], $text, null, 1 ); } $result->free(); $sql = 'DELETE FROM '.$this->getDataBaseTable('online').' WHERE '.$condition.';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } } function updateOnlineStatus() { // Update online status every 50 seconds (this allows update requests to be in time): if(!$this->getStatusUpdateTimeStamp() || ((time() - $this->getStatusUpdateTimeStamp()) > 50)) { $this->updateOnlineList(); $this->setStatusUpdateTimeStamp(time()); } } function checkAndRemoveInactive() { // Remove inactive users every inactiveCheckInterval: if(!$this->getInactiveCheckTimeStamp() || ((time() - $this->getInactiveCheckTimeStamp()) > $this->getConfig('inactiveCheckInterval')*60)) { $this->removeInactive(); $this->setInactiveCheckTimeStamp(time()); } } function sendXMLMessages() { $httpHeader = new AJAXChatHTTPHeader('UTF-8', 'text/xml'); // Send HTTP header: $httpHeader->send(); // Output XML messages: echo $this->getXMLMessages(); } function getXMLMessages() { switch($this->getView()) { case 'chat': return $this->getChatViewXMLMessages(); case 'teaser': return $this->getTeaserViewXMLMessages(); case 'logs': return $this->getLogsViewXMLMessages(); default: return $this->getLogoutXMLMessage(); } } function getMessageCondition() { $condition = 'id > '.$this->db->makeSafe($this->getRequestVar('lastID')).' AND ( channel = '.$this->db->makeSafe($this->getChannel()).' OR channel = '.$this->db->makeSafe($this->getPrivateMessageID()).' ) AND '; if($this->getConfig('requestMessagesPriorChannelEnter') || ($this->getConfig('requestMessagesPriorChannelEnterList') && in_array($this->getChannel(), $this->getConfig('requestMessagesPriorChannelEnterList')))) { $condition .= 'NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('requestMessagesTimeDiff').' HOUR)'; } else { $condition .= 'dateTime >= \''.date('Y-m-d H:i:s', $this->getChannelEnterTimeStamp()).'\''; } return $condition; } function getMessageFilter() { $filterChannelMessages = ''; if(!$this->getConfig('showChannelMessages') || $this->getRequestVar('shoutbox')) { $filterChannelMessages = ' AND NOT ( text LIKE (\'/login%\') OR text LIKE (\'/logout%\') OR text LIKE (\'/channelEnter%\') OR text LIKE (\'/channelLeave%\') OR text LIKE (\'/kick%\') )'; } return $filterChannelMessages; } function getInfoMessagesXML() { $xml = ''; // Go through the info messages: foreach($this->getInfoMessages() as $type=>$infoArray) { foreach($infoArray as $info) { $xml .= ''; $xml .= 'encodeSpecialChars($info).']]>'; $xml .= ''; } } $xml .= ''; return $xml; } function getChatViewOnlineUsersXML($channelIDs) { // Get the online users for the given channels: $onlineUsersData = $this->getOnlineUsersData($channelIDs); $xml = ''; foreach($onlineUsersData as $onlineUserData) { $xml .= 'encodeSpecialChars($onlineUserData['userName']).']]>'; $xml .= ''; } $xml .= ''; return $xml; } function getLogoutXMLMessage() { $xml = ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= 'encodeSpecialChars($this->getConfig('logoutData')).']]>'; $xml .= ''; $xml .= ''; $xml .= ''; return $xml; } function getChatViewMessageXML( $messageID, $timeStamp, $userID, $userName, $userRole, $channelID, $text ) { $message = 'encodeSpecialChars($userName).']]>'; $message .= 'encodeSpecialChars($text).']]>'; $message .= ''; return $message; } function getChatViewMessagesXML() { // Get the last messages in descending order (this optimises the LIMIT usage): $sql = 'SELECT id, userID, userName, userRole, channel AS channelID, UNIX_TIMESTAMP(dateTime) AS timeStamp, text FROM '.$this->getDataBaseTable('messages').' WHERE '.$this->getMessageCondition().' '.$this->getMessageFilter().' ORDER BY id DESC LIMIT '.$this->getConfig('requestMessagesLimit').';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $messages = ''; // Add the messages in reverse order so it is ascending again: while($row = $result->fetch()) { $message = $this->getChatViewMessageXML( $row['id'], $row['timeStamp'], $row['userID'], $row['userName'], $row['userRole'], $row['channelID'], $row['text'] ); $messages = $message.$messages; } $result->free(); $messages = ''.$messages.''; return $messages; } function getChatViewXMLMessages() { $xml = ''; $xml .= ''; $xml .= $this->getInfoMessagesXML(); $xml .= $this->getChatViewOnlineUsersXML(array($this->getChannel())); $xml .= $this->getChatViewMessagesXML(); $xml .= ''; return $xml; } function getTeaserMessageCondition() { $channelID = $this->getValidRequestChannelID(); $condition = 'channel = '.$this->db->makeSafe($channelID).' AND '; if($this->getConfig('requestMessagesPriorChannelEnter') || ($this->getConfig('requestMessagesPriorChannelEnterList') && in_array($channelID, $this->getConfig('requestMessagesPriorChannelEnterList')))) { $condition .= 'NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('requestMessagesTimeDiff').' HOUR)'; } else { // Teaser content may not be shown for this channel: $condition .= '0 = 1'; } return $condition; } function getTeaserViewMessagesXML() { // Get the last messages in descending order (this optimises the LIMIT usage): $sql = 'SELECT id, userID, userName, userRole, channel AS channelID, UNIX_TIMESTAMP(dateTime) AS timeStamp, text FROM '.$this->getDataBaseTable('messages').' WHERE '.$this->getTeaserMessageCondition().' '.$this->getMessageFilter().' ORDER BY id DESC LIMIT '.$this->getConfig('requestMessagesLimit').';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $messages = ''; // Add the messages in reverse order so it is ascending again: while($row = $result->fetch()) { $message = ''; $message .= 'encodeSpecialChars($row['userName']).']]>'; $message .= 'encodeSpecialChars($row['text']).']]>'; $message .= ''; $messages = $message.$messages; } $result->free(); $messages = ''.$messages.''; return $messages; } function getTeaserViewXMLMessages() { $xml = ''; $xml .= ''; $xml .= $this->getInfoMessagesXML(); $xml .= $this->getTeaserViewMessagesXML(); $xml .= ''; return $xml; } function getLogsViewCondition() { $condition = 'id > '.$this->db->makeSafe($this->getRequestVar('lastID')); // Check the channel condition: switch($this->getRequestVar('channelID')) { case '-3': // Just display messages from all accessible channels if($this->getUserRole() != AJAX_CHAT_ADMIN) { $condition .= ' AND (channel = '.$this->db->makeSafe($this->getPrivateMessageID()); $condition .= ' OR channel = '.$this->db->makeSafe($this->getPrivateChannelID()); foreach($this->getChannels() as $channel) { if($this->getConfig('logsUserAccessChannelList') && !in_array($channel, $this->getConfig('logsUserAccessChannelList'))) { continue; } $condition .= ' OR channel = '.$this->db->makeSafe($channel); } $condition .= ')'; } break; case '-2': if($this->getUserRole() != AJAX_CHAT_ADMIN) { $condition .= ' AND channel = '.($this->getPrivateMessageID()); } else { $condition .= ' AND channel > '.($this->getConfig('privateMessageDiff')-1); } break; case '-1': if($this->getUserRole() != AJAX_CHAT_ADMIN) { $condition .= ' AND channel = '.($this->getPrivateChannelID()); } else { $condition .= ' AND (channel > '.($this->getConfig('privateChannelDiff')-1).' AND channel < '.($this->getConfig('privateMessageDiff')).')'; } break; default: if(($this->getUserRole() == AJAX_CHAT_ADMIN || !$this->getConfig('logsUserAccessChannelList') || in_array($this->getRequestVar('channelID'), $this->getConfig('logsUserAccessChannelList'))) && $this->validateChannel($this->getRequestVar('channelID'))) { $condition .= ' AND channel = '.$this->db->makeSafe($this->getRequestVar('channelID')); } else { // No valid channel: $condition .= ' AND 0 = 1'; } } // Check the period condition: $hour = ($this->getRequestVar('hour') === null || $this->getRequestVar('hour') > 23 || $this->getRequestVar('hour') < 0) ? null : $this->getRequestVar('hour'); $day = ($this->getRequestVar('day') === null || $this->getRequestVar('day') > 31 || $this->getRequestVar('day') < 1) ? null : $this->getRequestVar('day'); $month = ($this->getRequestVar('month') === null || $this->getRequestVar('month') > 12 || $this->getRequestVar('month') < 1) ? null : $this->getRequestVar('month'); $year = ($this->getRequestVar('year') === null || $this->getRequestVar('year') > date('Y') || $this->getRequestVar('year') < $this->getConfig('logsFirstYear')) ? null : $this->getRequestVar('year'); // If a time (hour) is given but no date (year, month, day), use the current date: if($hour !== null) { if($day === null) $day = date('j'); if($month === null) $month = date('n'); if($year === null) $year = date('Y'); } if($year === null) { // No year given, so no period condition } else if($month === null) { // Define the given year as period: $periodStart = mktime(0, 0, 0, 1, 1, $year); // The last day in a month can be expressed by using 0 for the day of the next month: $periodEnd = mktime(23, 59, 59, 13, 0, $year); } else if($day === null) { // Define the given month as period: $periodStart = mktime(0, 0, 0, $month, 1, $year); // The last day in a month can be expressed by using 0 for the day of the next month: $periodEnd = mktime(23, 59, 59, $month+1, 0, $year); } else if($hour === null){ // Define the given day as period: $periodStart = mktime(0, 0, 0, $month, $day, $year); $periodEnd = mktime(23, 59, 59, $month, $day, $year); } else { // Define the given hour as period: $periodStart = mktime($hour, 0, 0, $month, $day, $year); $periodEnd = mktime($hour, 59, 59, $month, $day, $year); } if(isset($periodStart)) $condition .= ' AND dateTime > \''.date('Y-m-d H:i:s', $periodStart).'\' AND dateTime <= \''.date('Y-m-d H:i:s', $periodEnd).'\''; // Check the search condition: if($this->getRequestVar('search')) { if(($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == PURPLE || $this->getUserRole() == CMOD) && strpos($this->getRequestVar('search'), 'ip=') === 0) { // Search for messages with the given IP: $ip = substr($this->getRequestVar('search'), 3); $condition .= ' AND (ip = '.$this->db->makeSafe($this->ipToStorageFormat($ip)).')'; } else if(strpos($this->getRequestVar('search'), 'userID=') === 0) { // Search for messages with the given userID: $userID = substr($this->getRequestVar('search'), 7); $condition .= ' AND (userID = '.$this->db->makeSafe($userID).')'; } else { // Use the search value as regular expression on message text and username: $condition .= ' AND (userName REGEXP '.$this->db->makeSafe($this->getRequestVar('search')).' OR text REGEXP '.$this->db->makeSafe($this->getRequestVar('search')).')'; } } // If no period or search condition is given, just monitor the last messages on the given channel: if(!isset($periodStart) && !$this->getRequestVar('search')) { $condition .= ' AND NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('logsRequestMessagesTimeDiff').' HOUR)'; } return $condition; } function getLogsViewMessagesXML() { $sql = 'SELECT id, userID, userName, userRole, channel AS channelID, UNIX_TIMESTAMP(dateTime) AS timeStamp, ip, text FROM '.$this->getDataBaseTable('messages').' WHERE '.$this->getLogsViewCondition().' ORDER BY id LIMIT '.$this->getConfig('logsRequestMessagesLimit').';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } $xml = ''; while($row = $result->fetch()) { $xml .= 'getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == PURPLE) { $xml .= ' ip="'.$this->ipFromStorageFormat($row['ip']).'"'; } $xml .= '>'; $xml .= 'encodeSpecialChars($row['userName']).']]>'; $xml .= 'encodeSpecialChars($row['text']).']]>'; $xml .= ''; } $result->free(); $xml .= ''; return $xml; } function getLogsViewXMLMessages() { $xml = ''; $xml .= ''; $xml .= $this->getInfoMessagesXML(); $xml .= $this->getLogsViewMessagesXML(); $xml .= ''; return $xml; } function purgeLogs() { $sql = 'DELETE FROM '.$this->getDataBaseTable('messages').' WHERE dateTime < DATE_SUB(NOW(), interval '.$this->getConfig('logsPurgeTimeDiff').' DAY);'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function getInfoMessages($type=null) { if(!isset($this->_infoMessages)) { $this->_infoMessages = array(); } if($type) { if(!isset($this->_infoMessages[$type])) { $this->_infoMessages[$type] = array(); } return $this->_infoMessages[$type]; } else { return $this->_infoMessages; } } function addInfoMessage($info, $type='error') { if(!isset($this->_infoMessages)) { $this->_infoMessages = array(); } if(!isset($this->_infoMessages[$type])) { $this->_infoMessages[$type] = array(); } if(!in_array($info, $this->_infoMessages[$type])) { array_push($this->_infoMessages[$type], $info); } } function getRequestVars() { return $this->_requestVars; } function getRequestVar($key) { if($this->_requestVars && isset($this->_requestVars[$key])) { return $this->_requestVars[$key]; } return null; } function setRequestVar($key, $value) { if(!$this->_requestVars) { $this->_requestVars = array(); } $this->_requestVars[$key] = $value; } function getOnlineUsersData($channelIDs=null, $key=null, $value=null) { if($this->_onlineUsersData === null) { $this->_onlineUsersData = array(); $sql = 'SELECT userID, userName, userRole, channel, UNIX_TIMESTAMP(dateTime) AS timeStamp, ip FROM '.$this->getDataBaseTable('online').' ORDER BY userName;'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } while($row = $result->fetch()) { $row['ip'] = $this->ipFromStorageFormat($row['ip']); array_push($this->_onlineUsersData, $row); } $result->free(); } if($channelIDs || $key) { $onlineUsersData = array(); foreach($this->_onlineUsersData as $userData) { if($channelIDs && !in_array($userData['channel'], $channelIDs)) { continue; } if($key) { if(!isset($userData[$key])) { return $onlineUsersData; } if($value !== null) { if($userData[$key] == $value) { array_push($onlineUsersData, $userData); } else { continue; } } else { array_push($onlineUsersData, $userData[$key]); } } else { array_push($onlineUsersData, $userData); } } return $onlineUsersData; } return $this->_onlineUsersData; } function removeUserFromOnlineUsersData($userID=null) { if(!$this->_onlineUsersData) { return; } $userID = ($userID === null) ? $this->getUserID() : $userID; for($i=0; $i_onlineUsersData); $i++) { if($this->_onlineUsersData[$i]['userID'] == $userID) { array_splice($this->_onlineUsersData, $i, 1); break; } } } function resetOnlineUsersData() { $this->_onlineUsersData = null; } function getOnlineUsers($channelIDs=null) { return $this->getOnlineUsersData($channelIDs, 'userName'); } function getOnlineUserIDs($channelIDs=null) { return $this->getOnlineUsersData($channelIDs, 'userID'); } function startSession() { if(!session_id()) { // Set the session name: session_name($this->getConfig('sessionName')); // Set session cookie parameters: session_set_cookie_params( 0, // The session is destroyed on logout anyway, so no use to set this $this->getConfig('sessionCookiePath'), $this->getConfig('sessionCookieDomain'), $this->getConfig('sessionCookieSecure') ); // Start the session: session_start(); // We started a new session: $this->_sessionNew = true; } } function destroySession() { if($this->_sessionNew) { // Delete all session variables: $_SESSION = array(); // Delete the session cookie: if (isset($_COOKIE[session_name()])) { setcookie( session_name(), '', time()-42000, $this->getConfig('sessionCookiePath'), $this->getConfig('sessionCookieDomain') ?? '', $this->getConfig('sessionCookieSecure') ?? false ); } // Destroy the session: session_destroy(); } else { // Unset all session variables starting with the sessionKeyPrefix: foreach($_SESSION as $key=>$value) { if(strpos($key, $this->getConfig('sessionKeyPrefix')) === 0) { unset($_SESSION[$key]); } } } } function regenerateSessionID() { if($this->_sessionNew) { // Regenerate session id: @session_regenerate_id(true); } } function getSessionVar($key, $prefix=null) { if($prefix === null) $prefix = $this->getConfig('sessionKeyPrefix'); // Return the session value if existing: if(isset($_SESSION[$prefix.$key])) return $_SESSION[$prefix.$key]; else return null; } function setSessionVar($key, $value, $prefix=null) { if($prefix === null) $prefix = $this->getConfig('sessionKeyPrefix'); // Set the session value: $_SESSION[$prefix.$key] = $value; } function getSessionIP() { return $this->getSessionVar('IP'); } function setSessionIP($ip) { $this->setSessionVar('IP', $ip); } function getQueryUserName() { return $this->getSessionVar('QueryUserName'); } function setQueryUserName($userName) { $this->setSessionVar('QueryUserName', $userName); } function getInvitations() { if($this->_invitations === null) { $this->_invitations = array(); $sql = 'SELECT channel FROM '.$this->getDataBaseTable('invitations').' WHERE userID='.$this->db->makeSafe($this->getUserID()).' AND DATE_SUB(NOW(), interval 1 DAY) < dateTime;'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } while($row = $result->fetch()) { array_push($this->_invitations, $row['channel']); } $result->free(); } return $this->_invitations; } function removeExpiredInvitations() { $sql = 'DELETE FROM '.$this->getDataBaseTable('invitations').' WHERE DATE_SUB(NOW(), interval 1 DAY) > dateTime;'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function addInvitation($userID, $channelID=null) { $this->removeExpiredInvitations(); $channelID = ($channelID === null) ? $this->getChannel() : $channelID; $sql = 'INSERT INTO '.$this->getDataBaseTable('invitations').'( userID, channel, dateTime ) VALUES ( '.$this->db->makeSafe($userID).', '.$this->db->makeSafe($channelID).', NOW() );'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function removeInvitation($userID, $channelID=null) { $channelID = ($channelID === null) ? $this->getChannel() : $channelID; $sql = 'DELETE FROM '.$this->getDataBaseTable('invitations').' WHERE userID='.$this->db->makeSafe($userID).' AND channel='.$this->db->makeSafe($channelID).';'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); // Stop if an error occurs: if($result->error()) { echo $result->getError(); die(); } } function getUserID() { return $this->getSessionVar('UserID'); } function setUserID($id) { $this->setSessionVar('UserID', $id); } function getUserName() { return $this->getSessionVar('UserName'); } function setUserName($name) { $this->setSessionVar('UserName', $name); } function getLoginUserName() { return $this->getSessionVar('LoginUserName'); } function setLoginUserName($name) { $this->setSessionVar('LoginUserName', $name); } function getUserRole() { $userRole = $this->getSessionVar('UserRole'); if($userRole === null) return AJAX_CHAT_GUEST; return $userRole; } function setUserRole($role) { $this->setSessionVar('UserRole', $role); } function getChannel() { return $this->getSessionVar('Channel'); } function setChannel($channel) { $this->setSessionVar('Channel', $channel); // Save the channel enter timestamp: $this->setChannelEnterTimeStamp(time()); // Update the channel authentication for the socket server: if($this->getConfig('socketServerEnabled')) { $this->updateSocketAuthentication( $this->getUserID(), $this->getSocketRegistrationID(), array($channel,$this->getPrivateMessageID()) ); } // Reset the logs view socket authentication session var: if($this->getSessionVar('logsViewSocketAuthenticated')) { $this->setSessionVar('logsViewSocketAuthenticated', false); } } function isLoggedIn() { return (bool)$this->getSessionVar('LoggedIn'); } function setLoggedIn($bool) { $this->setSessionVar('LoggedIn', $bool); } function getLoginTimeStamp() { return $this->getSessionVar('LoginTimeStamp'); } function setLoginTimeStamp($time) { $this->setSessionVar('LoginTimeStamp', $time); } function getChannelEnterTimeStamp() { return $this->getSessionVar('ChannelEnterTimeStamp'); } function setChannelEnterTimeStamp($time) { $this->setSessionVar('ChannelEnterTimeStamp', $time); } function getStatusUpdateTimeStamp() { return $this->getSessionVar('StatusUpdateTimeStamp'); } function setStatusUpdateTimeStamp($time) { $this->setSessionVar('StatusUpdateTimeStamp', $time); } function getInactiveCheckTimeStamp() { return $this->getSessionVar('InactiveCheckTimeStamp'); } function setInactiveCheckTimeStamp($time) { $this->setSessionVar('InactiveCheckTimeStamp', $time); } function getInsertedMessagesRate() { return $this->getSessionVar('InsertedMessagesRate'); } function setInsertedMessagesRate($rate) { $this->setSessionVar('InsertedMessagesRate', $rate); } function getInsertedMessagesRateTimeStamp() { return $this->getSessionVar('InsertedMessagesRateTimeStamp'); } function setInsertedMessagesRateTimeStamp($time) { $this->setSessionVar('InsertedMessagesRateTimeStamp', $time); } function getLangCode() { // Get the langCode from request or cookie: $langCodeCookie = isset($_COOKIE[$this->getConfig('sessionName').'_lang']) ? $_COOKIE[$this->getConfig('sessionName').'_lang'] : null; $langCode = $this->getRequestVar('lang') ? $this->getRequestVar('lang') : $langCodeCookie; // Check if the langCode is valid: if(!in_array($langCode, $this->getConfig('langAvailable'))) { // Determine the user language: $language = new AJAXChatLanguage($this->getConfig('langAvailable'), $this->getConfig('langDefault')); $langCode = $language->getLangCode(); } return $langCode; } function setLangCodeCookie() { setcookie( $this->getConfig('sessionName').'_lang', $this->getLangCode(), time()+60*60*24*$this->getConfig('sessionCookieLifeTime'), $this->getConfig('sessionCookiePath'), $this->getConfig('sessionCookieDomain') ?? '', $this->getConfig('sessionCookieSecure') ?? false ); } function removeUnsafeCharacters($str) { // Remove NO-WS-CTL, non-whitespace control characters (RFC 2822), decimal 1–8, 11–12, 14–31, and 127: return AJAXChatEncoding::removeUnsafeCharacters($str); } function subString($str, $start=0, $length=null, $encoding='UTF-8') { return AJAXChatString::subString($str, $start, $length, $encoding); } function stringLength($str, $encoding='UTF-8') { return AJAXChatString::stringLength($str, $encoding); } function trimMessageText($text) { return $this->trimString($text, 'UTF-8', $this->getConfig('messageTextMaxLength')); } function trimUserName($userName) { return $this->trimString($userName, null, $this->getConfig('userNameMaxLength'), true, true); } function trimChannelName($channelName) { return $this->trimString($channelName, null, null, true, true); } function trimString($str, $sourceEncoding=null, $maxLength=null, $replaceWhitespace=false, $decodeEntities=false, $htmlEntitiesMap=null) { // Make sure the string contains valid unicode: $str = $this->convertToUnicode($str, $sourceEncoding); // Make sure the string contains no unsafe characters: $str = $this->removeUnsafeCharacters($str); // Strip whitespace from the beginning and end of the string: $str = trim($str); if($replaceWhitespace) { // Replace any whitespace in the userName with the underscore "_": $str = preg_replace('/\s/u', '_', $str); } if($decodeEntities) { // Decode entities: $str = $this->decodeEntities($str, 'UTF-8', $htmlEntitiesMap); } if($maxLength) { // Cut the string to the allowed length: $str = $this->subString($str, 0, $maxLength); } return $str; } function convertToUnicode($str, $sourceEncoding=null) { if($sourceEncoding === null) { $sourceEncoding = $this->getConfig('sourceEncoding'); } return $this->convertEncoding($str, $sourceEncoding, 'UTF-8'); } function convertFromUnicode($str, $contentEncoding=null) { if($contentEncoding === null) { $contentEncoding = $this->getConfig('contentEncoding'); } return $this->convertEncoding($str, 'UTF-8', $contentEncoding); } function convertEncoding($str, $charsetFrom, $charsetTo) { return AJAXChatEncoding::convertEncoding($str, $charsetFrom, $charsetTo); } function encodeEntities($str, $encoding='UTF-8', $convmap=null) { return AJAXChatEncoding::encodeEntities($str, $encoding, $convmap); } function decodeEntities($str, $encoding='UTF-8', $htmlEntitiesMap=null) { return AJAXChatEncoding::decodeEntities($str, $encoding, $htmlEntitiesMap); } function htmlEncode($str) { return AJAXChatEncoding::htmlEncode($str, $this->getConfig('contentEncoding')); } function encodeSpecialChars($str) { return AJAXChatEncoding::encodeSpecialChars($str); } function decodeSpecialChars($str) { return AJAXChatEncoding::decodeSpecialChars($str); } function ipToStorageFormat($ip) { if(function_exists('inet_pton')) { // ipv4 & ipv6: return @inet_pton($ip); } // Only ipv4: return @pack('N',@ip2long($ip)); } function ipFromStorageFormat($ip) { if(function_exists('inet_ntop')) { // ipv4 & ipv6: return @inet_ntop($ip); } // Only ipv4: $unpacked = @unpack('Nlong',$ip); if(isset($unpacked['long'])) { return @long2ip($unpacked['long']); } return null; } function getConfig($key, $subkey=null) { if($subkey) return $this->_config[$key][$subkey]; else return $this->_config[$key]; } function setConfig($key, $subkey, $value) { if($subkey) { if(!isset($this->_config[$key])) { $this->_config[$key] = array(); } $this->_config[$key][$subkey] = $value; } else { $this->_config[$key] = $value; } } function getLang($key=null) { if(!$this->_lang) { // Include the language file: $lang = null; require AJAX_CHAT_PATH . '/lang/' . $this->getLangCode() . '.php'; $this->_lang = &$lang; } if($key === null) return $this->_lang; if(isset($this->_lang[$key])) return str_replace('%flashii%', $this->getConfig('flashiiString'), $this->_lang[$key]); return null; } function getChatURL() { if(defined('AJAX_CHAT_URL')) { return AJAX_CHAT_URL; } return (isset($_SERVER['HTTPS']) ? 'https://' : 'http://'). (isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : ''). (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME']. (isset($_SERVER['HTTPS']) && $_SERVER['SERVER_PORT'] == 443 || $_SERVER['SERVER_PORT'] == 80 ? '' : ':'.$_SERVER['SERVER_PORT']))). substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/')+1); } function getIDFromName($userName) { $userDataArray = $this->getOnlineUsersData(null,'userName',$userName); if($userDataArray && isset($userDataArray[0])) { return $userDataArray[0]['userID']; } return null; } function getNameFromID($userID) { $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); if($userDataArray && isset($userDataArray[0])) { return $userDataArray[0]['userName']; } return null; } function getChannelFromID($userID) { $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); if($userDataArray && isset($userDataArray[0])) { return $userDataArray[0]['channel']; } return null; } function getIPFromID($userID) { $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); if($userDataArray && isset($userDataArray[0])) { return $userDataArray[0]['ip']; } return null; } function getRoleFromID($userID) { $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); if($userDataArray && isset($userDataArray[0])) { return $userDataArray[0]['userRole']; } return null; } function getChannelNames() { return array_flip($this->getChannels()); } function getChannelIDFromChannelName($channelName) { if(!$channelName) return null; $channels = $this->getAllChannels(); if(array_key_exists($channelName,$channels)) { return $channels[$channelName]; } $channelID = null; // Check if the requested channel is the own private channel: if($channelName == $this->getPrivateChannelName()) { return $this->getPrivateChannelID(); } // Try to retrieve a private room ID: $strlenChannelName = $this->stringLength($channelName); $strlenPrefix = $this->stringLength($this->getConfig('privateChannelPrefix')); $strlenSuffix = $this->stringLength($this->getConfig('privateChannelSuffix')); if($this->subString($channelName,0,$strlenPrefix) == $this->getConfig('privateChannelPrefix') && $this->subString($channelName,$strlenChannelName-$strlenSuffix) == $this->getConfig('privateChannelSuffix')) { $userName = $this->subString( $channelName, $strlenPrefix, $strlenChannelName-($strlenPrefix+$strlenSuffix) ); $userID = $this->getIDFromName($userName); if($userID !== null) { $channelID = $this->getPrivateChannelID($userID); } } return $channelID; } function getChannelNameFromChannelID($channelID) { foreach($this->getAllChannels() as $key=>$value) { if($value == $channelID) { return $key; } } // Try to retrieve a private room name: if($channelID == $this->getPrivateChannelID()) { return $this->getPrivateChannelName(); } $userName = $this->getNameFromID($channelID-$this->getConfig('privateChannelDiff')); if($userName === null) { return null; } return $this->getPrivateChannelName($userName); } function getChannelName() { return $this->getChannelNameFromChannelID($this->getChannel()); } function getPrivateChannelName($userName=null) { if($userName === null) { $userName = $this->getUserName(); } return $this->getConfig('privateChannelPrefix').$userName.$this->getConfig('privateChannelSuffix'); } function getPrivateChannelID($userID=null) { if($userID === null) { $userID = $this->getUserID(); } return $userID + $this->getConfig('privateChannelDiff'); } function getPrivateMessageID($userID=null) { if($userID === null) { $userID = $this->getUserID(); } return $userID + $this->getConfig('privateMessageDiff'); } function isAllowedToSendPrivateMessage() { if($this->getConfig('allowPrivateMessages') || $this->getUserRole() == AJAX_CHAT_ADMIN) { return true; } return false; } function isAllowedToCreatePrivateChannel() { if($this->getConfig('allowPrivateChannels')) { switch($this->getUserRole()) { case DONATOR: return true; case BOTS: return true; case PURPLE: return true; case CMOD: return true; case AJAX_CHAT_MODERATOR: return true; case AJAX_CHAT_ADMIN: return true; default: return false; } } return false; } function isAllowedToListHiddenUsers() { // Hidden users are users within private or restricted channels: switch($this->getUserRole()) { case CMOD: return true; case PURPLE: return true; case AJAX_CHAT_MODERATOR: return true; case AJAX_CHAT_ADMIN: return true; default: return false; } } function isUserOnline($userID=null) { $userID = ($userID === null) ? $this->getUserID() : $userID; $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); if($userDataArray && count($userDataArray) > 0) { return true; } return false; } function isUserNameInUse($userName=null) { $userName = ($userName === null) ? $this->getUserName() : $userName; $userDataArray = $this->getOnlineUsersData(null,'userName',$userName); if($userDataArray && count($userDataArray) > 0) { return true; } return false; } function isUserBanned($userName, $userID=null, $ip=null) { if($userID !== null) { $bannedUserDataArray = $this->getBannedUsersData('userID',$userID); if($bannedUserDataArray && isset($bannedUserDataArray[0])) { return true; } } if($ip !== null) { $bannedUserDataArray = $this->getBannedUsersData('ip',$ip); if($bannedUserDataArray && isset($bannedUserDataArray[0])) { return true; } } $bannedUserDataArray = $this->getBannedUsersData('userName',$userName); if($bannedUserDataArray && isset($bannedUserDataArray[0])) { return true; } return false; } function isMaxUsersLoggedIn() { if(count($this->getOnlineUsersData()) >= $this->getConfig('maxUsersLoggedIn')) { return true; } return false; } function validateChannel($channelID) { if($channelID === null) { return false; } // Return true for normal channels the user has acces to: if(in_array($channelID, $this->getChannels())) { return true; } // Return true if the user is allowed to join his own private channel: if($channelID == $this->getPrivateChannelID() && $this->isAllowedToCreatePrivateChannel()) { return true; } // Return true if the user has been invited to a restricted or private channel: if(in_array($channelID, $this->getInvitations())) { return true; } // No valid channel, return false: return false; } function createGuestUserName() { $maxLength = $this->getConfig('userNameMaxLength') - $this->stringLength($this->getConfig('guestUserPrefix')) - $this->stringLength($this->getConfig('guestUserSuffix')); // seed with microseconds since last "whole" second: mt_srand((double)microtime()*1000000); // Create a random userName using numbers between 100000 and 999999: $userName = substr(mt_rand(100000, 999999), 0, $maxLength); return $this->getConfig('guestUserPrefix').$userName.$this->getConfig('guestUserSuffix'); } // Guest userIDs must not interfere with existing userIDs and must be lower than privateChannelDiff: function createGuestUserID() { // seed with microseconds since last "whole" second: mt_srand((double)microtime()*1000000); return mt_rand($this->getConfig('minGuestUserID'), $this->getConfig('privateChannelDiff')-1); } function getGuestUser() { if(!$this->getConfig('allowGuestLogins')) return null; if($this->getConfig('allowGuestUserName')) { $maxLength = $this->getConfig('userNameMaxLength') - $this->stringLength($this->getConfig('guestUserPrefix')) - $this->stringLength($this->getConfig('guestUserSuffix')); // Trim guest userName: $userName = $this->trimString($this->getRequestVar('userName'), null, $maxLength, true, true); // If given userName is invalid, create one: if(!$userName) { $userName = $this->createGuestUserName(); } else { // Add the guest users prefix and suffix to the given userName: $userName = $this->getConfig('guestUserPrefix').$userName.$this->getConfig('guestUserSuffix'); } } else { $userName = $this->createGuestUserName(); } $userData = array(); $userData['userID'] = $this->createGuestUserID(); $userData['userName'] = $userName; $userData['userRole'] = AJAX_CHAT_GUEST; return $userData; } function getCustomVar($key) { if(!isset($this->_customVars)) $this->_customVars = array(); if(!isset($this->_customVars[$key])) return null; return $this->_customVars[$key]; } function setCustomVar($key, $value) { if(!isset($this->_customVars)) $this->_customVars = array(); $this->_customVars[$key] = $value; } // Override to replace custom template tags: // Return the replacement for the given tag (and given tagContent) function replaceCustomTemplateTags($tag, $tagContent) { return null; } // Override to initialize custom configuration settings: function initCustomConfig() { } // Override to add custom request variables: // Add values to the request variables array: $this->_requestVars['customVariable'] = null; function initCustomRequestVars() { } // Override to add custom session code right after the session has been started: function initCustomSession() { } // Override, to parse custom info requests: // $infoRequest contains the current info request // Add info responses using the method addInfoMessage($info, $type) function parseCustomInfoRequest($infoRequest) { } // Override to replace custom text: // Return replaced text // $text contains the whole message function replaceCustomText(&$text) { return $text; } // Override to add custom commands: // Return true if a custom command has been successfully parsed, else false // $text contains the whole message, $textParts the message split up as words array function parseCustomCommands($text, $textParts) { return false; } // Override to perform custom actions on new messages: // Return true if message may be inserted, else false // $text contains the whole message function onNewMessage($text) { return true; } // Override to perform custom actions on new messages: // Method to set the style cookie depending on user data function setStyle() { } // Override: // Returns true if the userID of the logged in user is identical to the userID of the authentication system // or the user is authenticated as guest in the chat and the authentication system function revalidateUserID() { return true; } // Override: // Returns an associative array containing userName, userID and userRole // Returns null if login is invalid function getValidLoginUserData() { // Check if we have a valid registered user: if(false) { // Here is the place to check user authentication } else { // Guest users: return $this->getGuestUser(); } } // Override: // Store the channels the current user has access to // Make sure channel names don't contain any whitespace function &getChannels() { if($this->_channels === null) { $this->_channels = $this->getAllChannels(); } return $this->_channels; } // Override: // Store all existing channels // Make sure channel names don't contain any whitespace function &getAllChannels() { if($this->_allChannels === null) { $this->_allChannels = array(); // Default channel, public to everyone: $this->_allChannels[$this->trimChannelName($this->getConfig('defaultChannelName'))] = $this->getConfig('defaultChannelID'); } return $this->_allChannels; } }