summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp2
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp8
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp2
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp9
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp115
-rw-r--r--Swift/Controllers/EventNotifier.cpp36
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h4
-rw-r--r--Swift/Controllers/XMPPEvents/MessageEvent.h8
-rw-r--r--Swift/QtUI/EventViewer/QtEvent.cpp4
-rw-r--r--Swiften/Elements/Message.h29
-rw-r--r--Swiften/Elements/Stanza.cpp16
-rw-r--r--Swiften/Elements/Stanza.h10
-rw-r--r--Swiften/Examples/MUCListAndJoin/MUCListAndJoin.cpp4
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/ForwardedParserTest.cpp10
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/MAMResultParserTest.cpp8
-rw-r--r--Swiftob/Commands.cpp9
-rw-r--r--Swiftob/LuaCommands.cpp6
-rw-r--r--Swiftob/Swiftob.cpp12
18 files changed, 215 insertions, 77 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index eb86766..11ba89e 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -150,61 +150,61 @@ void ChatController::setToJID(const JID& jid) {
}
void ChatController::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) {
ChatControllerBase::setAvailableServerFeatures(info);
if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::BlockingCommandFeature)) {
boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
handleBlockingStateChanged();
}
}
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
return false;
}
void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
if (messageEvent->isReadable()) {
chatWindow_->flash();
lastWasPresence_ = false;
}
boost::shared_ptr<Message> message = messageEvent->getStanza();
JID from = message->getFrom();
if (!from.equals(toJID_, JID::WithResource)) {
if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){
// Bind controller to a full JID if message contains body text or is a typing chat state.
ChatState::ref chatState = message->getPayload<ChatState>();
- if (!message->getBody().empty() || (chatState && chatState->getChatState() == ChatState::Composing)) {
+ if (!message->getBody().get_value_or("").empty() || (chatState && chatState->getChatState() == ChatState::Composing)) {
setToJID(from);
}
}
}
chatStateTracker_->handleMessageReceived(message);
chatStateNotifier_->receivedMessageFromContact(!!message->getPayload<ChatState>());
// handle XEP-0184 Message Receipts
// incomming receipts
if (boost::shared_ptr<DeliveryReceipt> receipt = message->getPayload<DeliveryReceipt>()) {
SWIFT_LOG(debug) << "received receipt for id: " << receipt->getReceivedID() << std::endl;
if (requestedReceipts_.find(receipt->getReceivedID()) != requestedReceipts_.end()) {
chatWindow_->setMessageReceiptState(requestedReceipts_[receipt->getReceivedID()], ChatWindow::ReceiptReceived);
requestedReceipts_.erase(receipt->getReceivedID());
}
// incomming errors in response to send out receipts
} else if (message->getPayload<DeliveryReceiptRequest>() && (message->getType() == Message::Error)) {
if (requestedReceipts_.find(message->getID()) != requestedReceipts_.end()) {
chatWindow_->setMessageReceiptState(requestedReceipts_[message->getID()], ChatWindow::ReceiptFailed);
requestedReceipts_.erase(message->getID());
}
// incoming receipt requests
} else if (message->getPayload<DeliveryReceiptRequest>()) {
if (receivingPresenceFromUs_) {
boost::shared_ptr<Message> receiptMessage = boost::make_shared<Message>();
receiptMessage->setTo(toJID_);
receiptMessage->addPayload(boost::make_shared<DeliveryReceipt>(message->getID()));
stanzaChannel_->sendMessage(receiptMessage);
}
}
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 4a84a6e..fef3e7a 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -145,62 +145,62 @@ int ChatControllerBase::getUnreadCount() {
}
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());
+ postSendMessage(message->getBody().get(), boost::dynamic_pointer_cast<Stanza>(message));
+ onActivity(message->getBody().get());
#ifdef SWIFT_EXPERIMENTAL_HISTORY
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::showChatWindow() {
chatWindow_->show();
}
void ChatControllerBase::activateChatWindow() {
chatWindow_->activate();
}
@@ -210,110 +210,110 @@ bool ChatControllerBase::hasOpenWindow() const {
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,highlighter_->getNick(),senderIsSelf), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
}
}
void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, bool senderIsSelf, 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,highlighter_->getNick(),senderIsSelf), id, time, highlight);
}
}
bool ChatControllerBase::isFromContact(const JID& from) {
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();
+ std::string body = message->getBody().get_value_or("");
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, senderHighlightNameFromMessage(from));
}
boost::shared_ptr<Replace> replace = message->getPayload<Replace>();
if (replace) {
- std::string body = message->getBody();
+ std::string body = message->getBody().get_value_or("");
// 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], isIncomingMessageFromMe(message), timeStamp, highlight);
}
}
else {
addMessageHandleIncomingMessage(from, body, isIncomingMessageFromMe(message), label, timeStamp, highlight);
}
logMessage(body, from, selfJID_, timeStamp, true);
}
chatWindow_->show();
chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size()));
onUnreadCountChanged();
postHandleIncomingMessage(messageEvent, highlight);
}
void ChatControllerBase::addMessageHandleIncomingMessage(const JID& from, const std::string& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp, const HighlightAction& highlight) {
lastMessagesUIID_[from] = addMessage(message, senderDisplayNameFromMessage(from), senderIsSelf, label, avatarManager_->getAvatarPath(from), timeStamp, highlight);
}
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()) {
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 1a69982..49caee4 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -890,61 +890,61 @@ void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
return;
}
}
// check for impromptu invite to potentially auto-accept
MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>();
if (invite && autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) {
if (invite->getIsContinuation()) {
// check for existing chat controller for the from JID
ChatController* controller = getChatControllerIfExists(jid);
if (controller) {
ChatWindow* window = controller->detachChatWindow();
chatControllers_.erase(jid);
delete controller;
handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true, window);
return;
}
} else {
handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true);
return;
}
}
//if not a mucroom
if (!event->isReadable() && !isInvite && !isMediatedInvite) {
/* Only route such messages if a window exists, don't open new windows for them.*/
// Do not bind a controller to a full JID, for delivery receipts or chat state notifications.
bool bindControllerToJID = false;
ChatState::ref chatState = message->getPayload<ChatState>();
- if (!message->getBody().empty() || (chatState && chatState->getChatState() == ChatState::Composing)) {
+ if (!message->getBody().get_value_or("").empty() || (chatState && chatState->getChatState() == ChatState::Composing)) {
bindControllerToJID = true;
}
ChatController* controller = getChatControllerIfExists(jid, bindControllerToJID);
if (controller) {
controller->handleIncomingMessage(event);
}
} else {
getChatControllerOrCreate(jid)->handleIncomingMessage(event);
}
}
void ChatsManager::handleMUCSelectedAfterSearch(const JID& muc) {
if (joinMUCWindow_) {
joinMUCWindow_->setMUC(muc.toString());
}
}
void ChatsManager::handleMUCBookmarkActivated(const MUCBookmark& mucBookmark) {
uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(mucBookmark.getRoom(), mucBookmark.getPassword(), mucBookmark.getNick()));
}
void ChatsManager::handleNewFileTransferController(FileTransferController* ftc) {
ChatController* chatController = getChatControllerOrCreate(ftc->getOtherParty());
chatController->handleNewFileTransferController(ftc);
chatController->activateChatWindow();
if (ftc->isIncoming()) {
eventController_->handleIncomingEvent(boost::make_shared<IncomingFileTransferEvent>(ftc->getOtherParty()));
}
}
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index e6c16b7..409fe1f 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -1,54 +1,55 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Chat/MUCController.h>
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/format.h>
#include <Swiften/Base/Tristate.h>
#include <Swiften/Client/BlockList.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
#include <Swiften/Elements/Delay.h>
+#include <Swiften/Elements/Thread.h>
#include <Swiften/MUC/MUC.h>
#include <Swiften/MUC/MUCBookmark.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <SwifTools/TabComplete.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
#include <Swift/Controllers/Highlighter.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
#include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h>
#include <Swift/Controllers/Roster/ItemOperations/SetMUC.h>
#include <Swift/Controllers/Roster/ItemOperations/SetPresence.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterVCardProvider.h>
#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.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>
@@ -504,89 +505,89 @@ void MUCController::clearPresenceQueue() {
std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) {
switch (role) {
case MUCOccupant::Moderator: return QT_TRANSLATE_NOOP("", "moderator");
case MUCOccupant::Participant: return QT_TRANSLATE_NOOP("", "participant");
case MUCOccupant::Visitor: return QT_TRANSLATE_NOOP("", "visitor");
case MUCOccupant::NoRole: return "";
}
assert(false);
return "";
}
std::string MUCController::roleToSortName(MUCOccupant::Role role) {
switch (role) {
case MUCOccupant::Moderator: return "1";
case MUCOccupant::Participant: return "2";
case MUCOccupant::Visitor: return "3";
case MUCOccupant::NoRole: return "4";
}
assert(false);
return "5";
}
JID MUCController::nickToJID(const std::string& nick) {
return muc_->getJID().withResource(nick);
}
bool MUCController::messageTargetsMe(boost::shared_ptr<Message> message) {
std::string stringRegexp(".*\\b" + boost::to_lower_copy(nick_) + "\\b.*");
boost::regex myRegexp(stringRegexp);
- return boost::regex_match(boost::to_lower_copy(message->getBody()), myRegexp);
+ return boost::regex_match(boost::to_lower_copy(message->getBody().get_value_or("")), myRegexp);
}
void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
if (messageEvent->getStanza()->getType() == Message::Groupchat) {
lastActivity_ = boost::posix_time::microsec_clock::universal_time();
}
clearPresenceQueue();
boost::shared_ptr<Message> message = messageEvent->getStanza();
if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>() && messageEvent->isReadable()) {
chatWindow_->flash();
}
else {
messageEvent->setTargetsMe(false);
}
if (messageEvent->isReadable() && isImpromptu_) {
chatWindow_->flash(); /* behave like a regular char*/
}
if (joined_) {
std::string nick = message->getFrom().getResource();
if (nick != nick_ && currentOccupants_.find(nick) != currentOccupants_.end()) {
completer_->addWord(nick);
}
}
/*Buggy implementations never send the status code, so use an incoming message as a hint that joining's done (e.g. the old ejabberd on psi-im.org).*/
receivedActivity();
joined_ = true;
- if (message->hasSubject() && message->getBody().empty()) {
+ if (message->hasSubject() && !message->getPayload<Body>() && !message->getPayload<Thread>()) {
chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % message->getSubject())), ChatWindow::DefaultDirection);
chatWindow_->setSubject(message->getSubject());
doneGettingHistory_ = true;
}
if (!doneGettingHistory_ && !message->getPayload<Delay>()) {
doneGettingHistory_ = true;
}
if (!doneGettingHistory_) {
checkDuplicates(message);
messageEvent->conclude();
}
}
void MUCController::addMessageHandleIncomingMessage(const JID& from, const std::string& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
if (from.isBare()) {
chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "%1%")) % message)), ChatWindow::DefaultDirection);
}
else {
ChatControllerBase::addMessageHandleIncomingMessage(from, message, senderIsSelf, label, time, highlight);
}
}
void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) {
boost::shared_ptr<Message> message = messageEvent->getStanza();
if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && !message->getPayload<Delay>()) {
if (messageTargetsMe(message) || isImpromptu_) {
eventController_->handleIncomingEvent(messageEvent);
}
@@ -1051,61 +1052,61 @@ void MUCController::handleBlockingStateChanged() {
}
}
void MUCController::handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids) {
chatWindow_->setAffiliations(affiliation, jids);
}
void MUCController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) {
// log only incoming messages
if (isIncoming && historyController_) {
historyController_->addMessage(message, fromJID, toJID, HistoryMessage::Groupchat, timeStamp);
}
}
void MUCController::addRecentLogs() {
if (!historyController_) {
return;
}
joinContext_ = historyController_->getMUCContext(selfJID_, toJID_, lastActivity_);
foreach (const HistoryMessage& message, joinContext_) {
bool senderIsSelf = nick_ == message.getFromJID().getResource();
// the chatWindow uses utc timestamps
addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), avatarManager_->getAvatarPath(message.getFromJID()), message.getTime() - boost::posix_time::hours(message.getOffset()), HighlightAction());
}
}
void MUCController::checkDuplicates(boost::shared_ptr<Message> newMessage) {
- std::string body = newMessage->getBody();
+ std::string body = newMessage->getBody().get_value_or("");
JID jid = newMessage->getFrom();
boost::optional<boost::posix_time::ptime> time = newMessage->getTimestamp();
reverse_foreach (const HistoryMessage& message, joinContext_) {
boost::posix_time::ptime messageTime = message.getTime() - boost::posix_time::hours(message.getOffset());
if (time && time < messageTime) {
break;
}
if (time && time != messageTime) {
continue;
}
if (message.getFromJID() != jid) {
continue;
}
if (message.getMessage() != body) {
continue;
}
// Mark the message as unreadable
newMessage->setBody("");
}
}
void MUCController::setNick(const std::string& nick) {
nick_ = nick;
highlighter_->setNick(nick_);
}
Form::ref MUCController::buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm) {
Form::ref result = boost::make_shared<Form>(Form::SubmitType);
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index bc6ada2..e8fc41d 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -1,90 +1,95 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <boost/algorithm/string.hpp>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <hippomocks.h>
#include <Swiften/Avatars/NullAvatarManager.h>
#include <Swiften/Base/foreach.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/DummyStanzaChannel.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
#include <Swiften/Disco/DummyEntityCapsProvider.h>
#include <Swiften/Elements/MUCUserPayload.h>
+#include <Swiften/Elements/Thread.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swiften/MUC/UnitTest/MockMUC.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Presence/DirectedPresenceSender.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Presence/StanzaChannelPresenceSender.h>
#include <Swiften/Queries/DummyIQChannel.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <Swiften/Roster/XMPPRosterImpl.h>
#include <Swiften/VCards/VCardManager.h>
#include <Swiften/VCards/VCardMemoryStorage.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
#include <Swift/Controllers/Chat/MUCController.h>
#include <Swift/Controllers/Chat/UserSearchController.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Settings/DummySettingsProvider.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h>
#include <Swift/Controllers/UnitTest/MockChatWindow.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
using namespace Swift;
class MUCControllerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(MUCControllerTest);
CPPUNIT_TEST(testJoinPartStringContructionSimple);
CPPUNIT_TEST(testJoinPartStringContructionMixed);
CPPUNIT_TEST(testAppendToJoinParts);
CPPUNIT_TEST(testAddressedToSelf);
CPPUNIT_TEST(testNotAddressedToSelf);
CPPUNIT_TEST(testAddressedToSelfBySelf);
CPPUNIT_TEST(testMessageWithEmptyLabelItem);
CPPUNIT_TEST(testMessageWithLabelItem);
CPPUNIT_TEST(testCorrectMessageWithLabelItem);
CPPUNIT_TEST(testRoleAffiliationStates);
+ CPPUNIT_TEST(testSubjectChangeCorrect);
+ CPPUNIT_TEST(testSubjectChangeIncorrectA);
+ CPPUNIT_TEST(testSubjectChangeIncorrectB);
+ CPPUNIT_TEST(testSubjectChangeIncorrectC);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
crypto_ = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create());
self_ = JID("girl@wonderland.lit/rabbithole");
nick_ = "aLiCe";
mucJID_ = JID("teaparty@rooms.wonderland.lit");
mocks_ = new MockRepository();
stanzaChannel_ = new DummyStanzaChannel();
iqChannel_ = new DummyIQChannel();
iqRouter_ = new IQRouter(iqChannel_);
eventController_ = new EventController();
chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
userSearchWindowFactory_ = mocks_->InterfaceMock<UserSearchWindowFactory>();
xmppRoster_ = new XMPPRosterImpl();
presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_);
presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_);
directedPresenceSender_ = new DirectedPresenceSender(presenceSender_);
uiEventStream_ = new UIEventStream();
avatarManager_ = new NullAvatarManager();
TimerFactory* timerFactory = NULL;
window_ = new MockChatWindow();
mucRegistry_ = new MUCRegistry();
entityCapsProvider_ = new DummyEntityCapsProvider();
settings_ = new DummySettingsProvider();
highlightManager_ = new HighlightManager(settings_);
muc_ = boost::make_shared<MockMUC>(mucJID_);
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_);
chatMessageParser_ = boost::make_shared<ChatMessageParser>(std::map<std::string, std::string>(), highlightManager_->getRules(), true);
@@ -188,126 +193,126 @@ public:
void testAddressedToSelfBySelf() {
finishJoin();
Message::ref message(new Message());
message->setFrom(JID(muc_->getJID().toString() + "/" + nick_));
message->setBody("Hi there " + nick_);
message->setType(Message::Groupchat);
controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
CPPUNIT_ASSERT_EQUAL((size_t)0, eventController_->getEvents().size());
}
void testMessageWithEmptyLabelItem() {
SecurityLabelsCatalog::Item label;
label.setSelector("Bob");
window_->label_ = label;
boost::shared_ptr<DiscoInfo> features = boost::make_shared<DiscoInfo>();
features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
controller_->setAvailableServerFeatures(features);
IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
SecurityLabelsCatalog::ref labelPayload = boost::make_shared<SecurityLabelsCatalog>();
labelPayload->addItem(label);
IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
iqChannel_->onIQReceived(result);
std::string messageBody("agamemnon");
window_->onSendMessageRequest(messageBody, false);
boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
CPPUNIT_ASSERT_EQUAL(iq->getTo(), result->getFrom());
CPPUNIT_ASSERT(window_->labelsEnabled_);
CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
CPPUNIT_ASSERT(message);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
CPPUNIT_ASSERT(!message->getPayload<SecurityLabel>());
}
void testMessageWithLabelItem() {
boost::shared_ptr<SecurityLabel> label = boost::make_shared<SecurityLabel>();
label->setLabel("a");
SecurityLabelsCatalog::Item labelItem;
labelItem.setSelector("Bob");
labelItem.setLabel(label);
window_->label_ = labelItem;
boost::shared_ptr<DiscoInfo> features = boost::make_shared<DiscoInfo>();
features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
controller_->setAvailableServerFeatures(features);
IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
SecurityLabelsCatalog::ref labelPayload = boost::make_shared<SecurityLabelsCatalog>();
labelPayload->addItem(labelItem);
IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
iqChannel_->onIQReceived(result);
std::string messageBody("agamemnon");
window_->onSendMessageRequest(messageBody, false);
boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
CPPUNIT_ASSERT_EQUAL(iq->getTo(), result->getFrom());
CPPUNIT_ASSERT(window_->labelsEnabled_);
CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
CPPUNIT_ASSERT(message);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
CPPUNIT_ASSERT_EQUAL(label, message->getPayload<SecurityLabel>());
}
void testCorrectMessageWithLabelItem() {
boost::shared_ptr<SecurityLabel> label = boost::make_shared<SecurityLabel>();
label->setLabel("a");
SecurityLabelsCatalog::Item labelItem;
labelItem.setSelector("Bob");
labelItem.setLabel(label);
boost::shared_ptr<SecurityLabel> label2 = boost::make_shared<SecurityLabel>();
label->setLabel("b");
SecurityLabelsCatalog::Item labelItem2;
labelItem2.setSelector("Charlie");
labelItem2.setLabel(label2);
window_->label_ = labelItem;
boost::shared_ptr<DiscoInfo> features = boost::make_shared<DiscoInfo>();
features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
controller_->setAvailableServerFeatures(features);
IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
SecurityLabelsCatalog::ref labelPayload = boost::make_shared<SecurityLabelsCatalog>();
labelPayload->addItem(labelItem);
IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
iqChannel_->onIQReceived(result);
std::string messageBody("agamemnon");
window_->onSendMessageRequest(messageBody, false);
boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
CPPUNIT_ASSERT_EQUAL(iq->getTo(), result->getFrom());
CPPUNIT_ASSERT(window_->labelsEnabled_);
CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
CPPUNIT_ASSERT(message);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
CPPUNIT_ASSERT_EQUAL(label, message->getPayload<SecurityLabel>());
window_->label_ = labelItem2;
window_->onSendMessageRequest(messageBody, true);
rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
message = boost::dynamic_pointer_cast<Message>(rawStanza);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
CPPUNIT_ASSERT_EQUAL(label, message->getPayload<SecurityLabel>());
}
void checkEqual(const std::vector<NickJoinPart>& expected, const std::vector<NickJoinPart>& actual) {
CPPUNIT_ASSERT_EQUAL(expected.size(), actual.size());
for (size_t i = 0; i < expected.size(); i++) {
CPPUNIT_ASSERT_EQUAL(expected[i].nick, actual[i].nick);
CPPUNIT_ASSERT_EQUAL(expected[i].type, actual[i].type);
}
}
void testAppendToJoinParts() {
std::vector<NickJoinPart> list;
std::vector<NickJoinPart> gold;
MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join));
gold.push_back(NickJoinPart("Kev", Join));
checkEqual(gold, list);
MUCController::appendToJoinParts(list, NickJoinPart("Remko", Join));
gold.push_back(NickJoinPart("Remko", Join));
checkEqual(gold, list);
MUCController::appendToJoinParts(list, NickJoinPart("Bert", Join));
gold.push_back(NickJoinPart("Bert", Join));
checkEqual(gold, list);
MUCController::appendToJoinParts(list, NickJoinPart("Bert", Part));
gold[2].type = JoinThenPart;
checkEqual(gold, list);
MUCController::appendToJoinParts(list, NickJoinPart("Kev", Part));
gold[0].type = JoinThenPart;
checkEqual(gold, list);
MUCController::appendToJoinParts(list, NickJoinPart("Remko", Part));
@@ -371,60 +376,160 @@ public:
muc_->insertOccupant(occupant.second);
}
std::vector<MUCOccupant> alterations;
alterations.push_back(MUCOccupant("Kev", MUCOccupant::Visitor, MUCOccupant::Admin));
alterations.push_back(MUCOccupant("Remko", MUCOccupant::Moderator, MUCOccupant::Member));
alterations.push_back(MUCOccupant("Bert", MUCOccupant::Visitor, MUCOccupant::Outcast));
alterations.push_back(MUCOccupant("Ernie", MUCOccupant::NoRole, MUCOccupant::Member));
alterations.push_back(MUCOccupant("Bert", MUCOccupant::Moderator, MUCOccupant::Owner));
alterations.push_back(MUCOccupant("Kev", MUCOccupant::Participant, MUCOccupant::Outcast));
alterations.push_back(MUCOccupant("Bert", MUCOccupant::Visitor, MUCOccupant::NoAffiliation));
alterations.push_back(MUCOccupant("Remko", MUCOccupant::NoRole, MUCOccupant::NoAffiliation));
alterations.push_back(MUCOccupant("Ernie", MUCOccupant::Visitor, MUCOccupant::Outcast));
foreach(const MUCOccupant& alteration, alterations) {
/* perform an alteration to a user's role and affiliation */
occupant_map::iterator occupant = occupants.find(alteration.getNick());
CPPUNIT_ASSERT(occupant != occupants.end());
const JID jid = jidFromOccupant(occupant->second);
/* change the affiliation, leave the role in place */
muc_->changeAffiliation(jid, alteration.getAffiliation());
occupant->second = MUCOccupant(occupant->first, occupant->second.getRole(), alteration.getAffiliation());
testRoleAffiliationStatesVerify(occupants);
/* change the role, leave the affiliation in place */
muc_->changeOccupantRole(jid, alteration.getRole());
occupant->second = MUCOccupant(occupant->first, alteration.getRole(), occupant->second.getAffiliation());
testRoleAffiliationStatesVerify(occupants);
}
}
+ void testSubjectChangeCorrect() {
+ std::string messageBody("test message");
+ window_->onSendMessageRequest(messageBody, false);
+ boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
+ CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ CPPUNIT_ASSERT(message);
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get_value_or(""));
+
+ {
+ Message::ref message = boost::make_shared<Message>();
+ message->setType(Message::Groupchat);
+ message->setTo(self_);
+ message->setFrom(mucJID_.withResource("SomeNickname"));
+ message->setID(iqChannel_->getNewIQID());
+ message->setSubject("New Room Subject");
+
+ controller_->handleIncomingMessage(boost::make_shared<MessageEvent>(message));
+ CPPUNIT_ASSERT_EQUAL(std::string("The room subject is now: New Room Subject"), boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
+ }
+ }
+
+ /*
+ * Test that message stanzas with subject element and non-empty body element do not cause a subject change.
+ */
+ void testSubjectChangeIncorrectA() {
+ std::string messageBody("test message");
+ window_->onSendMessageRequest(messageBody, false);
+ boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
+ CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ CPPUNIT_ASSERT(message);
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get_value_or(""));
+
+ {
+ Message::ref message = boost::make_shared<Message>();
+ message->setType(Message::Groupchat);
+ message->setTo(self_);
+ message->setFrom(mucJID_.withResource("SomeNickname"));
+ message->setID(iqChannel_->getNewIQID());
+ message->setSubject("New Room Subject");
+ message->setBody("Some body text that prevents this stanza from being a subject change.");
+
+ controller_->handleIncomingMessage(boost::make_shared<MessageEvent>(message));
+ CPPUNIT_ASSERT_EQUAL(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
+ }
+ }
+
+ /*
+ * Test that message stanzas with subject element and thread element do not cause a subject change.
+ */
+ void testSubjectChangeIncorrectB() {
+ std::string messageBody("test message");
+ window_->onSendMessageRequest(messageBody, false);
+ boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
+ CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ CPPUNIT_ASSERT(message);
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get_value_or(""));
+
+ {
+ Message::ref message = boost::make_shared<Message>();
+ message->setType(Message::Groupchat);
+ message->setTo(self_);
+ message->setFrom(mucJID_.withResource("SomeNickname"));
+ message->setID(iqChannel_->getNewIQID());
+ message->setSubject("New Room Subject");
+ message->addPayload(boost::make_shared<Thread>("Thread that prevents the subject change."));
+
+ controller_->handleIncomingMessage(boost::make_shared<MessageEvent>(message));
+ CPPUNIT_ASSERT_EQUAL(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
+ }
+ }
+
+ /*
+ * Test that message stanzas with subject element and empty body element do not cause a subject change.
+ */
+ void testSubjectChangeIncorrectC() {
+ std::string messageBody("test message");
+ window_->onSendMessageRequest(messageBody, false);
+ boost::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = boost::dynamic_pointer_cast<Message>(rawStanza);
+ CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ CPPUNIT_ASSERT(message);
+ CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get_value_or(""));
+
+ {
+ Message::ref message = boost::make_shared<Message>();
+ message->setType(Message::Groupchat);
+ message->setTo(self_);
+ message->setFrom(mucJID_.withResource("SomeNickname"));
+ message->setID(iqChannel_->getNewIQID());
+ message->setSubject("New Room Subject");
+ message->setBody("");
+
+ controller_->handleIncomingMessage(boost::make_shared<MessageEvent>(message));
+ CPPUNIT_ASSERT_EQUAL(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
+ }
+ }
+
void testRoleAffiliationStatesVerify(const std::map<std::string, MUCOccupant> &occupants) {
/* verify that the roster is in sync */
GroupRosterItem* group = window_->getRosterModel()->getRoot();
foreach(RosterItem* rosterItem, group->getChildren()) {
GroupRosterItem* child = dynamic_cast<GroupRosterItem*>(rosterItem);
CPPUNIT_ASSERT(child);
foreach(RosterItem* childItem, child->getChildren()) {
ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(childItem);
CPPUNIT_ASSERT(item);
std::map<std::string, MUCOccupant>::const_iterator occupant = occupants.find(item->getJID().getResource());
CPPUNIT_ASSERT(occupant != occupants.end());
CPPUNIT_ASSERT(item->getMUCRole() == occupant->second.getRole());
CPPUNIT_ASSERT(item->getMUCAffiliation() == occupant->second.getAffiliation());
}
}
}
private:
JID self_;
JID mucJID_;
MockMUC::ref muc_;
std::string nick_;
DummyStanzaChannel* stanzaChannel_;
DummyIQChannel* iqChannel_;
IQRouter* iqRouter_;
EventController* eventController_;
ChatWindowFactory* chatWindowFactory_;
UserSearchWindowFactory* userSearchWindowFactory_;
MUCController* controller_;
// NickResolver* nickResolver_;
diff --git a/Swift/Controllers/EventNotifier.cpp b/Swift/Controllers/EventNotifier.cpp
index 47bb888..626fd40 100644
--- a/Swift/Controllers/EventNotifier.cpp
+++ b/Swift/Controllers/EventNotifier.cpp
@@ -1,78 +1,80 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
-#include "Swift/Controllers/EventNotifier.h"
+#include <Swift/Controllers/EventNotifier.h>
-#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
-#include <Swift/Controllers/Intl.h>
-#include <Swiften/Base/format.h>
+#include <Swiften/Avatars/AvatarManager.h>
+#include <Swiften/Client/NickResolver.h>
+#include <Swiften/JID/JID.h>
#include <Swiften/Base/String.h>
-#include "Swift/Controllers/XMPPEvents/EventController.h"
-#include "SwifTools/Notifier/Notifier.h"
-#include "Swiften/Avatars/AvatarManager.h"
-#include "Swiften/Client/NickResolver.h"
-#include "Swiften/JID/JID.h"
-#include "Swift/Controllers/XMPPEvents/MessageEvent.h"
-#include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
-#include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
-#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h"
-#include "Swift/Controllers/Settings/SettingsProvider.h"
+#include <Swiften/Base/format.h>
+
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
+#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
+#include <Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h>
+#include <Swift/Controllers/Intl.h>
+
+#include <SwifTools/Notifier/Notifier.h>
namespace Swift {
EventNotifier::EventNotifier(EventController* eventController, Notifier* notifier, AvatarManager* avatarManager, NickResolver* nickResolver) : eventController(eventController), notifier(notifier), avatarManager(avatarManager), nickResolver(nickResolver) {
eventController->onEventQueueEventAdded.connect(boost::bind(&EventNotifier::handleEventAdded, this, _1));
}
EventNotifier::~EventNotifier() {
notifier->purgeCallbacks();
eventController->onEventQueueEventAdded.disconnect(boost::bind(&EventNotifier::handleEventAdded, this, _1));
}
void EventNotifier::handleEventAdded(boost::shared_ptr<StanzaEvent> event) {
if (event->getConcluded()) {
return;
}
if (boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event)) {
JID jid = messageEvent->getStanza()->getFrom();
std::string title = nickResolver->jidToNick(jid);
- if (!messageEvent->getStanza()->isError() && !messageEvent->getStanza()->getBody().empty()) {
+ if (!messageEvent->getStanza()->isError() && !messageEvent->getStanza()->getBody().get_value_or("").empty()) {
JID activationJID = jid;
if (messageEvent->getStanza()->getType() == Message::Groupchat) {
activationJID = jid.toBare();
}
- std::string messageText = messageEvent->getStanza()->getBody();
+ std::string messageText = messageEvent->getStanza()->getBody().get_value_or("");
if (boost::starts_with(messageText, "/me ")) {
messageText = "*" + String::getSplittedAtFirst(messageText, ' ').second + "*";
}
notifier->showMessage(Notifier::IncomingMessage, title, messageText, avatarManager->getAvatarPath(jid), boost::bind(&EventNotifier::handleNotificationActivated, this, activationJID));
}
}
else if(boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event)) {
JID jid = subscriptionEvent->getJID();
std::string title = jid;
std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% wants to add you to his/her contact list")) % nickResolver->jidToNick(jid));
notifier->showMessage(Notifier::SystemMessage, title, message, boost::filesystem::path(), boost::function<void()>());
}
else if(boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event)) {
notifier->showMessage(Notifier::SystemMessage, QT_TRANSLATE_NOOP("", "Error"), errorEvent->getText(), boost::filesystem::path(), boost::function<void()>());
}
else if (boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event)) {
std::string title = mucInviteEvent->getInviter();
std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% has invited you to enter the %2% room")) % nickResolver->jidToNick(mucInviteEvent->getInviter()) % mucInviteEvent->getRoomJID());
// FIXME: not show avatar or greyed out avatar for mediated invites
notifier->showMessage(Notifier::SystemMessage, title, message, avatarManager->getAvatarPath(mucInviteEvent->getInviter()), boost::bind(&EventNotifier::handleNotificationActivated, this, mucInviteEvent->getInviter()));
}
}
void EventNotifier::handleNotificationActivated(JID jid) {
onNotificationActivated(jid);
}
}
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 4523d29..dddea6c 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -1,58 +1,59 @@
/*
* Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/foreach.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
namespace Swift {
class MockChatWindow : public ChatWindow {
public:
MockChatWindow() : labelsEnabled_(false), impromptuMUCSupported_(false) {}
virtual ~MockChatWindow();
virtual std::string addMessage(const ChatMessage& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {
lastMessageBody_ = bodyFromMessage(message);
return "id";
}
virtual std::string addAction(const ChatMessage& /*message*/, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {return "id";}
- virtual std::string addSystemMessage(const ChatMessage& /*message*/, Direction /*direction*/) {
+ virtual std::string addSystemMessage(const ChatMessage& message, Direction /*direction*/) {
+ lastAddedSystemMessage_ = message;
return "id";
}
virtual void addPresenceMessage(const ChatMessage& message, Direction /*direction*/) {
lastAddedPresence_ = message;
}
virtual void addErrorMessage(const ChatMessage& /*message*/) {}
virtual void replaceMessage(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}
virtual void replaceWithAction(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}
virtual void replaceLastMessage(const ChatMessage& message, const TimestampBehaviour /*timestampBehaviour*/) {
lastReplacedMessage_ = message;
}
virtual void replaceSystemMessage(const ChatMessage& /*message*/, const std::string& /*id*/, const TimestampBehaviour /*timestampBehaviour*/) {}
// File transfer related stuff
virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/, const std::string& /*description*/) { return 0; }
virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { }
virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { }
virtual void setMessageReceiptState(const std::string &/* id */, ReceiptState /* state */) { }
virtual void setContactChatState(ChatState::ChatStateType /*state*/) {}
virtual void setName(const std::string& name) {name_ = name;}
virtual void show() {}
virtual bool isVisible() const { return true; }
virtual void activate() {}
virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;}
virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;}
virtual void setUnreadMessageCount(int /*count*/) {}
@@ -76,38 +77,39 @@ namespace Swift {
virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true, bool = false, bool = false) {}
virtual std::string addWhiteboardRequest(bool) {return "";}
virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){}
virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {}
virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {}
virtual void setBlockingState(BlockingState) {}
virtual void setCanInitiateImpromptuChats(bool supportsImpromptu) {
impromptuMUCSupported_ = supportsImpromptu;
}
virtual void showBookmarkWindow(const MUCBookmark& /*bookmark*/) {}
virtual void setBookmarkState(RoomBookmarkState) {}
static std::string bodyFromMessage(const ChatMessage& message) {
boost::shared_ptr<ChatTextMessagePart> text;
foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) {
if ((text = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) {
return text->text;
}
}
return "";
}
std::string name_;
std::string lastMessageBody_;
ChatMessage lastAddedPresence_;
ChatMessage lastReplacedMessage_;
+ ChatMessage lastAddedSystemMessage_;
std::vector<SecurityLabelsCatalog::Item> labels_;
bool labelsEnabled_;
bool impromptuMUCSupported_;
SecurityLabelsCatalog::Item label_;
Roster* roster_;
};
}
diff --git a/Swift/Controllers/XMPPEvents/MessageEvent.h b/Swift/Controllers/XMPPEvents/MessageEvent.h
index be8b1b0..b5b1215 100644
--- a/Swift/Controllers/XMPPEvents/MessageEvent.h
+++ b/Swift/Controllers/XMPPEvents/MessageEvent.h
@@ -1,46 +1,48 @@
/*
- * Copyright (c) 2010-2012 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+
#include <cassert>
#include <boost/shared_ptr.hpp>
-#include <Swift/Controllers/XMPPEvents/StanzaEvent.h>
#include <Swiften/Elements/Message.h>
+#include <Swift/Controllers/XMPPEvents/StanzaEvent.h>
+
namespace Swift {
class MessageEvent : public StanzaEvent {
public:
typedef boost::shared_ptr<MessageEvent> ref;
MessageEvent(boost::shared_ptr<Message> stanza) : stanza_(stanza), targetsMe_(true) {}
boost::shared_ptr<Message> getStanza() {return stanza_;}
bool isReadable() {
- return getStanza()->isError() || !getStanza()->getBody().empty();
+ return getStanza()->isError() || !getStanza()->getBody().get_value_or("").empty();
}
void read() {
assert (isReadable());
conclude();
}
void setTargetsMe(bool targetsMe) {
targetsMe_ = targetsMe;
}
bool targetsMe() const {
return targetsMe_;
}
private:
boost::shared_ptr<Message> stanza_;
bool targetsMe_;
};
}
diff --git a/Swift/QtUI/EventViewer/QtEvent.cpp b/Swift/QtUI/EventViewer/QtEvent.cpp
index 4d90bd9..4830aec 100644
--- a/Swift/QtUI/EventViewer/QtEvent.cpp
+++ b/Swift/QtUI/EventViewer/QtEvent.cpp
@@ -1,32 +1,32 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/QtUI/EventViewer/QtEvent.h>
#include <QColor>
#include <QDateTime>
#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
#include <Swift/Controllers/XMPPEvents/IncomingFileTransferEvent.h>
#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
#include <Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h>
#include "Swift/QtUI/QtSwiftUtil.h"
namespace Swift {
QtEvent::QtEvent(boost::shared_ptr<StanzaEvent> event, bool active) : event_(event) {
active_ = active;
}
QVariant QtEvent::data(int role) {
switch (role) {
case Qt::ToolTipRole: return QVariant(text()).toString() + "\n" + B2QDATE(event_->getTime()).toString();
case Qt::DisplayRole: return QVariant(text());
case Qt::TextColorRole: return QColor(active_ ? Qt::black : Qt::darkGray);
case Qt::BackgroundColorRole: return QColor(active_ ? Qt::white : Qt::lightGray);
case SenderRole: return QVariant(sender());
@@ -37,61 +37,61 @@ QVariant QtEvent::data(int role) {
}
}
QString QtEvent::sender() {
boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event_);
if (messageEvent) {
return P2QSTRING(messageEvent->getStanza()->getFrom().toString());
}
boost::shared_ptr<SubscriptionRequestEvent> subscriptionRequestEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event_);
if (subscriptionRequestEvent) {
return P2QSTRING(subscriptionRequestEvent->getJID().toBare().toString());
}
boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event_);
if (errorEvent) {
return P2QSTRING(errorEvent->getJID().toBare().toString());
}
boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event_);
if (mucInviteEvent) {
return P2QSTRING(mucInviteEvent->getInviter().toString());
}
boost::shared_ptr<IncomingFileTransferEvent> incomingFTEvent = boost::dynamic_pointer_cast<IncomingFileTransferEvent>(event_);
if (incomingFTEvent) {
return P2QSTRING(incomingFTEvent->getSender().toString());
}
return "";
}
QString QtEvent::text() {
boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event_);
if (messageEvent) {
- return P2QSTRING(messageEvent->getStanza()->getBody());
+ return P2QSTRING(messageEvent->getStanza()->getBody().get_value_or(""));
}
boost::shared_ptr<SubscriptionRequestEvent> subscriptionRequestEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event_);
if (subscriptionRequestEvent) {
std::string reason = subscriptionRequestEvent->getReason();
QString message;
if (reason.empty()) {
message = QString(QObject::tr("%1 would like to add you to their contact list.")).arg(subscriptionRequestEvent->getJID().toBare().toString().c_str());
}
else {
message = QString(QObject::tr("%1 would like to add you to their contact list, saying '%2'")).arg(subscriptionRequestEvent->getJID().toBare().toString().c_str()).arg(reason.c_str());
}
return message;
}
boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event_);
if (errorEvent) {
return P2QSTRING(errorEvent->getText());
}
boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event_);
if (mucInviteEvent) {
QString message = QString(QObject::tr("%1 has invited you to enter the %2 room.")).arg(P2QSTRING(mucInviteEvent->getInviter().toBare().toString())).arg(P2QSTRING(mucInviteEvent->getRoomJID().toString()));
return message;
}
boost::shared_ptr<IncomingFileTransferEvent> incomingFTEvent = boost::dynamic_pointer_cast<IncomingFileTransferEvent>(event_);
if (incomingFTEvent) {
QString message = QString(QObject::tr("%1 would like to send a file to you.")).arg(P2QSTRING(incomingFTEvent->getSender().toBare().toString()));
return message;
}
return "";
}
diff --git a/Swiften/Elements/Message.h b/Swiften/Elements/Message.h
index f6c16e4..0f0d380 100644
--- a/Swiften/Elements/Message.h
+++ b/Swiften/Elements/Message.h
@@ -1,71 +1,82 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <string>
+
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
-#include <string>
#include <Swiften/Base/API.h>
#include <Swiften/Elements/Body.h>
-#include <Swiften/Elements/Subject.h>
#include <Swiften/Elements/ErrorPayload.h>
-#include <Swiften/Elements/Stanza.h>
#include <Swiften/Elements/Replace.h>
+#include <Swiften/Elements/Stanza.h>
+#include <Swiften/Elements/Subject.h>
namespace Swift {
class SWIFTEN_API Message : public Stanza {
public:
typedef boost::shared_ptr<Message> ref;
enum Type { Normal, Chat, Error, Groupchat, Headline };
Message() : type_(Chat) { }
std::string getSubject() const {
boost::shared_ptr<Subject> subject(getPayload<Subject>());
if (subject) {
return subject->getText();
}
return "";
}
void setSubject(const std::string& subject) {
updatePayload(boost::make_shared<Subject>(subject));
}
// Explicitly convert to bool. In C++11, it would be cleaner to
// compare to nullptr.
bool hasSubject() {
return static_cast<bool>(getPayload<Subject>());
}
- std::string getBody() const {
+ boost::optional<std::string> getBody() const {
boost::shared_ptr<Body> body(getPayload<Body>());
+ boost::optional<std::string> bodyData;
if (body) {
- return body->getText();
+ bodyData = body->getText();
}
- return "";
+ return bodyData;
+ }
+
+ void setBody(const std::string& body) {
+ setBody(boost::optional<std::string>(body));
}
- void setBody(const std::string& body) {
- updatePayload(boost::make_shared<Body>(body));
+ void setBody(const boost::optional<std::string>& body) {
+ if (body) {
+ updatePayload(boost::make_shared<Body>(body.get()));
+ }
+ else {
+ removePayloadOfSameType(boost::make_shared<Body>());
+ }
}
bool isError() {
boost::shared_ptr<Swift::ErrorPayload> error(getPayload<Swift::ErrorPayload>());
return getType() == Message::Error || error;
}
Type getType() const { return type_; }
void setType(Type type) { type_ = type; }
private:
Type type_;
};
}
diff --git a/Swiften/Elements/Stanza.cpp b/Swiften/Elements/Stanza.cpp
index 234878c..f385e1c 100644
--- a/Swiften/Elements/Stanza.cpp
+++ b/Swiften/Elements/Stanza.cpp
@@ -1,58 +1,70 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/Elements/Stanza.h>
-#include <Swiften/Elements/Delay.h>
#include <typeinfo>
+#include <boost/bind.hpp>
+
#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/Delay.h>
namespace Swift {
Stanza::Stanza() {
}
Stanza::~Stanza() {
payloads_.clear();
}
void Stanza::updatePayload(boost::shared_ptr<Payload> payload) {
foreach (boost::shared_ptr<Payload>& i, payloads_) {
if (typeid(*i.get()) == typeid(*payload.get())) {
i = payload;
return;
}
}
addPayload(payload);
}
+static bool sameType(boost::shared_ptr<Payload> a, boost::shared_ptr<Payload> b) {
+ return typeid(*a.get()) == typeid(*b.get());
+}
+
+void Stanza::removePayloadOfSameType(boost::shared_ptr<Payload> payload) {
+ payloads_.erase(std::remove_if(payloads_.begin(), payloads_.end(),
+ boost::bind<bool>(&sameType, payload, _1)),
+ payloads_.end());
+}
+
boost::shared_ptr<Payload> Stanza::getPayloadOfSameType(boost::shared_ptr<Payload> payload) const {
foreach (const boost::shared_ptr<Payload>& i, payloads_) {
if (typeid(*i.get()) == typeid(*payload.get())) {
return i;
}
}
return boost::shared_ptr<Payload>();
}
boost::optional<boost::posix_time::ptime> Stanza::getTimestamp() const {
boost::shared_ptr<Delay> delay = getPayload<Delay>();
return delay ? delay->getStamp() : boost::optional<boost::posix_time::ptime>();
}
boost::optional<boost::posix_time::ptime> Stanza::getTimestampFrom(const JID& jid) const {
std::vector< boost::shared_ptr<Delay> > delays = getPayloads<Delay>();
for (size_t i = 0; i < delays.size(); ++i) {
if (delays[i]->getFrom() == jid) {
return delays[i]->getStamp();
}
}
return getTimestamp();
}
}
diff --git a/Swiften/Elements/Stanza.h b/Swiften/Elements/Stanza.h
index 41df894..8da6280 100644
--- a/Swiften/Elements/Stanza.h
+++ b/Swiften/Elements/Stanza.h
@@ -1,94 +1,96 @@
/*
- * Copyright (c) 2010-2014 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
-#include <vector>
#include <string>
-#include <boost/shared_ptr.hpp>
-#include <boost/optional/optional_fwd.hpp>
+#include <vector>
+
#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/optional/optional_fwd.hpp>
+#include <boost/shared_ptr.hpp>
#include <Swiften/Base/API.h>
#include <Swiften/Elements/ToplevelElement.h>
#include <Swiften/JID/JID.h>
namespace Swift {
class Payload;
class SWIFTEN_API Stanza : public ToplevelElement {
public:
typedef boost::shared_ptr<Stanza> ref;
protected:
Stanza();
public:
virtual ~Stanza();
SWIFTEN_DEFAULT_COPY_CONSTRUCTOR(Stanza)
template<typename T>
boost::shared_ptr<T> getPayload() const {
for (size_t i = 0; i < payloads_.size(); ++i) {
boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(payloads_[i]));
if (result) {
return result;
}
}
return boost::shared_ptr<T>();
}
template<typename T>
std::vector< boost::shared_ptr<T> > getPayloads() const {
std::vector< boost::shared_ptr<T> > results;
for (size_t i = 0; i < payloads_.size(); ++i) {
boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(payloads_[i]));
if (result) {
results.push_back(result);
}
}
return results;
}
const std::vector< boost::shared_ptr<Payload> >& getPayloads() const {
return payloads_;
}
void addPayload(boost::shared_ptr<Payload> payload) {
payloads_.push_back(payload);
}
template<typename InputIterator>
void addPayloads(InputIterator begin, InputIterator end) {
payloads_.insert(payloads_.end(), begin, end);
}
void updatePayload(boost::shared_ptr<Payload> payload);
+ void removePayloadOfSameType(boost::shared_ptr<Payload>);
boost::shared_ptr<Payload> getPayloadOfSameType(boost::shared_ptr<Payload>) const;
const JID& getFrom() const { return from_; }
void setFrom(const JID& from) { from_ = from; }
const JID& getTo() const { return to_; }
void setTo(const JID& to) { to_ = to; }
const std::string& getID() const { return id_; }
void setID(const std::string& id) { id_ = id; }
boost::optional<boost::posix_time::ptime> getTimestamp() const;
// Falls back to any timestamp if no specific timestamp for the given JID is found.
boost::optional<boost::posix_time::ptime> getTimestampFrom(const JID& jid) const;
private:
std::string id_;
JID from_;
JID to_;
std::vector< boost::shared_ptr<Payload> > payloads_;
};
}
diff --git a/Swiften/Examples/MUCListAndJoin/MUCListAndJoin.cpp b/Swiften/Examples/MUCListAndJoin/MUCListAndJoin.cpp
index 2e2503b..216d16d 100644
--- a/Swiften/Examples/MUCListAndJoin/MUCListAndJoin.cpp
+++ b/Swiften/Examples/MUCListAndJoin/MUCListAndJoin.cpp
@@ -1,32 +1,32 @@
/*
- * Copyright (c) 2014 Isode Limited.
+ * Copyright (c) 2014-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <iostream>
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/foreach.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Client/ClientXMLTracer.h>
#include <Swiften/Disco/GetDiscoItemsRequest.h>
#include <Swiften/EventLoop/SimpleEventLoop.h>
#include <Swiften/JID/JID.h>
#include <Swiften/MUC/MUC.h>
#include <Swiften/MUC/MUCManager.h>
#include <Swiften/Network/BoostNetworkFactories.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/TimerFactory.h>
using namespace Swift;
using namespace std;
static SimpleEventLoop eventLoop;
static BoostNetworkFactories networkFactories(&eventLoop);
static boost::shared_ptr<Client> client;
static MUC::ref muc;
static JID mucJID;
@@ -48,61 +48,61 @@ static void handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorP
int roomCount = 0;
cout << "List of rooms at " << mucJID.toString() << endl;
foreach (DiscoItems::Item item, items->getItems()) {
roomCount++;
cout << "\t" << roomCount << ". " << item.getJID().getNode() << " - " << item.getName() << std::endl;
if (roomCount == 1) {
roomJID = item.getJID();
}
}
cout << endl;
joinMUC();
}
static void handleConnected() {
cout << "Connected." << endl;
// search for MUC rooms
GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(mucJID, client->getIQRouter());
discoItemsRequest->onResponse.connect(&handleRoomsItemsResponse);
cout << "Request list of rooms." << endl;
discoItemsRequest->send();
}
static void handleDisconnected(const boost::optional<ClientError>&) {
cout << "Disconnected." << endl;
}
static void handleIncomingMessage(boost::shared_ptr<Message> message) {
if (message->getFrom().toBare() == roomJID) {
- cout << "[ " << roomJID << " ] " << message->getFrom().getResource() << ": " << message->getBody() << endl;
+ cout << "[ " << roomJID << " ] " << message->getFrom().getResource() << ": " << message->getBody().get_value_or("") << endl;
}
}
/*
* Usage: ./MUCListAndJoin <jid> <password> <muc_domain>
*/
int main(int argc, char* argv[]) {
int ret = 0;
if (argc != 4) {
cout << "Usage: ./" << argv[0] << " <jid> <password> <muc_domain>" << endl;
ret = -1;
}
else {
mucJID = JID(argv[3]);
client = boost::make_shared<Client>(JID(argv[1]), string(argv[2]), &networkFactories);
client->setAlwaysTrustCertificates();
// Enable the following line for detailed XML logging
// ClientXMLTracer* tracer = new ClientXMLTracer(client.get());
client->onConnected.connect(&handleConnected);
client->onDisconnected.connect(&handleDisconnected);
client->onMessageReceived.connect(&handleIncomingMessage);
cout << "Connecting..." << flush;
client->connect();
{
Timer::ref timer = networkFactories.getTimerFactory()->createTimer(30000);
timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/ForwardedParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/ForwardedParserTest.cpp
index e77e821..fae259f 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/ForwardedParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/ForwardedParserTest.cpp
@@ -1,118 +1,118 @@
/*
- * Copyright (c) 2014 Isode Limited.
+ * Copyright (c) 2014-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <Swiften/Base/DateTime.h>
-#include <Swiften/Parser/PayloadParsers/ForwardedParser.h>
-#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
#include <Swiften/Elements/Delay.h>
#include <Swiften/Elements/IQ.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/Presence.h>
+#include <Swiften/Parser/PayloadParsers/ForwardedParser.h>
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
using namespace Swift;
class ForwardedParserTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(ForwardedParserTest);
CPPUNIT_TEST(testParseIQ);
CPPUNIT_TEST(testParseMessage);
CPPUNIT_TEST(testParseMessageNoDelay);
CPPUNIT_TEST(testParsePresence);
CPPUNIT_TEST_SUITE_END();
public:
void testParseIQ() {
PayloadsParserTester parser;
CPPUNIT_ASSERT(parser.parse(
"<forwarded xmlns=\"urn:xmpp:forward:0\">"
"<delay xmlns=\"urn:xmpp:delay\" stamp=\"2010-07-10T23:08:25Z\"/>"
"<iq xmlns=\"jabber:client\" type=\"get\" from=\"kindanormal@example.com/IM\" to=\"stupidnewbie@example.com\" id=\"id0\"/>"
"</forwarded>"));
boost::shared_ptr<Forwarded> payload = parser.getPayload<Forwarded>();
CPPUNIT_ASSERT(!!payload);
CPPUNIT_ASSERT(payload->getDelay());
CPPUNIT_ASSERT_EQUAL(std::string("2010-07-10T23:08:25Z"), dateTimeToString(payload->getDelay()->getStamp()));
boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(payload->getStanza());
CPPUNIT_ASSERT(!!iq);
CPPUNIT_ASSERT_EQUAL(JID("stupidnewbie@example.com"), iq->getTo());
CPPUNIT_ASSERT_EQUAL(JID("kindanormal@example.com/IM"), iq->getFrom());
CPPUNIT_ASSERT_EQUAL(std::string("id0"), iq->getID());
CPPUNIT_ASSERT_EQUAL(IQ::Get, iq->getType());
}
void testParseMessage() {
PayloadsParserTester parser;
CPPUNIT_ASSERT(parser.parse(
"<forwarded xmlns=\"urn:xmpp:forward:0\">"
"<delay xmlns=\"urn:xmpp:delay\" stamp=\"2010-07-10T23:08:25Z\"/>"
"<message xmlns=\"jabber:client\" to=\"juliet@capulet.lit/balcony\" from=\"romeo@montague.lit/orchard\" type=\"chat\">"
"<body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>"
"</message>"
"</forwarded>"));
boost::shared_ptr<Forwarded> payload = parser.getPayload<Forwarded>();
CPPUNIT_ASSERT(!!payload);
CPPUNIT_ASSERT(payload->getDelay());
CPPUNIT_ASSERT_EQUAL(std::string("2010-07-10T23:08:25Z"), dateTimeToString(payload->getDelay()->getStamp()));
boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(payload->getStanza());
CPPUNIT_ASSERT(!!message);
const std::string expectedBody = "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.";
- CPPUNIT_ASSERT_EQUAL(expectedBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(expectedBody, message->getBody().get());
CPPUNIT_ASSERT_EQUAL(Message::Chat, message->getType());
CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), message->getTo());
CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), message->getFrom());
}
void testParseMessageNoDelay() {
PayloadsParserTester parser;
CPPUNIT_ASSERT(parser.parse(
"<forwarded xmlns=\"urn:xmpp:forward:0\">"
"<message xmlns=\"jabber:client\" to=\"juliet@capulet.lit/balcony\" from=\"romeo@montague.lit/orchard\" type=\"chat\">"
"<body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>"
"</message>"
"</forwarded>"));
boost::shared_ptr<Forwarded> payload = parser.getPayload<Forwarded>();
CPPUNIT_ASSERT(!!payload);
CPPUNIT_ASSERT(!payload->getDelay());
boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(payload->getStanza());
CPPUNIT_ASSERT(!!message);
const std::string expectedBody = "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.";
- CPPUNIT_ASSERT_EQUAL(expectedBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(expectedBody, message->getBody().get());
CPPUNIT_ASSERT_EQUAL(Message::Chat, message->getType());
CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), message->getTo());
CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), message->getFrom());
}
void testParsePresence() {
PayloadsParserTester parser;
CPPUNIT_ASSERT(parser.parse(
"<forwarded xmlns=\"urn:xmpp:forward:0\">"
"<delay xmlns=\"urn:xmpp:delay\" stamp=\"2010-07-10T23:08:25Z\"/>"
"<presence xmlns=\"jabber:client\" from=\"alice@wonderland.lit/rabbithole\" to=\"madhatter@wonderland.lit\" type=\"unavailable\"/>"
"</forwarded>"));
boost::shared_ptr<Forwarded> payload = parser.getPayload<Forwarded>();
CPPUNIT_ASSERT(!!payload);
CPPUNIT_ASSERT(payload->getDelay());
CPPUNIT_ASSERT_EQUAL(std::string("2010-07-10T23:08:25Z"), dateTimeToString(payload->getDelay()->getStamp()));
boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(payload->getStanza());
CPPUNIT_ASSERT(!!presence);
CPPUNIT_ASSERT_EQUAL(JID("madhatter@wonderland.lit"), presence->getTo());
CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/rabbithole"), presence->getFrom());
CPPUNIT_ASSERT_EQUAL(Presence::Unavailable, presence->getType());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(ForwardedParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/MAMResultParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/MAMResultParserTest.cpp
index c67f7f8..a4c2f08 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/MAMResultParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/MAMResultParserTest.cpp
@@ -1,58 +1,58 @@
/*
- * Copyright (c) 2014 Isode Limited.
+ * Copyright (c) 2014-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <Swiften/Base/DateTime.h>
-#include <Swiften/Parser/PayloadParsers/MAMResultParser.h>
-#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
#include <Swiften/Elements/Delay.h>
#include <Swiften/Elements/Forwarded.h>
#include <Swiften/Elements/Message.h>
+#include <Swiften/Parser/PayloadParsers/MAMResultParser.h>
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
using namespace Swift;
class MAMResultParserTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(MAMResultParserTest);
CPPUNIT_TEST(testParse);
CPPUNIT_TEST_SUITE_END();
public:
void testParse() {
PayloadsParserTester parser;
CPPUNIT_ASSERT(parser.parse(
"<result id=\"28482-98726-73623\" queryid=\"f27\" xmlns=\"urn:xmpp:mam:0\">"
"<forwarded xmlns=\"urn:xmpp:forward:0\">"
"<delay stamp=\"2010-07-10T23:08:25Z\" xmlns=\"urn:xmpp:delay\"/>"
"<message xmlns=\"jabber:client\" from=\"romeo@montague.lit/orchard\" to=\"juliet@capulet.lit/balcony\" type=\"chat\">"
"<body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>"
"</message>"
"</forwarded>"
"</result>"));
boost::shared_ptr<MAMResult> payload = parser.getPayload<MAMResult>();
CPPUNIT_ASSERT(!!payload);
CPPUNIT_ASSERT_EQUAL(std::string("28482-98726-73623"), payload->getID());
CPPUNIT_ASSERT(payload->getQueryID());
CPPUNIT_ASSERT_EQUAL(std::string("f27"), *payload->getQueryID());
boost::shared_ptr<Forwarded> forwarded = payload->getPayload();
CPPUNIT_ASSERT(forwarded->getDelay());
CPPUNIT_ASSERT_EQUAL(std::string("2010-07-10T23:08:25Z"), dateTimeToString(forwarded->getDelay()->getStamp()));
boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(forwarded->getStanza());
CPPUNIT_ASSERT(!!message);
const std::string expectedBody = "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.";
- CPPUNIT_ASSERT_EQUAL(expectedBody, message->getBody());
+ CPPUNIT_ASSERT_EQUAL(expectedBody, message->getBody().get());
CPPUNIT_ASSERT_EQUAL(Message::Chat, message->getType());
CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), message->getTo());
CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), message->getFrom());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(MAMResultParserTest);
diff --git a/Swiftob/Commands.cpp b/Swiftob/Commands.cpp
index 4e31212..9212aaf 100644
--- a/Swiftob/Commands.cpp
+++ b/Swiftob/Commands.cpp
@@ -1,43 +1,44 @@
/*
- * Copyright (c) 2011 Isode Limited.
+ * Copyright (c) 2011-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiftob/Commands.h>
-#include <Swiften/Base/foreach.h>
#include <iostream>
-#include <boost/bind.hpp>
+
#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+#include <Swiften/Base/foreach.h>
#include <Swiften/Client/Client.h>
typedef std::pair<std::string, Commands::Command*> NamedCommand;
Commands::Commands(Users* users, Swift::Client* client, Storage* storage, MUCs* mucs) {
users_ = users;
client_ = client;
mucs_ = mucs;
storage_ = storage;
resetCommands();
}
Commands::~Commands() {
clearCommands();
}
void Commands::clearCommands() {
foreach (NamedCommand command, commands_) {
delete command.second;
}
commands_.clear();
}
void Commands::resetCommands() {
clearCommands();
registerCommand("quit", Owner, "Quit the bot", boost::bind(&Commands::handleQuitCommand, this, _1, _2, _3));
registerCommand("help", Anyone, "Get help", boost::bind(&Commands::handleHelpCommand, this, _1, _2, _3));
registerCommand("join", Owner, "Join a MUC", boost::bind(&Commands::handleJoinCommand, this, _1, _2, _3));
registerCommand("part", Owner, "Leave a MUC", boost::bind(&Commands::handlePartCommand, this, _1, _2, _3));
@@ -184,33 +185,33 @@ void Commands::handleHelpCommand(const std::string& /*command*/, const std::stri
std::string result("Available commands:");
std::string bang = message->getType() == Swift::Message::Groupchat ? "\n!" : "\n";
foreach (NamedCommand pair, commands_) {
if (roleIn(userRole, pair.second->getAllowedBy())) {
result += bang + pair.first + " - " + pair.second->getDescription();
}
}
replyTo(message, result, true);
}
/**
* \param outOfMUC Reply to the sender directly, don't spam MUCs with the reply
*/
void Commands::replyTo(Swift::Message::ref source, std::string replyBody, bool outOfMUC) {
Swift::Message::ref reply(new Swift::Message());
Swift::Message::Type type = source->getType();
reply->setType(type);
reply->setBody(type == Swift::Message::Groupchat ? source->getFrom().getResource() + ": " + replyBody : replyBody);
Swift::JID to = source->getFrom();
if (type == Swift::Message::Groupchat) {
if (outOfMUC) {
reply->setType(Swift::Message::Chat);
} else {
to = to.toBare();
}
}
reply->setTo(to);
if (client_->isAvailable()) {
client_->sendMessage(reply);
} else {
- std::cout << "Dropping '" + reply->getBody() + "' -> " + reply->getTo().toString() + " on the floor due to missing connection." << std::endl;
+ std::cout << "Dropping '" + reply->getBody().get_value_or("") + "' -> " + reply->getTo().toString() + " on the floor due to missing connection." << std::endl;
}
}
diff --git a/Swiftob/LuaCommands.cpp b/Swiftob/LuaCommands.cpp
index 1192452..18535f3 100644
--- a/Swiftob/LuaCommands.cpp
+++ b/Swiftob/LuaCommands.cpp
@@ -1,32 +1,32 @@
/*
- * Copyright (c) 2011 Isode Limited.
+ * Copyright (c) 2011-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiftob/LuaCommands.h>
#include <boost/bind.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
#include <Swiften/Base/foreach.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Network/TimerFactory.h>
#include <boost/filesystem/operations.hpp>
#include <Swiften/Base/Path.h>
#include <Swiftob/Commands.h>
#include <Swiften/Base/BoostFilesystemVersion.h>
#define LUA_COMMANDS "__Lua_Commands"
#define STORAGE "__Storage"
static const luaL_Reg defaultLibraries[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
@@ -353,107 +353,107 @@ static int l_store_setting(lua_State *L) {
}
static int l_get_setting(lua_State *L) {
return LuaCommands::commandsFromLua(L)->get_setting(L);
}
int LuaCommands::store_setting(lua_State *L) {
if (!lua_isstring(L, 2) || !lua_isstring(L, 1)) {
return luaL_error(L, "both setting and key must be strings");
}
std::string value(lua_tostring(L, 2));
std::string key(lua_tostring(L, 1));
lua_pop(L, 2);
storageFromLua(L)->saveSetting(key, value);
return 0;
}
int LuaCommands::get_setting(lua_State *L) {
if (!lua_isstring(L, 1)) {
return luaL_error(L, "key must be a string");
}
std::string key(lua_tostring(L, 1));
lua_pop(L, 1);
lua_pushstring(L, storageFromLua(L)->getSetting(key).c_str());
return 1;
}
void LuaCommands::handleLuaListener(int callbackIndex, lua_State* L, Swift::Message::ref message) {
lua_rawgeti(L, LUA_REGISTRYINDEX, callbackIndex);
- lua_pushstring(L, message->getBody().c_str());
+ lua_pushstring(L, message->getBody().get_value_or("").c_str());
lua_pushstring(L, message->getFrom().toBare().toString().c_str());
lua_pushstring(L, message->getFrom().getResource().c_str());
messageOntoStack(message, L);
int result = lua_pcall(L, 4, 0, 0);
if (result != 0) {
std::string error(lua_tostring(L, -1));
lua_pop(L, 1);
error = "Listener failed: " + error;
std::cout << error << std::endl;
}
}
void LuaCommands::handleLuaCommand(int callbackIndex, lua_State* L, const std::string& command, const std::string& params, Swift::Message::ref message) {
lua_rawgeti(L, LUA_REGISTRYINDEX, callbackIndex);
lua_pushstring(L, command.c_str());
lua_pushstring(L, params.c_str());
messageOntoStack(message, L);
int result = lua_pcall(L, 3, 0, 0);
if (result != 0) {
std::string error(lua_tostring(L, -1));
lua_pop(L, 1);
error = "Command '" + command + "' failed: " + error;
std::cout << error << std::endl;
commands_->replyTo(message, error, false);
}
}
void LuaCommands::messageOntoStack(Swift::Message::ref message, lua_State* L) {
lua_createtable(L, 0, 4);
std::string typeString;
switch (message->getType()) {
case Message::Chat : typeString = "chat";break;
case Message::Groupchat : typeString = "groupchat";break;
case Message::Normal : typeString = "normal";break;
case Message::Error : typeString = "error";break;
case Message::Headline : typeString = "headline";break;
}
lua_pushstring(L, typeString.c_str());
lua_setfield(L, -2, "type");
lua_pushstring(L, message->getFrom().toString().c_str());
lua_setfield(L, -2, "from");
lua_pushstring(L, message->getFrom().toBare().toString().c_str());
lua_setfield(L, -2, "frombare");
lua_pushstring(L, message->getTo().toString().c_str());
lua_setfield(L, -2, "to");
- lua_pushstring(L, message->getBody().c_str());
+ lua_pushstring(L, message->getBody().get_value_or("").c_str());
lua_setfield(L, -2, "body");
}
void LuaCommands::loadScript(boost::filesystem::path filePath) {
std::cout << "Trying to load file from " << filePath << std::endl;
lua_State* lua = luaL_newstate();
initialize(lua);
lua_pushlightuserdata(lua, this);
lua_setfield(lua, LUA_REGISTRYINDEX, LUA_COMMANDS);
#if BOOST_FILESYSTEM_VERSION == 2 // TODO: Delete this when boost 1.44 becomes a minimum requirement, and we no longer need v2
std::string filename = filePath.filename();
#else
std::string filename = filePath.filename().string();
#endif
filename += ".storage";
boost::filesystem::path storagePath(boost::filesystem::path(path_) / stringToPath(filename));
Storage* storage = new Storage(storagePath);
lua_pushlightuserdata(lua, storage);
lua_setfield(lua, LUA_REGISTRYINDEX, STORAGE);
lua_register(lua, "swiftob_register_command", &l_register_command);
lua_register(lua, "swiftob_register_listener", &l_register_listener);
lua_register(lua, "swiftob_reply_to", &l_reply_to);
lua_register(lua, "swiftob_get_software_version", &l_get_software_version);
lua_register(lua, "swiftob_muc_input_to_jid", &l_muc_input_to_jid);
lua_register(lua, "swiftob_store_setting", &l_store_setting);
lua_register(lua, "swiftob_get_setting", &l_get_setting);
lua_register(lua, "swiftob_muc_kick", &l_muc_kick);
int fileLoaded = luaL_dofile(lua, filePath.string().c_str());
if (fileLoaded == 0 ) {
std::cout << "Loaded" << std::endl;
diff --git a/Swiftob/Swiftob.cpp b/Swiftob/Swiftob.cpp
index d26ea23..d534479 100644
--- a/Swiftob/Swiftob.cpp
+++ b/Swiftob/Swiftob.cpp
@@ -1,49 +1,49 @@
/*
- * Copyright (c) 2011 Isode Limited.
+ * Copyright (c) 2011-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiftob/Swiftob.h>
-#include <string>
#include <iostream>
+#include <string>
+
#include <boost/bind.hpp>
-#include <Swiften/JID/JID.h>
#include <Swiften/Base/String.h>
+#include <Swiften/JID/JID.h>
#include <Swiften/Presence/PresenceSender.h>
-#include <Swiftob/Users.h>
#include <Swiftob/Storage.h>
-
+#include <Swiftob/Users.h>
po::options_description Swiftob::getOptionsDescription() {
po::options_description result("Options");
result.add_options()
("path", po::value<std::string>(), "Configuration folder")
("help", "produce help message")
("init", "Reset everything (Really, everything, be careful, you only want to use this on first run).")
("jid", po::value<std::string>(), "JID to use")
("password", po::value<std::string>(), "password")
("initial-owner", po::value<std::string>(), "Initial bot owner (JID)")
;
return result;
}
Swiftob::Swiftob(const po::variables_map& options) : options_(options), networkFactories_(&eventLoop_), quitting_(false) {
path_ = options["path"].as<std::string>();
client_ = new Swift::Client(Swift::JID(options["jid"].as<std::string>()), options["password"].as<std::string>(), &networkFactories_);
storage_ = new Storage(boost::filesystem::path(path_) / "settings.txt");
mucs_ = NULL;
users_ = NULL;
commands_ = NULL;
lua_ = NULL;
init();
client_->onConnected.connect(boost::bind(&Swiftob::handleConnected, this));
client_->onDisconnected.connect(boost::bind(&Swiftob::handleDisconnected, this, _1));
client_->onMessageReceived.connect(boost::bind(&Swiftob::handleMessageReceived, this, _1));
if (options_.count("init") > 0) {
} else if (options_.count("jid") > 0 || options_.count("password") > 0 || options_.count("initial-owner") == 0) {
std::cout << "Ignoring initial config options without --initial" << std::endl;
@@ -71,61 +71,61 @@ void Swiftob::handleRestartRequested() {
init();
}
void Swiftob::handleConnected() {
std::cout << "Connected" << std::endl;
if (options_.count("init") > 0) {}{ /* FIXME: Not ready for persistence yet*/
users_->clearAll();
users_->addUser(Users::User(Swift::JID(options_["initial-owner"].as<std::string>()), Users::User::Owner));
}
Swift::Presence::ref presence(new Swift::Presence());
presence->setStatus("Online and botty");
client_->getPresenceSender()->sendPresence(presence);
}
void Swiftob::handleDisconnected(const boost::optional<Swift::ClientError>& /*error*/) {
std::cout << "Disconnected" << std::endl;
/* FIXME: check if last connect was more than a minute ago. If so, go ahead and connect, if not then wait a minute before connecting.*/
if (quitting_) {
eventLoop_.stop();
} else {
client_->connect();
}
}
void Swiftob::handleMessageReceived(Swift::Message::ref message) {
Swift::Message::Type type = message->getType();
if (type == Swift::Message::Error || type == Swift::Message::Headline) {
std::cout << "Ignoring typed message" << std::endl;
return;
}
- std::string body = message->getBody();
+ std::string body = message->getBody().get_value_or("");
std::cout << "Got message with body " << body << std::endl;
if (body.size() == 0) {
std::cout << "Not handling empty body" << std::endl;
return;
}
/* Run through any full-message listeners */
commands_->runListeners(message);
/*Convert body into !command if it's not a MUC, and it misses the bang*/
std::string bangBody(body);
if (type != Swift::Message::Groupchat && body[0] != '!') {
bangBody = "!" + body;
}
std::cout << "After banging, body is " << bangBody << std::endl;
std::pair<std::string, std::string> split = Swift::String::getSplittedAtFirst(bangBody, ' ');
std::string commandName(split.first);
commandName = Swift::String::getSplittedAtFirst(commandName, '!').second;
/*FIXME: remove leading bang in commandName*/
if (commands_->hasCommand(commandName)) {
std::cout << "Matched command " << commandName << std::endl;
commands_->runCommand(commandName, split.second, message);
}
}
Swiftob::~Swiftob() {
delete lua_;
delete commands_;
delete storage_;
delete users_;