diff options
26 files changed, 541 insertions, 90 deletions
diff --git a/BuildTools/SCons/Tools/textfile.py b/BuildTools/SCons/Tools/textfile.py index 89f8963..cc58666 100644 --- a/BuildTools/SCons/Tools/textfile.py +++ b/BuildTools/SCons/Tools/textfile.py @@ -63,7 +63,7 @@ def _do_subst(node, subs): then all instances of %VERSION% in the file will be replaced with 1.2345 and so forth. """ - contents = node.get_text_contents() + contents = node.get_contents().decode('utf-8') if not subs: return contents for (k,v) in subs: contents = re.sub(k, v, contents) @@ -113,7 +113,7 @@ def _action(target, source, env): lsep = None for s in source: if lsep: fd.write(lsep) - fd.write(_do_subst(s, subs)) + fd.write(_do_subst(s, subs).encode("utf-8")) lsep = linesep fd.close() diff --git a/BuildTools/SCons/Version.py b/BuildTools/SCons/Version.py index d34c2a7..f215a5d 100644 --- a/BuildTools/SCons/Version.py +++ b/BuildTools/SCons/Version.py @@ -1,4 +1,4 @@ -import subprocess, os, datetime, re, os.path +import subprocess, os, datetime, re, os.path, sys, unittest def getGitBuildVersion(root, project) : tag = git("describe --tags --exact --match \"" + project + "-*\"", root) @@ -37,32 +37,165 @@ def getBuildVersion(root, project) : return datetime.date.today().strftime("%Y%m%d") -def convertToWindowsVersion(version) : - version_match = re.match("(\d+)\.(\d+)(.*)", version) - major = version_match and int(version_match.group(1)) or 0 - minor = version_match and int(version_match.group(2)) or 0 - if version_match and len(version_match.group(3)) == 0 : - patch = 60000 - else : - match = re.match("^beta(\d+)(.*)", version_match.group(3)) - build_string = "" - if match : - patch = 1000*int(match.group(1)) - build_string = match.group(2) - else : - rc_match = re.match("^rc(\d+)(.*)", version_match.group(3)) - if rc_match : - patch = 10000*int(rc_match.group(1)) - build_string = rc_match.group(2) - else : - patch = 0 - alpha_match = re.match("^alpha(.*)", version_match.group(3)) - if alpha_match : - build_string = alpha_match.group(1) - - if len(build_string) > 0 : - build_match = re.match("^-dev(\d+)", build_string) - if build_match : - patch += int(build_match.group(1)) +# The following conversion allows us to use version tags the format: +# major.0 +# major.0.(0 to 9) +# +# Either from above followed by: +# alpha(0 to 4) +# beta(0 to 6) +# rc(1 to 11) +# +# Followed by an optional -dev(1-65535) for off tag builds. +def convertToWindowsVersion(version): + match = re.match(r"(?P<major>\d+)\.(?P<minor>\d+)\.?(?P<patch>\d+)?(?:(?P<stage>rc|beta|alpha)(?P<stage_number>\d+)?)?(?:-dev(?P<dev>\d+))?", version) + assert(match) + major, minor, patch = (0, 0, 0) + groups = match.groupdict() + assert(groups['major']) + major = int(groups['major']) + + if groups['minor']: + assert(int(groups['minor']) == 0) + + if groups['patch']: + assert(0 <= int(groups['patch']) <= 9) + minor = int(groups['patch']) * 25 + + stage = groups["stage"] + if stage: + stageNumber = groups['stage_number'] + if not stageNumber or stageNumber == "": + stageNumber = 0 + else: + stageNumber = int(stageNumber) + + if stage == "alpha": + assert(0 <= stageNumber <= 4) + minor = 1 + stageNumber + elif stage == "beta": + assert(0 <= stageNumber <= 6) + minor = 6 + stageNumber + elif stage == "rc": + assert(1 <= stageNumber <= 11) + minor = 12 + stageNumber + else: + assert(False) + else: + minor = minor + 24 + + if groups['dev']: + patch = 1 + int(groups['dev']) + + # The following constraints are set by Windows Installer framework + assert(0 <= major <= 255) + assert(0 <= minor <= 255) + assert(0 <= patch <= 65535) return (major, minor, patch) + +# Test Windows version mapping scheme +class convertToWindowsVersionTest(unittest.TestCase): + def testWindowsVersionsAreDescending(self): + versionStringsWithOldVersions = [ + ("5.0rc11", None), + ("5.0rc1", None), + ("5.0beta6", None), + ("5.0alpha4", None), + ("5.0alpha2", None), + ("5.0alpha", None), + ("4.0.9", None), + ("4.0.1", None), + ("4.0", (4, 0, 60000)), + ("4.0rc6", (4, 0, 60000)), + ("4.0rc5", (4, 0, 50000)), + ("4.0rc4", (4, 0, 40000)), + ("4.0rc3", (4, 0, 30000)), + ('4.0rc2-dev34', (4, 0, 20034)), + ('4.0rc2-dev33', (4, 0, 20033)), + ('4.0rc2-dev31', (4, 0, 20031)), + ('4.0rc2-dev30', (4, 0, 20030)), + ('4.0rc2-dev29', (4, 0, 20029)), + ('4.0rc2-dev27', (4, 0, 20027)), + ('4.0rc2-dev39', (4, 0, 20039)), + ('4.0rc2', (4, 0, 20000)), + ('4.0rc1', (4, 0, 10000)), + ('4.0beta2-dev203', (4, 0, 2203)), + ('4.0beta2-dev195', (4, 0, 2195)), + ('4.0beta2-dev177', (4, 0, 2177)), + ('4.0beta2-dev171', (4, 0, 2171)), + ('4.0beta2-dev154', (4, 0, 2154)), + ('4.0beta2-dev150', (4, 0, 2150)), + ('4.0beta2-dev142', (4, 0, 2142)), + ('4.0beta2-dev140', (4, 0, 2140)), + ('4.0beta2-dev133', (4, 0, 2133)), + ('4.0beta2-dev118', (4, 0, 2118)), + ('4.0beta2-dev112', (4, 0, 2112)), + ('4.0beta2-dev93', (4, 0, 2093)), + ('4.0beta2-dev80', (4, 0, 2080)), + ('4.0beta2-dev72', (4, 0, 2072)), + ('4.0beta2-dev57', (4, 0, 2057)), + ('4.0beta2-dev44', (4, 0, 2044)), + ('4.0beta2-dev38', (4, 0, 2038)), + ('4.0beta2-dev29', (4, 0, 2029)), + ('4.0beta2-dev15', (4, 0, 2015)), + ('4.0beta2', (4, 0, 2000)), + ('4.0beta1', (4, 0, 1000)), + ('4.0alpha-dev80', (4, 0, 80)), + ('4.0alpha-dev50', (4, 0, 50)), + ('4.0alpha-dev43', (4, 0, 43)), + ('4.0alpha-dev21', (4, 0, 21)), + ('3.0', (3, 0, 60000)), + ('3.0rc3', (3, 0, 30000)), + ('3.0rc2', (3, 0, 20000)), + ('3.0rc1', (3, 0, 10000)), + ('3.0beta2-dev124', (3, 0, 2124)), + ('3.0beta2-dev81', (3, 0, 2081)), + ('3.0beta2-dev50', (3, 0, 2050)), + ('3.0beta2-dev44', (3, 0, 2044)), + ('3.0beta2-dev40', (3, 0, 2040)), + ('3.0beta2-dev26', (3, 0, 2026)), + ('3.0beta2', (3, 0, 2000)), + ('3.0beta1', (3, 0, 1000)), + ('3.0alpha-dev529', (3, 0, 529)), + ('3.0alpha-dev528', (3, 0, 528)), + ('3.0alpha-dev526', (3, 0, 526)), + ('3.0alpha-dev524', (3, 0, 524)), + ('3.0alpha-dev515', (3, 0, 515)), + ] + windowsVersionMapping = list(map(lambda (x,y): (x, convertToWindowsVersion(x)), versionStringsWithOldVersions)) + + def testThatBetaIsHigherThanAlpha(self): + self.assertTrue(convertToWindowsVersion("3.0beta0") > convertToWindowsVersion("3.0alpha0")) + self.assertTrue(convertToWindowsVersion("3.0beta6") > convertToWindowsVersion("3.0alpha1")) + self.assertTrue(convertToWindowsVersion("3.0beta6") > convertToWindowsVersion("3.0alpha4")) + + def testThatRcIsHigherThanAlphaAndBeta(self): + self.assertTrue(convertToWindowsVersion("3.0rc11") > convertToWindowsVersion("3.0alpha0")) + self.assertTrue(convertToWindowsVersion("3.0rc11") > convertToWindowsVersion("3.0alpha4")) + self.assertTrue(convertToWindowsVersion("3.0rc1") > convertToWindowsVersion("3.0alpha0")) + self.assertTrue(convertToWindowsVersion("3.0rc1") > convertToWindowsVersion("3.0alpha4")) + self.assertTrue(convertToWindowsVersion("3.0rc11") > convertToWindowsVersion("3.0beta0")) + self.assertTrue(convertToWindowsVersion("3.0rc11") > convertToWindowsVersion("3.0beta6")) + self.assertTrue(convertToWindowsVersion("3.0rc1") > convertToWindowsVersion("3.0beta0")) + self.assertTrue(convertToWindowsVersion("3.0rc1") > convertToWindowsVersion("3.0beta6")) + + def testThatStableIsHigherThanAlphaAndBetaAndRc(self): + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0alpha0")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0alpha4")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0alpha0")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0alpha4")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0beta0")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0beta6")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc1")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc11")) + +if __name__ == '__main__': + if len(sys.argv) == 1: + unittest.main() + elif len(sys.argv) == 2: + print convertToWindowsVersion(sys.argv[1]) + sys.exit(0) + else: + print "Error: Simply run the script without arguments or pass a single argument." + sys.exit(-1) @@ -26,8 +26,9 @@ Platforms we officially test our releases on are listed below. We only test on d * Swift * Windows 7 to Windows 10 * Mac OS X 10.10 and Mac OS X 10.11 - * Ubuntu 14.04 ( Trusty Tahr ) and Ubuntu 16.04 ( Xenial Xerus ) + * Ubuntu 17.10 ( Artful Aardvark ) and Ubuntu 16.04 ( Xenial Xerus ) * Debian 8 ( jessie ) + * Debian 9 ( stretch ) ## External Dependencies The Swift repository includes some third party dependencies in the 3rdParty directory diff --git a/Swift/ChangeLog.md b/Swift/ChangeLog.md index 9467f7a..9152b50 100644 --- a/Swift/ChangeLog.md +++ b/Swift/ChangeLog.md @@ -1,3 +1,40 @@ +4.0.3 (2019-01-03) +------------------ +- Fix handling of empty bookmark responses + +4.0.2 (2018-04-05) +------------------ +- Fix versioning issue in Windows Installer process + +4.0.1 (2018-03-28) +------------------ +- Allow setting vCard on servers that do not return an empty vCard on fresh accounts + +4.0 (2018-03-20) +---------------- +- New chat theme including a new font +- Support for message carbons (XEP-0280) +- Enabled trellis mode as a default feature, allowing several tiled chats windows to be shown at once +- Redesigned keyword highlighting +- Improve date formatting +- Fix Last Message Correction in multi client scenarios +- Fix UI layout issue for translations that require right-to-left (RTL) layout +- Fix UX issues in trellis mode +- Improvements to font size handling in the chat theme +- Fix display of default avatar on Windows +- Support for automatic software updates on macOS +- macOS releases are now code-signed +- Support for unicode emojis on macOS +- Add AppImage for Linux 64-bit as a supported platform +- Improved spell checker support on Linux +- And assorted smaller features and usability enhancements + +4.0-rc6 ( 2018-03-07 ) +---------------------- +- Small usability fixes in Carbons ( XEP-0280 ) handling and Windows installer +- Fix crashes related to room invitations, layout changes, and vCard handling +- And smaller bug fixes + 4.0-rc5 ( 2018-01-09 ) ---------------------- - Fix crash during sleep on macOS @@ -27,7 +64,7 @@ - Fix Last Message Correction in multi client scenarios - Fix display of default avatar on Windows - Support for automatic software updates on macOS -- Redesigned keyword highlighing +- Redesigned keyword highlighting - Support for unicode emojis on macOS - Improvements to font size handling in the chat theme - Fix UX issues in trellis mode diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index fe8e870..40f6156 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -86,7 +86,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available); startMessage += "."; chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection); - chatWindow_->onContinuationsBroken.connect([this, startMessage]() { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection); }); + continuationsBrokenConnection_ = chatWindow_->onContinuationsBroken.connect([this, startMessage]() { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection); }); chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2)); @@ -320,6 +320,7 @@ void ChatController::handleIncomingOwnMessage(std::shared_ptr<Message> message) if (!message->getBody().get_value_or("").empty()) { postSendMessage(message->getBody().get_value_or(""), message); handleStanzaAcked(message); + onActivity(message->getBody().get_value_or("")); } } @@ -585,8 +586,20 @@ JID ChatController::messageCorrectionJID(const JID& fromJID) { } ChatWindow* ChatController::detachChatWindow() { - chatWindow_->onUserTyping.disconnect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); + continuationsBrokenConnection_.disconnect(); + chatWindow_->onClosed.disconnect(boost::bind(&ChatController::handleWindowClosed, this)); + chatWindow_->onInviteToChat.disconnect(boost::bind(&ChatController::handleInviteToChat, this, _1)); + chatWindow_->onUnblockUserRequest.disconnect(boost::bind(&ChatController::handleUnblockUserRequest, this)); + chatWindow_->onBlockUserRequest.disconnect(boost::bind(&ChatController::handleBlockUserRequest, this)); + chatWindow_->onWhiteboardWindowShow.disconnect(boost::bind(&ChatController::handleWhiteboardWindowShow, this)); + chatWindow_->onWhiteboardSessionCancel.disconnect(boost::bind(&ChatController::handleWhiteboardSessionCancel, this)); + chatWindow_->onWhiteboardSessionAccept.disconnect(boost::bind(&ChatController::handleWhiteboardSessionAccept, this)); + chatWindow_->onSendFileRequest.disconnect(boost::bind(&ChatController::handleSendFileRequest, this, _1)); + chatWindow_->onFileTransferCancel.disconnect(boost::bind(&ChatController::handleFileTransferCancel, this, _1)); + chatWindow_->onFileTransferAccept.disconnect(boost::bind(&ChatController::handleFileTransferAccept, this, _1, _2)); + chatWindow_->onFileTransferStart.disconnect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2)); chatWindow_->onUserCancelsTyping.disconnect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); + chatWindow_->onUserTyping.disconnect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); return ChatControllerBase::detachChatWindow(); } diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index d5011e4..a9093d0 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -113,6 +113,7 @@ namespace Swift { boost::signals2::scoped_connection blockingOnStateChangedConnection_; boost::signals2::scoped_connection blockingOnItemAddedConnection_; boost::signals2::scoped_connection blockingOnItemRemovedConnection_; + boost::signals2::scoped_connection continuationsBrokenConnection_; boost::optional<ChatWindow::AlertID> deliveryReceiptAlert_; boost::optional<ChatWindow::AlertID> blockedContactAlert_; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 0fc735a..19bbf8d 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -58,6 +58,9 @@ void ChatControllerBase::handleContinuationsBroken() { } ChatWindow* ChatControllerBase::detachChatWindow() { + chatWindow_->onContinuationsBroken.disconnect(boost::bind(&ChatControllerBase::handleContinuationsBroken, this)); + chatWindow_->onSendMessageRequest.disconnect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); + chatWindow_->onAllMessagesRead.disconnect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); ChatWindow* chatWindow = chatWindow_; chatWindow_ = nullptr; return chatWindow; diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index a6f7fe0..19400f9 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -331,11 +331,13 @@ void ChatsManager::handleBookmarksReady() { } void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) { - std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom()); - if (it == mucControllers_.end() && bookmark.getAutojoin()) { - handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false, false ); + if (bookmark.getRoom().isBare() && !bookmark.getRoom().getNode().empty()) { + std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom()); + if (it == mucControllers_.end() && bookmark.getAutojoin()) { + handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false, false ); + } + chatListWindow_->addMUCBookmark(bookmark); } - chatListWindow_->addMUCBookmark(bookmark); } void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) { diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 1502dc9..ac62942 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -32,9 +32,12 @@ #include <Swiften/Elements/Forwarded.h> #include <Swiften/Elements/MUCInvitationPayload.h> #include <Swiften/Elements/MUCUserPayload.h> +#include <Swiften/Elements/PrivateStorage.h> +#include <Swiften/Elements/Storage.h> #include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h> #include <Swiften/Jingle/JingleSessionManager.h> #include <Swiften/MUC/MUCManager.h> +#include <Swiften/MUC/UnitTest/MockMUC.h> #include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Presence/DirectedPresenceSender.h> #include <Swiften/Presence/PresenceOracle.h> @@ -71,7 +74,6 @@ #include <SwifTools/Notifier/Notifier.h> #include <Swift/QtUI/QtSwiftUtil.h> -#include <Swiften/MUC/UnitTest/MockMUC.h> using namespace Swift; @@ -155,6 +157,11 @@ class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testImpromptuChatWindowTitle); CPPUNIT_TEST(testStandardMUCChatWindowTitle); + // Bookmark tests + CPPUNIT_TEST(testReceivingBookmarksWithDomainJID); + CPPUNIT_TEST(testReceivingBookmarksWithBareJID); + CPPUNIT_TEST(testReceivingBookmarksWithFullJID); + CPPUNIT_TEST_SUITE_END(); public: @@ -1228,6 +1235,11 @@ public: CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); + + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getFrom().toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some further text.")); } } @@ -1264,6 +1276,11 @@ public: CPPUNIT_ASSERT_EQUAL(true, window->lastAddedMessageSenderIsSelf_); CPPUNIT_ASSERT_EQUAL(size_t(1), window->receiptChanges_.size()); CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptRequested, window->receiptChanges_[0].second); + + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getTo().toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some text my other resource sent.")); } // incoming carbons message for the received delivery receipt to the other resource @@ -1280,6 +1297,12 @@ public: CPPUNIT_ASSERT_EQUAL(size_t(2), window->receiptChanges_.size()); CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptReceived, window->receiptChanges_[1].second); + + //Delivery receipt should not change the latest recent entry. Checking for the original message. + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, messageJID.toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some text my other resource sent.")); } } @@ -1319,6 +1342,11 @@ public: manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(std::string(), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); + + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getFrom().toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some further text.")); } } @@ -1598,6 +1626,78 @@ public: CPPUNIT_ASSERT_EQUAL(std::string("mucroom"), window->name_); } + static std::shared_ptr<Storage> createBookmarkStorageWithJID(const JID& jid) { + auto storage = std::make_shared<Storage>(); + auto room = Storage::Room(); + room.jid = jid; + room.autoJoin = true; + storage->addRoom(room); + return storage; + } + + void testReceivingBookmarksWithDomainJID() { + auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]); + CPPUNIT_ASSERT(bookmarkRequest); + CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + + auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>(); + CPPUNIT_ASSERT(privateStorage); + + auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload()); + CPPUNIT_ASSERT(storage); + + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("montague.lit")) + ); + stanzaChannel_->onIQReceived(response); + } + + void testReceivingBookmarksWithBareJID() { + auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]); + CPPUNIT_ASSERT(bookmarkRequest); + CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + + auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>(); + CPPUNIT_ASSERT(privateStorage); + + auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload()); + CPPUNIT_ASSERT(storage); + + MockChatWindow* window = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(window); + + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("example@montague.lit")) + ); + stanzaChannel_->onIQReceived(response); + } + + void testReceivingBookmarksWithFullJID() { + auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]); + CPPUNIT_ASSERT(bookmarkRequest); + CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + + auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>(); + CPPUNIT_ASSERT(privateStorage); + + auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload()); + CPPUNIT_ASSERT(storage); + + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("example@montague.lit/someresource")) + ); + stanzaChannel_->onIQReceived(response); + } + private: std::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { std::shared_ptr<Message> message = std::make_shared<Message>(); @@ -1664,4 +1764,3 @@ private: }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); - diff --git a/Swift/Controllers/Storages/AvatarFileStorage.cpp b/Swift/Controllers/Storages/AvatarFileStorage.cpp index a103920..9d9b9ea 100644 --- a/Swift/Controllers/Storages/AvatarFileStorage.cpp +++ b/Swift/Controllers/Storages/AvatarFileStorage.cpp @@ -1,16 +1,15 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Storages/AvatarFileStorage.h> -#include <iostream> - #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> +#include <Swiften/Base/Log.h> #include <Swiften/Base/String.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/StringCodecs/Hexify.h> @@ -31,13 +30,13 @@ AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& avatarsDir, jidAvatars.insert(std::make_pair(jid, r.first)); } else if (!r.first.empty() || !r.second.empty()) { - std::cerr << "Invalid entry in avatars file: " << r.second << std::endl; + SWIFT_LOG(error) << "Invalid entry in avatars file: " << r.second << std::endl; } } } } catch (...) { - std::cerr << "Error reading avatars file" << std::endl; + SWIFT_LOG(error) << "Error reading avatars file" << std::endl; } } } @@ -55,12 +54,17 @@ void AvatarFileStorage::addAvatar(const std::string& hash, const ByteArray& avat boost::filesystem::create_directories(avatarPath.parent_path()); } catch (const boost::filesystem::filesystem_error& e) { - std::cerr << "ERROR: " << e.what() << std::endl; + SWIFT_LOG(error) << "filesystem error: " << e.what() << std::endl; } } - boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); - file.write(reinterpret_cast<const char*>(vecptr(avatar)), static_cast<std::streamsize>(avatar.size())); - file.close(); + + try { + boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); + file.write(reinterpret_cast<const char*>(vecptr(avatar)), static_cast<std::streamsize>(avatar.size())); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "filesystem error: " << e.what() << std::endl; + } } boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash) const { @@ -69,7 +73,12 @@ boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash ByteArray AvatarFileStorage::getAvatar(const std::string& hash) const { ByteArray data; - readByteArrayFromFile(data, getAvatarPath(hash)); + try { + readByteArrayFromFile(data, getAvatarPath(hash)); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "filesystem error: " << e.what() << std::endl; + } return data; } @@ -98,7 +107,7 @@ void AvatarFileStorage::saveJIDAvatars() { file.close(); } catch (...) { - std::cerr << "Error writing avatars file" << std::endl; + SWIFT_LOG(error) << "Error writing avatars file" << std::endl; } } diff --git a/Swift/Packaging/Debian/Testing/swift-distr-tests.sh b/Swift/Packaging/Debian/Testing/swift-distr-tests.sh index 8d03282..e817974 100755 --- a/Swift/Packaging/Debian/Testing/swift-distr-tests.sh +++ b/Swift/Packaging/Debian/Testing/swift-distr-tests.sh @@ -40,25 +40,31 @@ arr[0,0]="ubuntu" arr[0,1]="http://archive.ubuntu.com/ubuntu" arr[0,2]="xenial" -#Ubuntu yakkety +#Ubuntu artful arr[1,0]="ubuntu" arr[1,1]="http://archive.ubuntu.com/ubuntu" -arr[1,2]="yakkety" +arr[1,2]="artful" #Debian jessie arr[2,0]="debian" arr[2,1]="http://deb.debian.org/debian/" arr[2,2]="jessie" -#Debian sid +#Debian stretch arr[3,0]="debian" arr[3,1]="http://deb.debian.org/debian/" -arr[3,2]="sid" +arr[3,2]="stretch" + +#Debian sid +arr[4,0]="debian" +arr[4,1]="http://deb.debian.org/debian/" +arr[4,2]="sid" + DIST="$1" RETURN_VALUE=0 -for counter in {0..3} +for counter in {0..4} do # for ARCH in "amd64" "i386" for ARCH in "amd64" diff --git a/Swift/Packaging/Debian/changelog.debian-unstable b/Swift/Packaging/Debian/changelog.debian-unstable index ca9ffec..f2bf2c5 100644 --- a/Swift/Packaging/Debian/changelog.debian-unstable +++ b/Swift/Packaging/Debian/changelog.debian-unstable @@ -1,3 +1,21 @@ +swift-im (4.0.3-1) UNRELEASED; urgency=medium + + * New chat theme including a new font + * Support for message carbons (XEP-0280) + * Enabled trellis mode as a default feature, allowing several tiled chats windows to be shown at once + * Redesigned keyword highlighting + * Improve date formatting + * Fix Last Message Correction in multi client scenarios + * Fix UI layout issue for translations that require right-to-left (RTL) layout + * Fix UX issues in trellis mode + * Improvements to font size handling in the chat theme + * Add AppImage for Linux 64-bit as a supported platform + * Improved spell checker support on Linux + * Allow setting vCard on servers that do not return an empty vCard on fresh accounts + * And assorted smaller features and usability enhancements. Closes: 840151, 889062 + + -- Kevin Smith <kevin@kismith.co.uk> Thu, 03 Jan 2019 13:59:07 +0100 + swift-im (3.0.4-1) unstable; urgency=low * New upstream release diff --git a/Swift/Packaging/Debian/debian/copyright b/Swift/Packaging/Debian/debian/copyright index a0c2c79..9219930 100644 --- a/Swift/Packaging/Debian/debian/copyright +++ b/Swift/Packaging/Debian/debian/copyright @@ -3,7 +3,7 @@ with help from Olly Betts <olly@survex.com>. The upstream sources were obtained from http://swift.im. -Copyright (C) 2010-2016 Isode Limited. +Copyright (C) 2010-2019 Isode Limited. Licensed under the GNU General Public License. See /usr/share/common-licenses/GPL-3 for the full license. diff --git a/Swift/Packaging/Debian/package_all_platforms.sh b/Swift/Packaging/Debian/package_all_platforms.sh index a61201d..afd0621 100755 --- a/Swift/Packaging/Debian/package_all_platforms.sh +++ b/Swift/Packaging/Debian/package_all_platforms.sh @@ -26,7 +26,7 @@ unset SWIFT_FORCE_LUCID if [ -z ${SWIFT_PACKAGE_PLATFORMS+x} ]; then - platformsarray=( xenial yakkety jessie sid ) + platformsarray=( xenial artful jessie stretch sid ) else platformsarray=( $SWIFT_PACKAGE_PLATFORMS ) fi diff --git a/Swift/Packaging/Debian/update_debian_repo.sh b/Swift/Packaging/Debian/update_debian_repo.sh index db9f326..b057103 100644 --- a/Swift/Packaging/Debian/update_debian_repo.sh +++ b/Swift/Packaging/Debian/update_debian_repo.sh @@ -74,7 +74,7 @@ command -v reprepro >/dev/null 2>&1 || { echo >&2 "This script requires aptly bu mkdir -p $APT_REPO_ROOT if [ -z ${SWIFT_PACKAGE_PLATFORMS+x} ]; then - platformsarray=( xenial yakkety jessie sid ) + platformsarray=( xenial artful jessie stretch sid ) else platformsarray=( $SWIFT_PACKAGE_PLATFORMS ) fi diff --git a/Swift/Packaging/SConscript b/Swift/Packaging/SConscript index bd7e6d8..3aa791f 100644 --- a/Swift/Packaging/SConscript +++ b/Swift/Packaging/SConscript @@ -20,10 +20,10 @@ if env["SCONS_STAGE"] == "build" : swiftenConfigHelp = env.Command( target='$HELP2MAN_DEBIAN_DIR/swiften-config.1', source='#/Swiften/Config/swiften-config', - action = Action('$HELP2MAN -m "Swift Manual" -S "swiften-config" -n "swiften-config" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) + action = Action('$HELP2MAN --no-discard-stderr -m "Swift Manual" -S "swiften-config" -n "swiften-config" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) swiftHelp = env.Command( target='$HELP2MAN_DEBIAN_DIR/swift-im.1', source='#/Swift/QtUI/swift-im', - action = Action('$HELP2MAN -m "Swift Manual" -S "Swift" -n "swift-im" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) + action = Action('$HELP2MAN --no-discard-stderr -m "Swift Manual" -S "Swift" -n "swift-im" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) else: print "Enabled help2man but help2man is not in the PATH of the current environment." - Exit(1)
\ No newline at end of file + Exit(1) diff --git a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp index b753ffa..2509b3f 100644 --- a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp +++ b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp @@ -144,7 +144,9 @@ void QtDynamicGridLayout::removeTab(int index) { int tabIndex = -1; QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); if (tabWidget) { + QWidget* tab = tabWidget->widget(tabIndex); tabWidget->removeTab(tabIndex); + tab->setParent(nullptr); } } diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h index 0714ac1..fe536ab 100644 --- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h +++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,6 +8,7 @@ #include <set> +#include <QAbstractItemModel> #include <QWizard> #include <Swiften/Base/Override.h> diff --git a/Swiften/Avatars/UnitTest/AvatarManagerImplTest.cpp b/Swiften/Avatars/UnitTest/AvatarManagerImplTest.cpp index 241f375..5a35410 100644 --- a/Swiften/Avatars/UnitTest/AvatarManagerImplTest.cpp +++ b/Swiften/Avatars/UnitTest/AvatarManagerImplTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 Isode Limited. + * Copyright (c) 2014-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -91,6 +91,7 @@ class AvatarManagerImplTest : public CppUnit::TestFixture { /* send new presence to notify of blank avatar */ vcardUpdate = std::make_shared<VCardUpdate>(); + vcardUpdate->setPhotoHash("da39a3ee5e6b4b0d3255bfef95601890afd80709"); presence = std::make_shared<Presence>(); presence->setTo(ownerJID); presence->setFrom(personJID); diff --git a/Swiften/Avatars/VCardUpdateAvatarManager.cpp b/Swiften/Avatars/VCardUpdateAvatarManager.cpp index 3e8d87b..349af2f 100644 --- a/Swiften/Avatars/VCardUpdateAvatarManager.cpp +++ b/Swiften/Avatars/VCardUpdateAvatarManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -32,6 +32,10 @@ void VCardUpdateAvatarManager::handlePresenceReceived(std::shared_ptr<Presence> return; } JID from = getAvatarJID(presence->getFrom()); + if (update->getPhotoHash().size() != 40) { + SWIFT_LOG(debug) << "Invalid vCard avatar photo hash length. Must be hex-encoded SHA-1, i.e. 40 characters." << std::endl; + return; + } if (getAvatarHash(from) == update->getPhotoHash()) { return; } diff --git a/Swiften/Base/Platform.h b/Swiften/Base/Platform.h index 4deba2b..22dff30 100644 --- a/Swiften/Base/Platform.h +++ b/Swiften/Base/Platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -43,9 +43,9 @@ #endif // Endianness -#include <boost/detail/endian.hpp> -#if defined(BOOST_LITTLE_ENDIAN) +#include <boost/predef/other/endian.h> +#if defined(BOOST_ENDIAN_LITTLE_BYTE) #define SWIFTEN_LITTLE_ENDIAN -#elif defined(BOOST_BIG_ENDIAN) +#elif defined(BOOST_ENDIAN_BIG_BYTE) #define SWIFTEN_BIG_ENDIAN #endif diff --git a/Swiften/ChangeLog.md b/Swiften/ChangeLog.md index d823954..23d5185 100644 --- a/Swiften/ChangeLog.md +++ b/Swiften/ChangeLog.md @@ -1,3 +1,24 @@ +4.0.1 (2018-03-28) +------------------ +- Fix handling errors when fetching own vCard + +4.0 (2018-03-20) +---------------- +- Moved code-base to C++11 + - Use C++11 threading instead of Boost.Thread library + - Use C++11 smart pointers instead of Boost's +- Migrated from Boost.Signals to Boost.Signals2 +- Build without warnings on our CI platforms +- General cleanup like remove of superflous files and #include statements. This means header files that previously were included implictly need to be explicitly included now +- Support IPv6 addresses in URLs +- Handle sessions being closed by the server +- Verify certificates when using HTTPS in BOSH connections +- In memory caching of latest entity capabilites lookups +- Changed source code style to use soft tabs (4 spaces wide) instead of hard tabs. Custom patches for Swiften will need to be reformatted accordingly +- Require a TLS backend for building +- Update 3rdParty/lcov to version 1.12 +- Fix several possible race conditions, smaller leaks, and other small bugs + 4.0-rc1 ( 2017-05-17 ) ---------------------- - Handle sessions being closed by the server diff --git a/Swiften/MUC/MUCBookmarkManager.cpp b/Swiften/MUC/MUCBookmarkManager.cpp index 9f8ae77..511c88a 100644 --- a/Swiften/MUC/MUCBookmarkManager.cpp +++ b/Swiften/MUC/MUCBookmarkManager.cpp @@ -25,7 +25,7 @@ MUCBookmarkManager::MUCBookmarkManager(IQRouter* iqRouter) { } void MUCBookmarkManager::handleBookmarksReceived(std::shared_ptr<Storage> payload, ErrorPayload::ref error) { - if (error) { + if (error || !payload) { return; } diff --git a/Swiften/Network/PlatformNATTraversalWorker.cpp b/Swiften/Network/PlatformNATTraversalWorker.cpp index f56de0b..eaa13b3 100644 --- a/Swiften/Network/PlatformNATTraversalWorker.cpp +++ b/Swiften/Network/PlatformNATTraversalWorker.cpp @@ -157,7 +157,7 @@ NATTraversalInterface* PlatformNATTraversalWorker::getNATTraversalInterface() co miniUPnPInterface = new MiniUPnPInterface(); miniUPnPSupported = miniUPnPInterface->isAvailable(); } - SWIFT_LOG(debug) << "UPnP NAT traversal supported: " << miniUPnPSupported << std::endl; + SWIFT_LOG(debug) << "UPnP NAT traversal supported: " << static_cast<bool>(miniUPnPSupported) << std::endl; if (miniUPnPSupported) { return miniUPnPInterface; } @@ -168,7 +168,7 @@ NATTraversalInterface* PlatformNATTraversalWorker::getNATTraversalInterface() co natPMPInterface = new NATPMPInterface(); natPMPSupported = natPMPInterface->isAvailable(); } - SWIFT_LOG(debug) << "NAT-PMP NAT traversal supported: " << natPMPSupported << std::endl; + SWIFT_LOG(debug) << "NAT-PMP NAT traversal supported: " << static_cast<bool>(natPMPSupported) << std::endl; if (natPMPSupported) { return natPMPInterface; } diff --git a/Swiften/VCards/UnitTest/VCardManagerTest.cpp b/Swiften/VCards/UnitTest/VCardManagerTest.cpp index 3d5338d..669c3ff 100644 --- a/Swiften/VCards/UnitTest/VCardManagerTest.cpp +++ b/Swiften/VCards/UnitTest/VCardManagerTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -31,7 +31,17 @@ class VCardManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testRequest_Error); CPPUNIT_TEST(testRequest_VCardAlreadyRequested); CPPUNIT_TEST(testRequest_AfterPreviousRequest); - CPPUNIT_TEST(testRequestOwnVCard); + + CPPUNIT_TEST(testRequestVCard_ReturnFullVCard); + CPPUNIT_TEST(testRequestVCard_ReturnEmptyVCard); + CPPUNIT_TEST(testRequestVCard_ReturnItemNotFoundError); + CPPUNIT_TEST(testRequestVCard_ReturnFeatureNotImplementedError); + + CPPUNIT_TEST(testRequestOwnVCard_ReturnFullVCard); + CPPUNIT_TEST(testRequestOwnVCard_ReturnEmptyVCard); + CPPUNIT_TEST(testRequestOwnVCard_ReturnItemNotFoundError); + CPPUNIT_TEST(testRequestOwnVCard_ReturnFeatureNotImplementedError); + CPPUNIT_TEST(testCreateSetVCardRequest); CPPUNIT_TEST(testCreateSetVCardRequest_Error); CPPUNIT_TEST_SUITE_END(); @@ -54,7 +64,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testGet_NewVCardRequestsVCard() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); CPPUNIT_ASSERT(!result); @@ -63,7 +73,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testGet_ExistingVCard() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); VCard::ref vcard(new VCard()); vcard->setFullName("Foo Bar"); vcardStorage->setVCard(JID("foo@bar.com/baz"), vcard); @@ -75,7 +85,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testRequest_RequestsVCard() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); @@ -83,7 +93,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testRequest_ReceiveEmitsNotification() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); stanzaChannel->onIQReceived(createVCardResult()); @@ -96,7 +106,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testRequest_Error() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); @@ -105,7 +115,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testRequest_VCardAlreadyRequested() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); @@ -114,7 +124,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testRequest_AfterPreviousRequest() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); stanzaChannel->onIQReceived(createVCardResult()); testling->requestVCard(JID("foo@bar.com/baz")); @@ -123,8 +133,60 @@ class VCardManagerTest : public CppUnit::TestFixture { CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(1, JID("foo@bar.com/baz"), IQ::Get)); } - void testRequestOwnVCard() { - std::shared_ptr<VCardManager> testling = createManager(); + void testRequestVCard_ReturnFullVCard() { + auto testling = createManager(); + testling->requestVCard(JID("foo@bar.com/baz")); + stanzaChannel->onIQReceived(createVCardResult()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID("foo@bar.com/baz"), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), changes[0].first); + CPPUNIT_ASSERT_EQUAL(std::string("Foo Bar"), changes[0].second->getFullName()); + CPPUNIT_ASSERT_EQUAL(false, changes[0].second->isEmpty()); + } + + void testRequestVCard_ReturnEmptyVCard() { + auto testling = createManager(); + testling->requestVCard(JID("foo@bar.com/baz")); + stanzaChannel->onIQReceived([&](){ + auto vcard = std::make_shared<VCard>(); + return IQ::createResult(JID("foo@bar.com/baz"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), vcard); + }()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID("foo@bar.com/baz"), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(true, changes[0].second->isEmpty()); + } + + void testRequestVCard_ReturnItemNotFoundError() { + auto testling = createManager(); + testling->requestVCard(JID("foo@bar.com/baz")); + stanzaChannel->onIQReceived([&](){ + return IQ::createError(JID("foo@bar.com/baz"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), ErrorPayload::ItemNotFound); + }()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID("foo@bar.com/baz"), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(true, changes[0].second->isEmpty()); + } + + void testRequestVCard_ReturnFeatureNotImplementedError() { + auto testling = createManager(); + testling->requestVCard(JID("foo@bar.com/baz")); + stanzaChannel->onIQReceived([&](){ + return IQ::createError(JID("foo@bar.com/baz"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), ErrorPayload::FeatureNotImplemented); + }()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID("foo@bar.com/baz"), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size())); + } + + void testRequestOwnVCard_ReturnFullVCard() { + auto testling = createManager(); testling->requestVCard(ownJID); stanzaChannel->onIQReceived(createOwnVCardResult()); @@ -139,8 +201,47 @@ class VCardManagerTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("Myself"), ownChanges[0]->getFullName()); } + void testRequestOwnVCard_ReturnEmptyVCard() { + auto testling = createManager(); + testling->requestVCard(ownJID); + stanzaChannel->onIQReceived([&](){ + auto vcard = std::make_shared<VCard>(); + return IQ::createResult(JID(), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), vcard); + }()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID(), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(true, changes[0].second->isEmpty()); + } + + void testRequestOwnVCard_ReturnItemNotFoundError() { + auto testling = createManager(); + testling->requestVCard(ownJID); + stanzaChannel->onIQReceived([&](){ + return IQ::createError(JID(), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), ErrorPayload::ItemNotFound); + }()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID(), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(true, changes[0].second->isEmpty()); + } + + void testRequestOwnVCard_ReturnFeatureNotImplementedError() { + auto testling = createManager(); + testling->requestVCard(ownJID); + stanzaChannel->onIQReceived([&](){ + return IQ::createError(JID(), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), ErrorPayload::FeatureNotImplemented); + }()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCard>(0, JID(), IQ::Get)); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size())); + } + void testCreateSetVCardRequest() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); VCard::ref vcard = std::make_shared<VCard>(); vcard->setFullName("New Name"); SetVCardRequest::ref request = testling->createSetVCardRequest(vcard); @@ -154,7 +255,7 @@ class VCardManagerTest : public CppUnit::TestFixture { } void testCreateSetVCardRequest_Error() { - std::shared_ptr<VCardManager> testling = createManager(); + auto testling = createManager(); VCard::ref vcard = std::make_shared<VCard>(); vcard->setFullName("New Name"); SetVCardRequest::ref request = testling->createSetVCardRequest(vcard); diff --git a/Swiften/VCards/VCardManager.cpp b/Swiften/VCards/VCardManager.cpp index 95b96fa..9423702 100644 --- a/Swiften/VCards/VCardManager.cpp +++ b/Swiften/VCards/VCardManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -50,10 +50,9 @@ void VCardManager::requestOwnVCard() { requestVCard(JID()); } - void VCardManager::handleVCardReceived(const JID& actualJID, VCard::ref vcard, ErrorPayload::ref error) { requestedVCards.erase(actualJID); - if (!error) { + if (!error || (error && error->getCondition() == ErrorPayload::ItemNotFound)) { if (!vcard) { vcard = VCard::ref(new VCard()); } |