/* * Copyright (c) 2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { ChatMessageParser::ChatMessageParser(const std::map& emoticons, HighlightRulesListPtr highlightRules, bool mucMode) : emoticons_(emoticons), highlightRules_(highlightRules), mucMode_(mucMode) { } typedef std::pair StringPair; ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, bool senderIsSelf) { ChatWindow::ChatMessage parsedMessage; std::string remaining = body; /* Parse one, URLs */ while (!remaining.empty()) { bool found = false; std::pair, size_t> links = Linkify::splitLink(remaining); remaining = ""; for (size_t i = 0; i < links.first.size(); i++) { const std::string& part = links.first[i]; if (found) { // Must be on the last part, then remaining = part; } else { if (i == links.second) { found = true; parsedMessage.append(boost::make_shared(part)); } else { parsedMessage.append(boost::make_shared(part)); } } } } /* do emoticon substitution */ parsedMessage = emoticonHighlight(parsedMessage); if (!senderIsSelf) { /* do not highlight our own messsages */ /* do word-based color highlighting */ parsedMessage = splitHighlight(parsedMessage); } return parsedMessage; } ChatWindow::ChatMessage ChatMessageParser::emoticonHighlight(const ChatWindow::ChatMessage& message) { std::string regexString; /* Parse two, emoticons */ foreach (StringPair emoticon, emoticons_) { /* Construct a regexp that finds an instance of any of the emoticons inside a group */ regexString += regexString.empty() ? "(" : "|"; regexString += Regex::escape(emoticon.first); } if (regexString.empty()) { return message; } regexString += ")"; boost::regex emoticonRegex(regexString); ChatWindow::ChatMessage newMessage; foreach (boost::shared_ptr part, message.getParts()) { boost::shared_ptr textPart; if ((textPart = boost::dynamic_pointer_cast(part))) { try { boost::match_results match; const std::string& text = textPart->text; std::string::const_iterator start = text.begin(); while (regex_search(start, text.end(), match, emoticonRegex)) { std::string::const_iterator matchStart = match[0].first; std::string::const_iterator matchEnd = match[0].second; if (start != matchStart) { /* If we're skipping over plain text since the previous emoticon, record it as plain text */ newMessage.append(boost::make_shared(std::string(start, matchStart))); } boost::shared_ptr emoticonPart = boost::make_shared(); std::map::const_iterator emoticonIterator = emoticons_.find(match.str()); assert (emoticonIterator != emoticons_.end()); const StringPair& emoticon = *emoticonIterator; emoticonPart->imagePath = emoticon.second; emoticonPart->alternativeText = emoticon.first; newMessage.append(emoticonPart); start = matchEnd; } if (start != text.end()) { /* If there's plain text after the last emoticon, record it */ newMessage.append(boost::make_shared(std::string(start, text.end()))); } } catch (std::runtime_error) { /* Basically too expensive to compute the regex results and it gave up, so pass through as text */ newMessage.append(part); } } else { newMessage.append(part); } } return newMessage; } ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message) { ChatWindow::ChatMessage parsedMessage = message; for (size_t i = 0; i < highlightRules_->getSize(); ++i) { const HighlightRule& rule = highlightRules_->getRule(i); if (rule.getMatchMUC() && !mucMode_) { continue; /* this rule only applies to MUC's, and this is a CHAT */ } else if (rule.getMatchChat() && mucMode_) { continue; /* this rule only applies to CHAT's, and this is a MUC */ } foreach(const boost::regex ®ex, rule.getKeywordRegex()) { ChatWindow::ChatMessage newMessage; foreach (boost::shared_ptr part, parsedMessage.getParts()) { boost::shared_ptr textPart; if ((textPart = boost::dynamic_pointer_cast(part))) { try { boost::match_results match; const std::string& text = textPart->text; std::string::const_iterator start = text.begin(); while (regex_search(start, text.end(), match, regex)) { std::string::const_iterator matchStart = match[0].first; std::string::const_iterator matchEnd = match[0].second; if (start != matchStart) { /* If we're skipping over plain text since the previous emoticon, record it as plain text */ newMessage.append(boost::make_shared(std::string(start, matchStart))); } boost::shared_ptr highlightPart = boost::make_shared(); highlightPart->text = match.str(); highlightPart->foregroundColor = rule.getAction().getTextColor(); highlightPart->backgroundColor = rule.getAction().getTextBackground(); newMessage.append(highlightPart); start = matchEnd; } if (start != text.end()) { /* If there's plain text after the last emoticon, record it */ newMessage.append(boost::make_shared(std::string(start, text.end()))); } } catch (std::runtime_error) { /* Basically too expensive to compute the regex results and it gave up, so pass through as text */ newMessage.append(part); } } else { newMessage.append(part); } } parsedMessage = newMessage; } } return parsedMessage; } }