diff options
Diffstat (limited to 'Swift/Controllers/Chat/ChatControllerBase.cpp')
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 604 |
1 files changed, 328 insertions, 276 deletions
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 23137dc..3805084 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -1,369 +1,421 @@ /* - * Copyright (c) 2010-2013 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swift/Controllers/Chat/ChatControllerBase.h> -#include <sstream> #include <map> +#include <memory> +#include <sstream> +#include <boost/algorithm/string.hpp> #include <boost/bind.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/smart_ptr/make_shared.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/numeric/conversion/cast.hpp> -#include <boost/algorithm/string.hpp> -#include <Swiften/Base/format.h> +#include <Swiften/Avatars/AvatarManager.h> #include <Swiften/Base/Path.h> -#include <Swiften/Base/String.h> +#include <Swiften/Base/format.h> #include <Swiften/Client/StanzaChannel.h> +#include <Swiften/Disco/EntityCapsProvider.h> #include <Swiften/Elements/Delay.h> +#include <Swiften/Elements/DeliveryReceipt.h> +#include <Swiften/Elements/DeliveryReceiptRequest.h> #include <Swiften/Elements/MUCInvitationPayload.h> #include <Swiften/Elements/MUCUserPayload.h> -#include <Swiften/Base/foreach.h> -#include <Swiften/Disco/EntityCapsProvider.h> #include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h> -#include <Swiften/Avatars/AvatarManager.h> +#include <Swift/Controllers/Chat/AutoAcceptMUCInviteDecider.h> +#include <Swift/Controllers/Chat/Chattables.h> +#include <Swift/Controllers/Chat/ChatMessageParser.h> +#include <Swift/Controllers/Highlighting/HighlightManager.h> +#include <Swift/Controllers/Highlighting/Highlighter.h> #include <Swift/Controllers/Intl.h> -#include <Swift/Controllers/XMPPEvents/EventController.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> #include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h> -#include <Swift/Controllers/HighlightManager.h> -#include <Swift/Controllers/Highlighter.h> -#include <Swift/Controllers/Chat/AutoAcceptMUCInviteDecider.h> -#include <Swift/Controllers/Chat/ChatMessageParser.h> namespace Swift { -ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ChatMessageParser* chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry), chatMessageParser_(chatMessageParser), autoAcceptMUCInviteDecider_(autoAcceptMUCInviteDecider), eventStream_(eventStream) { - chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream); - chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); - chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); - chatWindow_->onLogCleared.connect(boost::bind(&ChatControllerBase::handleLogCleared, this)); - entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1)); - highlighter_ = highlightManager->createHighlighter(); - setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable()); - createDayChangeTimer(); +ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, SettingsProvider* settings, Chattables& chattables) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry), chatMessageParser_(chatMessageParser), autoAcceptMUCInviteDecider_(autoAcceptMUCInviteDecider), eventStream_(eventStream), roomSecurityMarking_(""), previousMessageSecurityMarking_(""), settings_(settings), chattables_(chattables) { + chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream); + chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); + chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); + chatWindow_->onContinuationsBroken.connect(boost::bind(&ChatControllerBase::handleContinuationsBroken, this)); + scopedConnectionResendMessage_ = chatWindow_->onResendMessageRequest.connect(boost::bind(&ChatControllerBase::handleResendMessageRequest, this, _1)); + entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1)); + highlighter_ = highlightManager->createHighlighter(nickResolver); + ChatControllerBase::setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable()); } ChatControllerBase::~ChatControllerBase() { - delete chatWindow_; + delete highlighter_; + delete chatWindow_; } -void ChatControllerBase::handleLogCleared() { - cancelReplaces(); +void ChatControllerBase::handleContinuationsBroken() { + cancelReplaces(); } ChatWindow* ChatControllerBase::detachChatWindow() { - ChatWindow* chatWindow = chatWindow_; - chatWindow_ = NULL; - return chatWindow; + chatWindow_->onContinuationsBroken.disconnect(boost::bind(&ChatControllerBase::handleContinuationsBroken, this)); + chatWindow_->onSendMessageRequest.disconnect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); + chatWindow_->onAllMessagesRead.disconnect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); + scopedConnectionResendMessage_.disconnect(); + ChatWindow* chatWindow = chatWindow_; + chatWindow_ = nullptr; + return chatWindow; } void ChatControllerBase::handleCapsChanged(const JID& jid) { - if (jid.compare(toJID_, JID::WithoutResource) == 0) { - handleBareJIDCapsChanged(jid); - } + if (jid.compare(toJID_, JID::WithoutResource) == 0) { + handleBareJIDCapsChanged(jid); + } } void ChatControllerBase::setCanStartImpromptuChats(bool supportsImpromptu) { - if (chatWindow_) { - chatWindow_->setCanInitiateImpromptuChats(supportsImpromptu); - } -} - -void ChatControllerBase::createDayChangeTimer() { - if (timerFactory_) { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - boost::posix_time::ptime midnight(now.date() + boost::gregorian::days(1)); - int millisecondsUntilMidnight = boost::numeric_cast<int>((midnight - now).total_milliseconds()); - dateChangeTimer_ = boost::shared_ptr<Timer>(timerFactory_->createTimer(millisecondsUntilMidnight)); - dateChangeTimer_->onTick.connect(boost::bind(&ChatControllerBase::handleDayChangeTick, this)); - dateChangeTimer_->start(); - } -} - -void ChatControllerBase::handleDayChangeTick() { - dateChangeTimer_->stop(); - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The day is now %1%")) % std::string(boost::posix_time::to_iso_extended_string(now)).substr(0,10))), ChatWindow::DefaultDirection); - dayTicked(); - createDayChangeTimer(); + if (chatWindow_) { + chatWindow_->setCanInitiateImpromptuChats(supportsImpromptu); + } } void ChatControllerBase::setEnabled(bool enabled) { - chatWindow_->setInputEnabled(enabled); + chatWindow_->setOnline(enabled); + chatWindow_->setCanInitiateImpromptuChats(false); } void ChatControllerBase::setOnline(bool online) { - setEnabled(online); + setEnabled(online); } JID ChatControllerBase::getBaseJID() { - return JID(toJID_.toBare()); + return JID(toJID_.toBare()); } -void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) { - if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::SecurityLabelsCatalogFeature)) { - GetSecurityLabelsCatalogRequest::ref request = GetSecurityLabelsCatalogRequest::create(getBaseJID(), iqRouter_); - request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2)); - request->send(); - } else { - chatWindow_->setSecurityLabelsEnabled(false); - labelsEnabled_ = false; - } +void ChatControllerBase::setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) { + if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::SecurityLabelsCatalogFeature)) { + GetSecurityLabelsCatalogRequest::ref request = GetSecurityLabelsCatalogRequest::create(getBaseJID(), iqRouter_); + request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2)); + request->send(); + } else { + chatWindow_->setSecurityLabelsEnabled(false); + labelsEnabled_ = false; + } } void ChatControllerBase::handleAllMessagesRead() { - if (!unreadMessages_.empty()) { - targetedUnreadMessages_.clear(); - foreach (boost::shared_ptr<StanzaEvent> stanzaEvent, unreadMessages_) { - stanzaEvent->conclude(); - } - unreadMessages_.clear(); - chatWindow_->setUnreadMessageCount(0); - onUnreadCountChanged(); - } + if (!unreadMessages_.empty()) { + targetedUnreadMessages_.clear(); + for (std::shared_ptr<StanzaEvent> stanzaEvent : unreadMessages_) { + stanzaEvent->conclude(); + } + unreadMessages_.clear(); + updateMessageCount(); + } } -int ChatControllerBase::getUnreadCount() { - return boost::numeric_cast<int>(targetedUnreadMessages_.size()); +size_t ChatControllerBase::getUnreadCount() { + return targetedUnreadMessages_.size(); } void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) { - if (!stanzaChannel_->isAvailable() || body.empty()) { - return; - } - boost::shared_ptr<Message> message(new Message()); - message->setTo(toJID_); - message->setType(Swift::Message::Chat); - message->setBody(body); - if (labelsEnabled_) { - if (!isCorrectionMessage) { - lastLabel_ = chatWindow_->getSelectedSecurityLabel(); - } - SecurityLabelsCatalog::Item labelItem = lastLabel_; - if (labelItem.getLabel()) { - message->addPayload(labelItem.getLabel()); - } - } - preSendMessageRequest(message); - - boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); - if (useDelayForLatency_) { - message->addPayload(boost::make_shared<Delay>(now, selfJID_)); - } - if (isCorrectionMessage) { - message->addPayload(boost::shared_ptr<Replace> (new Replace(lastSentMessageStanzaID_))); - } - message->setID(lastSentMessageStanzaID_ = idGenerator_.generateID()); - stanzaChannel_->sendMessage(message); - postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message)); - onActivity(message->getBody()); + if (!stanzaChannel_->isAvailable() || body.empty()) { + return; + } + std::shared_ptr<Message> message(new Message()); + message->setTo(toJID_); + message->setType(Swift::Message::Chat); + message->setBody(body); + if (labelsEnabled_) { + if (!isCorrectionMessage) { + lastLabel_ = chatWindow_->getSelectedSecurityLabel(); + } + SecurityLabelsCatalog::Item labelItem = lastLabel_; + if (labelItem.getLabel()) { + message->addPayload(labelItem.getLabel()); + } + } + preSendMessageRequest(message); + + boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); + if (useDelayForLatency_) { + message->addPayload(std::make_shared<Delay>(now, selfJID_)); + } + if (isCorrectionMessage) { + message->addPayload(std::shared_ptr<Replace> (new Replace(lastSentMessageStanzaID_))); + } + message->setID(lastSentMessageStanzaID_ = idGenerator_.generateID()); + stanzaChannel_->sendMessage(message); + postSendMessage(message->getBody().get(), std::dynamic_pointer_cast<Stanza>(message)); + onActivity(message->getBody().get()); #ifdef SWIFT_EXPERIMENTAL_HISTORY - logMessage(body, selfJID_, toJID_, now, false); + logMessage(body, selfJID_, toJID_, now, false); #endif } -void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) { - if (catalog && !error) { - if (catalog->getItems().size() == 0) { - chatWindow_->setSecurityLabelsEnabled(false); - labelsEnabled_ = false; - } else { - labelsEnabled_ = true; - chatWindow_->setAvailableSecurityLabels(catalog->getItems()); - chatWindow_->setSecurityLabelsEnabled(true); - } - } else { - labelsEnabled_ = false; - chatWindow_->setSecurityLabelsError(); - } +void ChatControllerBase::handleSecurityLabelsCatalogResponse(std::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) { + if (catalog && !error) { + if (catalog->getItems().size() == 0) { + chatWindow_->setSecurityLabelsEnabled(false); + labelsEnabled_ = false; + } else { + labelsEnabled_ = true; + chatWindow_->setAvailableSecurityLabels(catalog->getItems()); + chatWindow_->setSecurityLabelsEnabled(true); + } + } else { + labelsEnabled_ = false; + chatWindow_->setSecurityLabelsError(); + } } void ChatControllerBase::showChatWindow() { - chatWindow_->show(); + chatWindow_->show(); } void ChatControllerBase::activateChatWindow() { - chatWindow_->activate(); + chatWindow_->activate(); +} + +bool ChatControllerBase::hasOpenWindow() const { + return chatWindow_ && chatWindow_->isVisible(); +} + +ChatWindow::ChatMessage ChatControllerBase::buildChatWindowChatMessage(const std::string& message, const std::string& senderName, bool senderIsSelf) { + ChatWindow::ChatMessage chatMessage; + chatMessage = chatMessageParser_->parseMessageBody(message, senderName, senderIsSelf); + return chatMessage; } -std::string ChatControllerBase::addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) { - if (boost::starts_with(message, "/me ")) { - return chatWindow_->addAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight); - } else { - return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight); - } +void ChatControllerBase::updateMessageCount() { + auto baseJID = getBaseJID(); + auto state = chattables_.getState(baseJID); + state.unreadCount = unreadMessages_.size(); + chatWindow_->setUnreadMessageCount(state.unreadCount); + chattables_.setState(baseJID, state); +#ifndef NOT_YET + onUnreadCountChanged(); +#endif +} + +std::string ChatControllerBase::addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, const std::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time) { + auto displayedLabel = label; + + if (settings_->getSetting(SettingConstants::MUC_MARKING_ELISION)) { + if (roomSecurityMarking_ != "") { + + if (label && label->getDisplayMarking() == roomSecurityMarking_) { + if (label->getDisplayMarking() == previousMessageSecurityMarking_) { + displayedLabel = std::make_shared<SecurityLabel>(); + } + previousMessageSecurityMarking_ = label->getDisplayMarking(); + } + else if (!label || label->getDisplayMarking().empty()) { + displayedLabel = std::make_shared<SecurityLabel>(); + displayedLabel->setDisplayMarking("Unmarked"); + previousMessageSecurityMarking_ = "Unmarked"; + } + else { + previousMessageSecurityMarking_ = displayedLabel->getDisplayMarking(); + } + } + } + + if (chatMessage.isMeCommand()) { + return chatWindow_->addAction(chatMessage, senderName, senderIsSelf, displayedLabel, pathToString(avatarPath), time); + } + else { + return chatWindow_->addMessage(chatMessage, senderName, senderIsSelf, displayedLabel, pathToString(avatarPath), time); + } } -void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { - if (boost::starts_with(message, "/me ")) { - chatWindow_->replaceWithAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), id, time, highlight); - } else { - chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message), id, time, highlight); - } +void ChatControllerBase::replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time) { + if (chatMessage.isMeCommand()) { + chatWindow_->replaceWithAction(chatMessage, id, time); + } + else { + chatWindow_->replaceMessage(chatMessage, id, time); + } } bool ChatControllerBase::isFromContact(const JID& from) { - return from.toBare() == toJID_.toBare(); + return from.toBare() == toJID_.toBare(); } -void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { - preHandleIncomingMessage(messageEvent); - if (messageEvent->isReadable() && !messageEvent->getConcluded()) { - unreadMessages_.push_back(messageEvent); - if (messageEvent->targetsMe()) { - targetedUnreadMessages_.push_back(messageEvent); - } - } - boost::shared_ptr<Message> message = messageEvent->getStanza(); - std::string body = message->getBody(); - HighlightAction highlight; - if (message->isError()) { - if (!message->getTo().getResource().empty()) { - std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>())); - chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage)); - } - } - else if (messageEvent->getStanza()->getPayload<MUCInvitationPayload>()) { - handleMUCInvitation(messageEvent->getStanza()); - return; - } - else if (messageEvent->getStanza()->getPayload<MUCUserPayload>() && messageEvent->getStanza()->getPayload<MUCUserPayload>()->getInvite()) { - handleMediatedMUCInvitation(messageEvent->getStanza()); - return; - } - else { - if (!messageEvent->isReadable()) { - return; - } - showChatWindow(); - JID from = message->getFrom(); - std::vector<boost::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>(); - for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { - if (!delayPayloads[i]->getFrom()) { - continue; - } - boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); - std::ostringstream s; - s << "The following message took " << (now - delayPayloads[i]->getStamp()).total_milliseconds() / 1000.0 << " seconds to be delivered from " << delayPayloads[i]->getFrom()->toString() << "."; - chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(std::string(s.str())), ChatWindow::DefaultDirection); - } - boost::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>(); - - // Determine the timestamp - boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time(); - boost::optional<boost::posix_time::ptime> messageTimeStamp = getMessageTimestamp(message); - if (messageTimeStamp) { - timeStamp = *messageTimeStamp; - } - onActivity(body); - - // Highlight - if (!isIncomingMessageFromMe(message)) { - highlight = highlighter_->findAction(body, senderDisplayNameFromMessage(from)); - } - - boost::shared_ptr<Replace> replace = message->getPayload<Replace>(); - if (replace) { - std::string body = message->getBody(); - // Should check if the user has a previous message - std::map<JID, std::string>::iterator lastMessage; - lastMessage = lastMessagesUIID_.find(from); - if (lastMessage != lastMessagesUIID_.end()) { - replaceMessage(body, lastMessagesUIID_[from], timeStamp, highlight); - } - } - else { - lastMessagesUIID_[from] = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, avatarManager_->getAvatarPath(from), timeStamp, highlight); - } - - logMessage(body, from, selfJID_, timeStamp, true); - } - chatWindow_->show(); - chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size())); - onUnreadCountChanged(); - postHandleIncomingMessage(messageEvent, highlight); +void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) { + std::shared_ptr<Message> message = messageEvent->getStanza(); + if (shouldIgnoreMessage(message)) { + return; + } + + preHandleIncomingMessage(messageEvent); + if (messageEvent->isReadable() && !messageEvent->getConcluded()) { + unreadMessages_.push_back(messageEvent); + if (messageEvent->targetsMe()) { + targetedUnreadMessages_.push_back(messageEvent); + } + } + + ChatWindow::ChatMessage chatMessage; + boost::optional<std::string> optionalBody = message->getBody(); + std::string body = optionalBody.get_value_or(""); + if (message->isError()) { + if (!message->getTo().getResource().empty()) { + std::string errorMessage; + if (message->getPayload<Swift::ErrorPayload>()->getCondition() == ErrorPayload::ItemNotFound) { + errorMessage = QT_TRANSLATE_NOOP("", "This user could not be found in the room."); + } + else { + errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>())); + } + chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage)); + } + } + else if (messageEvent->getStanza()->getPayload<MUCInvitationPayload>()) { + handleMUCInvitation(messageEvent->getStanza()); + return; + } + else if (messageEvent->getStanza()->getPayload<MUCUserPayload>() && messageEvent->getStanza()->getPayload<MUCUserPayload>()->getInvite()) { + handleMediatedMUCInvitation(messageEvent->getStanza()); + return; + } + else { + if (!messageEvent->isReadable()) { + return; + } + showChatWindow(); + JID from = message->getFrom(); + std::vector<std::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>(); + for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { + if (!delayPayloads[i]->getFrom()) { + continue; + } + boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); + std::ostringstream s; + s << "The following message took " << (now - delayPayloads[i]->getStamp()).total_milliseconds() / 1000.0 << " seconds to be delivered from " << delayPayloads[i]->getFrom()->toString() << "."; + chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(std::string(s.str())), ChatWindow::DefaultDirection); + } + std::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>(); + + // Determine the timestamp + boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time(); + boost::optional<boost::posix_time::ptime> messageTimeStamp = getMessageTimestamp(message); + if (messageTimeStamp) { + timeStamp = *messageTimeStamp; + } + onActivity(body); + + std::shared_ptr<Replace> replace = message->getPayload<Replace>(); + bool senderIsSelf = isIncomingMessageFromMe(message); + chatMessage = buildChatWindowChatMessage(body, senderHighlightNameFromMessage(from), senderIsSelf); + if (replace) { + handleIncomingReplaceMessage(from, chatMessage, message->getID(), replace->getID(), senderIsSelf, label, timeStamp); + } + else { + addMessageHandleIncomingMessage(from, chatMessage, message->getID(), senderIsSelf, label, timeStamp); + } + +#ifdef SWIFT_EXPERIMENTAL_HISTORY + logMessage(body, from, selfJID_, timeStamp, true); +#endif + } + chatWindow_->show(); + updateMessageCount(); + postHandleIncomingMessage(messageEvent, chatMessage); } -std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload> error) { - std::string defaultMessage = QT_TRANSLATE_NOOP("", "Error sending message"); - if (!error->getText().empty()) { - return error->getText(); - } - else { - switch (error->getCondition()) { - case ErrorPayload::BadRequest: return QT_TRANSLATE_NOOP("", "Bad request"); - case ErrorPayload::Conflict: return QT_TRANSLATE_NOOP("", "Conflict"); - case ErrorPayload::FeatureNotImplemented: return QT_TRANSLATE_NOOP("", "This feature is not implemented"); - case ErrorPayload::Forbidden: return QT_TRANSLATE_NOOP("", "Forbidden"); - case ErrorPayload::Gone: return QT_TRANSLATE_NOOP("", "Recipient can no longer be contacted"); - case ErrorPayload::InternalServerError: return QT_TRANSLATE_NOOP("", "Internal server error"); - case ErrorPayload::ItemNotFound: return QT_TRANSLATE_NOOP("", "Item not found"); - case ErrorPayload::JIDMalformed: return QT_TRANSLATE_NOOP("", "JID Malformed"); - case ErrorPayload::NotAcceptable: return QT_TRANSLATE_NOOP("", "Message was rejected"); - case ErrorPayload::NotAllowed: return QT_TRANSLATE_NOOP("", "Not allowed"); - case ErrorPayload::NotAuthorized: return QT_TRANSLATE_NOOP("", "Not authorized"); - case ErrorPayload::PaymentRequired: return QT_TRANSLATE_NOOP("", "Payment is required"); - case ErrorPayload::RecipientUnavailable: return QT_TRANSLATE_NOOP("", "Recipient is unavailable"); - case ErrorPayload::Redirect: return QT_TRANSLATE_NOOP("", "Redirect"); - case ErrorPayload::RegistrationRequired: return QT_TRANSLATE_NOOP("", "Registration required"); - case ErrorPayload::RemoteServerNotFound: return QT_TRANSLATE_NOOP("", "Recipient's server not found"); - case ErrorPayload::RemoteServerTimeout: return QT_TRANSLATE_NOOP("", "Remote server timeout"); - case ErrorPayload::ResourceConstraint: return QT_TRANSLATE_NOOP("", "The server is low on resources"); - case ErrorPayload::ServiceUnavailable: return QT_TRANSLATE_NOOP("", "The service is unavailable"); - case ErrorPayload::SubscriptionRequired: return QT_TRANSLATE_NOOP("", "A subscription is required"); - case ErrorPayload::UndefinedCondition: return QT_TRANSLATE_NOOP("", "Undefined condition"); - case ErrorPayload::UnexpectedRequest: return QT_TRANSLATE_NOOP("", "Unexpected request"); - } - } - assert(false); - return defaultMessage; +std::string ChatControllerBase::getErrorMessage(std::shared_ptr<ErrorPayload> error) { + std::string defaultMessage = QT_TRANSLATE_NOOP("", "Error sending message"); + if (!error->getText().empty()) { + return error->getText(); + } + else { + switch (error->getCondition()) { + case ErrorPayload::BadRequest: return QT_TRANSLATE_NOOP("", "Bad request"); + case ErrorPayload::Conflict: return QT_TRANSLATE_NOOP("", "Conflict"); + case ErrorPayload::FeatureNotImplemented: return QT_TRANSLATE_NOOP("", "This feature is not implemented"); + case ErrorPayload::Forbidden: return QT_TRANSLATE_NOOP("", "Forbidden"); + case ErrorPayload::Gone: return QT_TRANSLATE_NOOP("", "Recipient can no longer be contacted"); + case ErrorPayload::InternalServerError: return QT_TRANSLATE_NOOP("", "Internal server error"); + case ErrorPayload::ItemNotFound: return QT_TRANSLATE_NOOP("", "Item not found"); + case ErrorPayload::JIDMalformed: return QT_TRANSLATE_NOOP("", "JID Malformed"); + case ErrorPayload::NotAcceptable: return QT_TRANSLATE_NOOP("", "Message was rejected"); + case ErrorPayload::NotAllowed: return QT_TRANSLATE_NOOP("", "Not allowed"); + case ErrorPayload::NotAuthorized: return QT_TRANSLATE_NOOP("", "Not authorized"); + case ErrorPayload::PaymentRequired: return QT_TRANSLATE_NOOP("", "Payment is required"); + case ErrorPayload::RecipientUnavailable: return QT_TRANSLATE_NOOP("", "Recipient is unavailable"); + case ErrorPayload::Redirect: return QT_TRANSLATE_NOOP("", "Redirect"); + case ErrorPayload::RegistrationRequired: return QT_TRANSLATE_NOOP("", "Registration required"); + case ErrorPayload::RemoteServerNotFound: return QT_TRANSLATE_NOOP("", "Recipient's server not found"); + case ErrorPayload::RemoteServerTimeout: return QT_TRANSLATE_NOOP("", "Remote server timeout"); + case ErrorPayload::ResourceConstraint: return QT_TRANSLATE_NOOP("", "The server is low on resources"); + case ErrorPayload::ServiceUnavailable: return QT_TRANSLATE_NOOP("", "The service is unavailable"); + case ErrorPayload::SubscriptionRequired: return QT_TRANSLATE_NOOP("", "A subscription is required"); + case ErrorPayload::UndefinedCondition: return QT_TRANSLATE_NOOP("", "Undefined condition"); + case ErrorPayload::UnexpectedRequest: return QT_TRANSLATE_NOOP("", "Unexpected request"); + } + } + assert(false); + return defaultMessage; } void ChatControllerBase::handleGeneralMUCInvitation(MUCInviteEvent::ref event) { - unreadMessages_.push_back(event); - chatWindow_->show(); - chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size())); - onUnreadCountChanged(); - chatWindow_->addMUCInvitation(senderDisplayNameFromMessage(event->getInviter()), event->getRoomJID(), event->getReason(), event->getPassword(), event->getDirect(), event->getImpromptu()); - eventController_->handleIncomingEvent(event); + unreadMessages_.push_back(event); + chatWindow_->show(); + updateMessageCount(); + chatWindow_->addMUCInvitation(senderDisplayNameFromMessage(event->getInviter()), event->getRoomJID(), event->getReason(), event->getPassword(), event->getDirect(), event->getImpromptu()); + eventController_->handleIncomingEvent(event); + lastWasPresence_ = false; } void ChatControllerBase::handleMUCInvitation(Message::ref message) { - MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>(); - - if (autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) { - eventStream_->send(boost::make_shared<JoinMUCUIEvent>(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true)); - } else { - MUCInviteEvent::ref inviteEvent = boost::make_shared<MUCInviteEvent>(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true, invite->getIsImpromptu()); - handleGeneralMUCInvitation(inviteEvent); - } + MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>(); + + if (autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) { + eventStream_->send(std::make_shared<JoinMUCUIEvent>(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, true)); + } else { + MUCInviteEvent::ref inviteEvent = std::make_shared<MUCInviteEvent>(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true, invite->getIsImpromptu()); + handleGeneralMUCInvitation(inviteEvent); + } } void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) { - MUCUserPayload::Invite invite = *message->getPayload<MUCUserPayload>()->getInvite(); - JID from = message->getFrom(); - std::string reason; - if (!invite.reason.empty()) { - reason = invite.reason; - } - std::string password; - if (message->getPayload<MUCUserPayload>()->getPassword()) { - password = *message->getPayload<MUCUserPayload>()->getPassword(); - } - - MUCInviteEvent::ref inviteEvent = boost::make_shared<MUCInviteEvent>(invite.from, from, reason, password, false, false); - handleGeneralMUCInvitation(inviteEvent); + MUCUserPayload::Invite invite = *message->getPayload<MUCUserPayload>()->getInvite(); + JID from = message->getFrom(); + std::string reason; + if (!invite.reason.empty()) { + reason = invite.reason; + } + std::string password; + if (message->getPayload<MUCUserPayload>()->getPassword()) { + password = *message->getPayload<MUCUserPayload>()->getPassword(); + } + + MUCInviteEvent::ref inviteEvent = std::make_shared<MUCInviteEvent>(invite.from, from, reason, password, false, false); + handleGeneralMUCInvitation(inviteEvent); +} + +void ChatControllerBase::handleResendMessageRequest(const std::string& id) { + if (failedStanzas_.find(id) != failedStanzas_.end()) { + if (auto resendMsg = std::dynamic_pointer_cast<Message>(failedStanzas_[id])) { + stanzaChannel_->sendMessage(resendMsg); + if (stanzaChannel_->getStreamManagementEnabled()) { + chatWindow_->setAckState(id, ChatWindow::Pending); + unackedStanzas_[failedStanzas_[id]] = id; + } + if (resendMsg->getPayload<DeliveryReceiptRequest>()) { + requestedReceipts_[resendMsg->getID()] = id; + chatWindow_->setMessageReceiptState(id, ChatWindow::ReceiptRequested); + } + lastWasPresence_ = false; + failedStanzas_.erase(id); + } + } } } |