summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2016-06-10 10:59:04 (GMT)
committerKevin Smith <kevin.smith@isode.com>2016-07-01 12:18:48 (GMT)
commitb16a2d1483f59ad93a2171c6c286e12f4ebbf3be (patch)
treef49c9ba36649797070a84955423e4611dae7dd8a
parenta1be6105b97dddc1e03db0075f6ca3fc47fa8e1d (diff)
downloadswift-b16a2d1483f59ad93a2171c6c286e12f4ebbf3be.zip
swift-b16a2d1483f59ad93a2171c6c286e12f4ebbf3be.tar.bz2
Implement Message Carbons in Swift and Swift/Controllers
If the server supports message carbons, Swift will try to enable it. Carbon copied messages will open a chat window in the background if no chat window exists for the conversation. Test-Information: Tested with a XMPP server Swift and a mobile Android client all supporting message carbons. Tested direct messages and MUC PM messages. All working as expected. Added unit tests for message carbons of sent messages and message carbons of received messages. All unit tests pass on OS X 10.11.5 Change-Id: I8d5b5d9975651a2353909dea976f58e4bf12e014
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp6
-rw-r--r--Swift/Controllers/Chat/ChatController.h1
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h1
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp63
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h3
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp110
-rw-r--r--Swift/Controllers/MainController.cpp22
-rw-r--r--Swift/Controllers/MainController.h2
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h24
-rw-r--r--Swiften/Elements/DiscoInfo.cpp1
-rw-r--r--Swiften/Elements/DiscoInfo.h1
-rw-r--r--Swiften/Elements/Forwarded.h6
-rw-r--r--Swiften/Queries/Requests/EnableCarbonsRequest.h28
13 files changed, 241 insertions, 27 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index b8bf4c3..e36728a 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -312,6 +312,12 @@ void ChatController::handleUIEvent(std::shared_ptr<UIEvent> event) {
}
}
+void ChatController::handleIncomingOwnMessage(std::shared_ptr<Message> message) {
+ if (!message->getBody().get_value_or("").empty()) {
+ postSendMessage(message->getBody().get_value_or(""), message);
+ handleStanzaAcked(message);
+ }
+}
void ChatController::postSendMessage(const std::string& body, std::shared_ptr<Stanza> sentStanza) {
std::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>();
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 206ee71..99f8e23 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -40,6 +40,7 @@ namespace Swift {
virtual void handleWhiteboardStateChange(const ChatWindow::WhiteboardSessionState state);
virtual void setContactIsReceivingPresence(bool /*isReceivingPresence*/) SWIFTEN_OVERRIDE;
virtual ChatWindow* detachChatWindow() SWIFTEN_OVERRIDE;
+ virtual void handleIncomingOwnMessage(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
protected:
virtual void cancelReplaces() SWIFTEN_OVERRIDE;
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index b97d7af..4255c19 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -54,6 +54,7 @@ namespace Swift {
void activateChatWindow();
bool hasOpenWindow() const;
virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info);
+ virtual void handleIncomingOwnMessage(std::shared_ptr<Message> /*message*/) {}
void handleIncomingMessage(std::shared_ptr<MessageEvent> message);
std::string addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time);
void replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time);
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index e8b85c4..f3bb8d3 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -24,9 +24,12 @@
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/DiscoServiceWalker.h>
+#include <Swiften/Elements/CarbonsReceived.h>
+#include <Swiften/Elements/CarbonsSent.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/Elements/Forwarded.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
@@ -333,7 +336,7 @@ void ChatsManager::loadRecents() {
return;
}
- foreach(ChatListWindow::Chat chat, recentChats) {
+ for (auto chat : recentChats) {
chat.statusType = StatusShow::None;
chat = updateChatStatusAndAvatarHelper(chat);
prependRecent(chat);
@@ -865,13 +868,49 @@ void ChatsManager::handleUserNicknameChanged(MUCController* mucController, const
}
}
-void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> message) {
- JID jid = message->getFrom();
+bool ChatsManager::messageCausesSessionBinding(std::shared_ptr<Message> message) {
+ bool causesRebind = false;
+ ChatState::ref chatState = message->getPayload<ChatState>();
+ if (!message->getBody().get_value_or("").empty() || (chatState && chatState->getChatState() == ChatState::Composing)) {
+ causesRebind = true;
+ }
+ return causesRebind;
+}
+
+void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> incomingMessage) {
+ std::shared_ptr<Message> message = incomingMessage;
+ if (message->getFrom().toBare() == jid_.toBare()) {
+ CarbonsReceived::ref carbonsReceived;
+ CarbonsSent::ref carbonsSent;
+ Forwarded::ref forwarded;
+ Message::ref forwardedMessage;
+ if ((carbonsReceived = incomingMessage->getPayload<CarbonsReceived>()) &&
+ (forwarded = carbonsReceived->getForwarded()) &&
+ (forwardedMessage = std::dynamic_pointer_cast<Message>(forwarded->getStanza()))) {
+ message = forwardedMessage;
+ }
+ else if ((carbonsSent = incomingMessage->getPayload<CarbonsSent>()) &&
+ (forwarded = carbonsSent->getForwarded()) &&
+ (forwardedMessage = std::dynamic_pointer_cast<Message>(forwarded->getStanza()))) {
+ JID toJID = forwardedMessage->getTo();
+
+ ChatController* controller = getChatControllerOrCreate(toJID);
+ if (controller) {
+ controller->handleIncomingOwnMessage(forwardedMessage);
+ }
+ else {
+ SWIFT_LOG(error) << "Carbons message ignored." << std::endl;
+ }
+ return;
+ }
+ }
+ JID fromJID = message->getFrom();
+
std::shared_ptr<MessageEvent> event(new MessageEvent(message));
bool isInvite = !!message->getPayload<MUCInvitationPayload>();
bool isMediatedInvite = (message->getPayload<MUCUserPayload>() && message->getPayload<MUCUserPayload>()->getInvite());
if (isMediatedInvite) {
- jid = (*message->getPayload<MUCUserPayload>()->getInvite()).from;
+ fromJID = (*message->getPayload<MUCUserPayload>()->getInvite()).from;
}
if (!event->isReadable() && !message->getPayload<ChatState>() && !message->getPayload<DeliveryReceipt>() && !message->getPayload<DeliveryReceiptRequest>() && !isInvite && !isMediatedInvite && !message->hasSubject()) {
return;
@@ -879,7 +918,7 @@ void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> message) {
// Try to deliver it to a MUC
if (message->getType() == Message::Groupchat || message->getType() == Message::Error /*|| (isInvite && message->getType() == Message::Normal)*/) {
- std::map<JID, MUCController*>::iterator i = mucControllers_.find(jid.toBare());
+ std::map<JID, MUCController*>::iterator i = mucControllers_.find(fromJID.toBare());
if (i != mucControllers_.end()) {
i->second->handleIncomingMessage(event);
return;
@@ -895,10 +934,10 @@ void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> message) {
if (invite && autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) {
if (invite->getIsContinuation()) {
// check for existing chat controller for the from JID
- ChatController* controller = getChatControllerIfExists(jid);
+ ChatController* controller = getChatControllerIfExists(fromJID);
if (controller) {
ChatWindow* window = controller->detachChatWindow();
- chatControllers_.erase(jid);
+ chatControllers_.erase(fromJID);
delete controller;
handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true, window);
return;
@@ -914,18 +953,12 @@ void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> message) {
/* 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().get_value_or("").empty() || (chatState && chatState->getChatState() == ChatState::Composing)) {
- bindControllerToJID = true;
- }
-
- ChatController* controller = getChatControllerIfExists(jid, bindControllerToJID);
+ ChatController* controller = getChatControllerIfExists(fromJID, messageCausesSessionBinding(message));
if (controller) {
controller->handleIncomingMessage(event);
}
} else {
- getChatControllerOrCreate(jid)->handleIncomingMessage(event);
+ getChatControllerOrCreate(fromJID)->handleIncomingMessage(event);
}
}
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index da85949..593624d 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -67,7 +67,7 @@ namespace Swift {
void setAvatarManager(AvatarManager* avatarManager);
void setOnline(bool enabled);
void setServerDiscoInfo(std::shared_ptr<DiscoInfo> info);
- void handleIncomingMessage(std::shared_ptr<Message> message);
+ void handleIncomingMessage(std::shared_ptr<Message> incomingMessage);
std::vector<ChatListWindow::Chat> getRecentChats() const;
virtual std::vector<Contact::ref> getContacts(bool withMUCNicks);
@@ -105,6 +105,7 @@ namespace Swift {
void handleWhiteboardSessionRequest(const JID& contact, bool senderIsSelf);
void handleWhiteboardStateChange(const JID& contact, const ChatWindow::WhiteboardSessionState state);
boost::optional<ChatListWindow::Chat> removeExistingChat(const ChatListWindow::Chat& chat);
+ bool messageCausesSessionBinding(std::shared_ptr<Message> message);
void cleanupPrivateMessageRecents();
void appendRecent(const ChatListWindow::Chat& chat);
void prependRecent(const ChatListWindow::Chat& chat);
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index f3908d6..e45bcae 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -23,8 +23,11 @@
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
#include <Swiften/Disco/DummyEntityCapsProvider.h>
+#include <Swiften/Elements/CarbonsReceived.h>
+#include <Swiften/Elements/CarbonsSent.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/Elements/Forwarded.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
@@ -80,11 +83,18 @@ class ChatsManagerTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testChatControllerFullJIDBindingOnTypingAndNotActive);
CPPUNIT_TEST(testChatControllerPMPresenceHandling);
CPPUNIT_TEST(testLocalMUCServiceDiscoveryResetOnDisconnect);
+ CPPUNIT_TEST(testPresenceChangeDoesNotReplaceMUCInvite);
+
+ // Highlighting tests
CPPUNIT_TEST(testChatControllerHighlightingNotificationTesting);
CPPUNIT_TEST(testChatControllerHighlightingNotificationDeduplicateSounds);
CPPUNIT_TEST(testChatControllerMeMessageHandling);
CPPUNIT_TEST(testChatControllerMeMessageHandlingInMUC);
- CPPUNIT_TEST(testPresenceChangeDoesNotReplaceMUCInvite);
+
+ // Carbons tests
+ CPPUNIT_TEST(testCarbonsForwardedIncomingMessageToSecondResource);
+ CPPUNIT_TEST(testCarbonsForwardedOutgoingMessageFromSecondResource);
+
CPPUNIT_TEST_SUITE_END();
public:
@@ -921,6 +931,104 @@ public:
CPPUNIT_ASSERT_EQUAL(std::string("testling@test.com has gone offline."), MockChatWindow::bodyFromMessage(window->lastAddedPresence_));
}
+ template <typename CarbonsType>
+ Message::ref createCarbonsMessage(std::shared_ptr<CarbonsType> carbons, std::shared_ptr<Message> forwardedMessage) {
+ auto messageWrapper = std::make_shared<Message>();
+ messageWrapper->setFrom(jid_.toBare());
+ messageWrapper->setTo(jid_);
+ messageWrapper->setType(Message::Chat);
+
+ messageWrapper->addPayload(carbons);
+ auto forwarded = std::make_shared<Forwarded>();
+ carbons->setForwarded(forwarded);
+ forwarded->setStanza(forwardedMessage);
+ return messageWrapper;
+ }
+
+ void testCarbonsForwardedIncomingMessageToSecondResource() {
+ JID messageJID("testling@test.com/resource1");
+ JID jid2 = jid_.toBare().withResource("someOtherResource");
+
+ MockChatWindow* window = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
+
+ std::shared_ptr<Message> message(new Message());
+ message->setFrom(messageJID);
+ std::string body("This is a legible message. >HEH@)oeueu");
+ message->setBody(body);
+ manager_->handleIncomingMessage(message);
+ CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_));
+
+ // incoming carbons message from another resource
+ {
+ auto originalMessage = std::make_shared<Message>();
+ originalMessage->setFrom(messageJID);
+ originalMessage->setTo(jid2);
+ originalMessage->setType(Message::Chat);
+ std::string forwardedBody = "Some further text.";
+ originalMessage->setBody(forwardedBody);
+
+ auto messageWrapper = createCarbonsMessage(std::make_shared<CarbonsReceived>(), originalMessage);
+
+ manager_->handleIncomingMessage(messageWrapper);
+
+ CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_));
+ CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_);
+ }
+ }
+
+ void testCarbonsForwardedOutgoingMessageFromSecondResource() {
+ JID messageJID("testling@test.com/resource1");
+ JID jid2 = jid_.toBare().withResource("someOtherResource");
+
+ MockChatWindow* window = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
+
+ std::shared_ptr<Message> message(new Message());
+ message->setFrom(messageJID);
+ std::string body("This is a legible message. >HEH@)oeueu");
+ message->setBody(body);
+ manager_->handleIncomingMessage(message);
+ CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_));
+
+ // incoming carbons message from another resource
+ {
+ auto originalMessage = std::make_shared<Message>();
+ originalMessage->setFrom(jid2);
+ originalMessage->setTo(messageJID);
+ originalMessage->setType(Message::Chat);
+ originalMessage->setID("abcdefg123456");
+ std::string forwardedBody = "Some text my other resource sent.";
+ originalMessage->setBody(forwardedBody);
+ originalMessage->addPayload(std::make_shared<DeliveryReceiptRequest>());
+
+ auto messageWrapper = createCarbonsMessage(std::make_shared<CarbonsSent>(), originalMessage);
+
+ manager_->handleIncomingMessage(messageWrapper);
+
+ CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_));
+ CPPUNIT_ASSERT_EQUAL(true, window->lastAddedMessageSenderIsSelf_);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), window->receiptChanges_.size());
+ CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptRequested, window->receiptChanges_[0].second);
+ }
+
+ // incoming carbons message for the received delivery receipt to the other resource
+ {
+ auto originalMessage = std::make_shared<Message>();
+ originalMessage->setFrom(messageJID);
+ originalMessage->setTo(jid2);
+ originalMessage->setType(Message::Chat);
+ originalMessage->addPayload(std::make_shared<DeliveryReceipt>("abcdefg123456"));
+
+ auto messageWrapper = createCarbonsMessage(std::make_shared<CarbonsReceived>(), originalMessage);
+
+ manager_->handleIncomingMessage(messageWrapper);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(2), window->receiptChanges_.size());
+ CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptReceived, window->receiptChanges_[1].second);
+ }
+ }
+
private:
std::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) {
std::shared_ptr<Message> message = std::make_shared<Message>();
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index eebac37..a9d3f5c 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -7,13 +7,13 @@
#include <Swift/Controllers/MainController.h>
#include <cstdlib>
+#include <memory>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
-#include <memory>
-#include <memory>
#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/Log.h>
#include <Swiften/Base/String.h>
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/format.h>
@@ -35,6 +35,7 @@
#include <Swiften/Network/NetworkFactories.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Presence/PresenceSender.h>
+#include <Swiften/Queries/Requests/EnableCarbonsRequest.h>
#include <Swiften/StringCodecs/Base64.h>
#include <Swiften/StringCodecs/Hexify.h>
#include <Swiften/VCards/GetVCardRequest.h>
@@ -802,9 +803,26 @@ void MainController::handleServerDiscoInfoResponse(std::shared_ptr<DiscoInfo> in
rosterController_->getWindow()->setBlockingCommandAvailable(true);
rosterController_->initBlockingCommand();
}
+ if (info->hasFeature(DiscoInfo::MessageCarbonsFeature)) {
+ enableMessageCarbons();
+ }
}
}
+void MainController::enableMessageCarbons() {
+ auto enableCarbonsRequest = EnableCarbonsRequest::create(client_->getIQRouter());
+ enableCarbonsRequestHandlerConnection_ = enableCarbonsRequest->onResponse.connect([&](Payload::ref /*payload*/, ErrorPayload::ref error) {
+ if (error) {
+ SWIFT_LOG(warning) << "Failed to enable carbons." << std::endl;
+ }
+ else {
+ SWIFT_LOG(debug) << "Successfully enabled carbons." << std::endl;
+ }
+ enableCarbonsRequestHandlerConnection_.disconnect();
+ });
+ enableCarbonsRequest->send();
+}
+
void MainController::handleVCardReceived(const JID& jid, VCard::ref vCard) {
if (!jid.equals(jid_, JID::WithoutResource) || !vCard) {
return;
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 5000d51..4f691ee 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -122,6 +122,7 @@ namespace Swift {
void setReconnectTimer();
void resetPendingReconnects();
void resetCurrentError();
+ void enableMessageCarbons();
void performLoginFromCachedCredentials();
void reconnectAfterError();
@@ -195,5 +196,6 @@ namespace Swift {
HighlightManager* highlightManager_;
HighlightEditorController* highlightEditorController_;
std::map<std::string, std::string> emoticons_;
+ boost::signals2::connection enableCarbonsRequestHandlerConnection_;
};
}
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 4f874e1..d7942ff 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -7,8 +7,7 @@
#pragma once
#include <memory>
-
-#include <Swiften/Base/foreach.h>
+#include <string>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
@@ -18,13 +17,17 @@ namespace Swift {
MockChatWindow() : labelsEnabled_(false), impromptuMUCSupported_(false) {}
virtual ~MockChatWindow();
- virtual std::string addMessage(const ChatMessage& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, std::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/) {
+ virtual std::string addMessage(const ChatMessage& message, const std::string& senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/) {
lastAddedMessage_ = message;
+ lastAddedMessageSenderName_ = senderName;
+ lastAddedMessageSenderIsSelf_ = senderIsSelf;
return "id";
}
- virtual std::string addAction(const ChatMessage& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, std::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/) {
+ virtual std::string addAction(const ChatMessage& message, const std::string& senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/) {
lastAddedAction_ = message;
+ lastAddedActionSenderName_ = senderName;
+ lastAddedActionSenderIsSelf_ = senderIsSelf;
return "id";
}
@@ -52,7 +55,9 @@ namespace Swift {
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 setMessageReceiptState(const std::string & id, ReceiptState state) {
+ receiptChanges_.emplace_back(id, state);
+ }
virtual void setContactChatState(ChatState::ChatStateType /*state*/) {}
virtual void setName(const std::string& name) {name_ = name;}
@@ -101,7 +106,7 @@ namespace Swift {
std::string body;
std::shared_ptr<ChatTextMessagePart> text;
std::shared_ptr<ChatHighlightingMessagePart> highlight;
- foreach (std::shared_ptr<ChatMessagePart> part, message.getParts()) {
+ for (auto &&part : message.getParts()) {
if ((text = std::dynamic_pointer_cast<ChatTextMessagePart>(part))) {
body += text->text;
}
@@ -114,11 +119,17 @@ namespace Swift {
void resetLastMessages() {
lastAddedMessage_ = lastAddedAction_ = lastAddedPresence_ = lastReplacedMessage_ = lastAddedSystemMessage_ = lastReplacedSystemMessage_ = ChatMessage();
+ lastAddedMessageSenderName_ = lastAddedActionSenderName_ = "";
+ lastAddedMessageSenderIsSelf_ = lastAddedActionSenderIsSelf_ = false;
}
std::string name_;
ChatMessage lastAddedMessage_;
+ std::string lastAddedMessageSenderName_;
+ bool lastAddedMessageSenderIsSelf_;
ChatMessage lastAddedAction_;
+ std::string lastAddedActionSenderName_;
+ bool lastAddedActionSenderIsSelf_;
ChatMessage lastAddedPresence_;
ChatMessage lastReplacedMessage_;
ChatMessage lastAddedSystemMessage_;
@@ -129,6 +140,7 @@ namespace Swift {
bool impromptuMUCSupported_;
SecurityLabelsCatalog::Item label_;
Roster* roster_;
+ std::vector<std::pair<std::string, ReceiptState>> receiptChanges_;
};
}
diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp
index 29676b5..51a4450 100644
--- a/Swiften/Elements/DiscoInfo.cpp
+++ b/Swiften/Elements/DiscoInfo.cpp
@@ -24,6 +24,7 @@ const std::string DiscoInfo::Bytestream = std::string("http://jabber.org/protoco
const std::string DiscoInfo::MessageDeliveryReceiptsFeature = std::string("urn:xmpp:receipts");
const std::string DiscoInfo::WhiteboardFeature = std::string("http://swift.im/whiteboard");
const std::string DiscoInfo::BlockingCommandFeature = std::string("urn:xmpp:blocking");
+const std::string DiscoInfo::MessageCarbonsFeature = std::string("urn:xmpp:carbons:2");
bool DiscoInfo::Identity::operator<(const Identity& other) const {
if (category_ == other.category_) {
diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h
index 6ce3fbb..ebc598c 100644
--- a/Swiften/Elements/DiscoInfo.h
+++ b/Swiften/Elements/DiscoInfo.h
@@ -35,6 +35,7 @@ namespace Swift {
static const std::string MessageDeliveryReceiptsFeature;
static const std::string WhiteboardFeature;
static const std::string BlockingCommandFeature;
+ static const std::string MessageCarbonsFeature;
class Identity {
public:
diff --git a/Swiften/Elements/Forwarded.h b/Swiften/Elements/Forwarded.h
index 1a31b89..a7eb492 100644
--- a/Swiften/Elements/Forwarded.h
+++ b/Swiften/Elements/Forwarded.h
@@ -6,8 +6,7 @@
#pragma once
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <boost/optional.hpp>
+#include <memory>
#include <Swiften/Base/API.h>
#include <Swiften/Elements/Payload.h>
@@ -18,6 +17,9 @@ namespace Swift {
class SWIFTEN_API Forwarded : public Payload {
public:
+ typedef std::shared_ptr<Forwarded> ref;
+
+ public:
virtual ~Forwarded();
void setDelay(std::shared_ptr<Delay> delay) { delay_ = delay; }
diff --git a/Swiften/Queries/Requests/EnableCarbonsRequest.h b/Swiften/Queries/Requests/EnableCarbonsRequest.h
new file mode 100644
index 0000000..7762e68
--- /dev/null
+++ b/Swiften/Queries/Requests/EnableCarbonsRequest.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsEnable.h>
+#include <Swiften/Queries/GenericRequest.h>
+
+namespace Swift {
+ class SWIFTEN_API EnableCarbonsRequest : public GenericRequest<CarbonsEnable> {
+ public:
+ typedef std::shared_ptr<EnableCarbonsRequest> ref;
+
+ static ref create(IQRouter* router) {
+ return ref(new EnableCarbonsRequest(router));
+ }
+
+ private:
+ EnableCarbonsRequest(IQRouter* router) : GenericRequest<CarbonsEnable>(IQ::Set, JID(), std::make_shared<CarbonsEnable>(), router) {
+ }
+ };
+}