public static function filter($text, $replacementText = null) { if (self::$words === null) { self::$words = file(dirname(__FILE__) . self::BAD_WORDS_FILE, FILE_IGNORE_NEW_LINES); } if (extension_loaded('mbstring')) { mb_internal_encoding("UTF-8"); foreach (self::$words as $word) { $notFuzzyMatching = true; $word = trim($word); // detect empty lines and comments: if (self::isCommentOrEmpty($word)) { continue; } // fuzzy matching: if (self::isFuzzyMatchingMb($word)) { $notFuzzyMatching = false; $word = trim($word, '[]'); } // masked word: $masked = self::getMaskedWordMb($word, $replacementText); // compose regexp for the word: $regexp = mb_substr($word, 0, 1); for ($i = 1; $i < mb_strlen($word); $i++) { $regexp .= "[^\\p{L}]*"; $regexp .= preg_quote(mb_substr($word, $i, 1)); } // if not fuzzy matching: if ($notFuzzyMatching) { $regexp = "(^|[^\\p{L}])" . $regexp . "([^\\p{L}]|\$)"; $masked = '$1' . $masked . '$2'; } $text = preg_replace("/{$regexp}/ui", $masked, $text); } } else { foreach (self::$words as $word) { $notFuzzyMatching = true; $word = trim($word); // detect empty lines and comments: if (self::isCommentOrEmpty($word)) { continue; } // fuzzy matching: if (self::isFuzzyMatching($word)) { $notFuzzyMatching = false; $word = trim($word, '[]'); } // masked word: $masked = $replacementText !== null ? $replacementText : self::getMaskedWord($word); // compose regexp for the word: $regexp = substr($word, 0, 1); for ($i = 1; $i < strlen($word); $i++) { $regexp .= "[^\\p{L}]*"; $regexp .= preg_quote(substr($word, $i, 1)); } // if not fuzzy matching: if ($notFuzzyMatching) { $regexp = "(^|[^\\p{L}])" . $regexp . "([^\\p{L}]|\$)"; $masked = '$1' . $masked . '$2'; } $text = preg_replace("/{$regexp}/i", $masked, $text); } } return $text; }
/** * Validates given username. * * @param string $userName * * @return string Validated username (trimmed and filtered) * @throws Exception If username is not valid */ public function validateUserName($userName) { $userName = trim($userName); // check for valid characters: if (strlen($userName) == 0 || !preg_match('/^[a-zA-Z0-9\\-_ ]+$/', $userName)) { throw new Exception($this->options->getOption('message_error_1', 'Only letters, number, spaces, hyphens and underscores are allowed')); } // filter the new username: if ($this->options->isOptionEnabled('filter_bad_words')) { WiseChatContainer::load('rendering/filters/pre/WiseChatFilter'); $userName = WiseChatFilter::filter($userName); } // check if the new username is already occupied: $occupiedException = new Exception($this->options->getOption('message_error_2', 'This name is already occupied')); $prefix = $this->options->getOption('user_name_prefix', 'Anonymous'); if ($this->getUserNameOrEmptyString() == $userName || $this->usersDAO->getWpUserByDisplayName($userName) !== null || $this->usersDAO->getWpUserByLogin($userName) !== null || $this->channelUsersDAO->isUserNameOccupied($userName, $this->userSessionDAO->getSessionId()) || preg_match("/^{$prefix}/", $userName) || $userName == $this->getSystemUser()->getName()) { throw $occupiedException; } return $userName; }
/** * @dataProvider dataFuzzyNegativeUnicode */ public function testFuzzyNegativeUnicode($input, $output) { WiseChatFilter::$words = array('[balls]', '[kość]'); if (extension_loaded(self::$mbExtension)) { $this->assertEquals($output, WiseChatFilter::filter($input)); } }
/** * Publishes a message in the given channel of the chat and returns it. * * @param WiseChatUser $user Author of the message * @param WiseChatChannel $channel A channel to publish in * @param string $text Content of the message * @param boolean $isAdmin Indicates whether to mark the message as admin-owned * * @return WiseChatMessage|null * @throws Exception On validation error */ public function addMessage($user, $channel, $text, $isAdmin = false) { $text = trim($text); $filteredMessage = $text; // basic validation: if ($user === null) { throw new Exception('User cannot be null'); } if ($channel === null) { throw new Exception('Channel cannot be null'); } if ($this->bansService->isIpAddressBanned($user->getIp())) { throw new Exception($this->options->getOption('message_error_3', 'You were banned from posting messages')); } // use bad words filtering: if ($this->options->isOptionEnabled('filter_bad_words')) { WiseChatContainer::load('rendering/filters/pre/WiseChatFilter'); $badWordsFilterReplacement = $this->options->getOption('bad_words_replacement_text'); $filteredMessage = WiseChatFilter::filter($filteredMessage, strlen($badWordsFilterReplacement) > 0 ? $badWordsFilterReplacement : null); } // auto-ban feature: if ($this->options->isOptionEnabled('enable_autoban') && $filteredMessage != $text) { $counter = $this->abuses->incrementAndGetAbusesCounter(); $threshold = $this->options->getIntegerOption('autoban_threshold', 3); if ($counter >= $threshold && $threshold > 0) { $duration = $this->options->getIntegerOption('autoban_duration', 1440); $this->bansService->banIpAddress($user->getIp(), $this->bansService->getDurationFromString($duration . 'm')); $this->abuses->clearAbusesCounter(); } } // flood prevention feature: if ($this->options->isOptionEnabled('enable_flood_control')) { $floodControlThreshold = $this->options->getIntegerOption('flood_control_threshold', 200); $floodControlTimeFrame = $this->options->getIntegerOption('flood_control_time_frame', 1); if ($floodControlThreshold > 0 && $floodControlTimeFrame > 0) { $messagesAmount = $this->messagesDAO->getNumberByCriteria(WiseChatMessagesCriteria::build()->setIp($user->getIp())->setMinimumTime(time() - $floodControlTimeFrame * 60)); if ($messagesAmount > $floodControlThreshold) { $duration = $this->options->getIntegerOption('flood_control_ban_duration', 1440); $this->bansService->banIpAddress($user->getIp(), $this->bansService->getDurationFromString($duration . 'm')); } } } // go through the custom filters: $filterChain = WiseChatContainer::get('services/WiseChatFilterChain'); $filteredMessage = $filterChain->filter($filteredMessage); // cut the message: $messageMaxLength = $this->options->getIntegerOption('message_max_length', 100); if ($messageMaxLength > 0) { $filteredMessage = substr($filteredMessage, 0, $messageMaxLength); } // convert images and links into proper shortcodes and download images (if enabled): /** @var WiseChatLinksPreFilter $linksPreFilter */ $linksPreFilter = WiseChatContainer::get('rendering/filters/pre/WiseChatLinksPreFilter'); $filteredMessage = $linksPreFilter->filter($filteredMessage, $this->options->isOptionEnabled('allow_post_images'), $this->options->isOptionEnabled('enable_youtube')); $message = new WiseChatMessage(); $message->setTime(time()); $message->setAdmin($isAdmin); $message->setUserName($user->getName()); $message->setUserId($user->getId()); $message->setText($filteredMessage); $message->setChannelName($channel->getName()); $message->setIp($user->getIp()); if ($user->getWordPressId() !== null) { $message->setWordPressUserId($user->getWordPressId()); } $message = $this->messagesDAO->save($message); // mark attachments created by the links pre-filter: $createdAttachments = $linksPreFilter->getCreatedAttachments(); if (count($createdAttachments) > 0) { $this->attachmentsService->markAttachmentsWithDetails($createdAttachments, $channel->getName(), $message->getId()); } return $message; }