summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2009-06-01 08:48:42 (GMT)
committerRemko Tronçon <git@el-tramo.be>2009-06-01 09:24:28 (GMT)
commit2812bddd81f8a1b804c7460f4e14cd0aa393d129 (patch)
treed46294f35150c4f0f43deaf2d31fceaf945ae715 /Swiften
downloadswift-2812bddd81f8a1b804c7460f4e14cd0aa393d129.zip
swift-2812bddd81f8a1b804c7460f4e14cd0aa393d129.tar.bz2
Import.
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/Application/Application.cpp27
-rw-r--r--Swiften/Application/Application.h31
-rw-r--r--Swiften/Application/ApplicationMessageDisplay.cpp8
-rw-r--r--Swiften/Application/ApplicationMessageDisplay.h15
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplication.cpp18
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplication.h22
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplicationInitializer.h16
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplicationInitializer.mm24
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h17
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.mm20
-rw-r--r--Swiften/Application/MacOSX/Makefile.inc6
-rw-r--r--Swiften/Application/Makefile.inc9
-rw-r--r--Swiften/Application/NullApplicationMessageDisplay.h16
-rw-r--r--Swiften/Application/Platform/PlatformApplication.h24
-rw-r--r--Swiften/Application/UnitTest/ApplicationTest.cpp39
-rw-r--r--Swiften/Application/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Application/Unix/UnixApplication.h29
-rw-r--r--Swiften/Application/Windows/WindowsApplication.h33
-rw-r--r--Swiften/Base/ByteArray.cpp34
-rw-r--r--Swiften/Base/ByteArray.h92
-rw-r--r--Swiften/Base/IDGenerator.cpp28
-rw-r--r--Swiften/Base/IDGenerator.h18
-rw-r--r--Swiften/Base/Makefile.inc7
-rw-r--r--Swiften/Base/Platform.h44
-rw-r--r--Swiften/Base/String.cpp93
-rw-r--r--Swiften/Base/String.h115
-rw-r--r--Swiften/Base/UnitTest/IDGeneratorTest.cpp34
-rw-r--r--Swiften/Base/UnitTest/Makefile.inc3
-rw-r--r--Swiften/Base/UnitTest/StringTest.cpp146
-rw-r--r--Swiften/Base/foreach.h9
-rw-r--r--Swiften/Base/sleep.cpp14
-rw-r--r--Swiften/Base/sleep.h8
-rw-r--r--Swiften/Client/Client.cpp147
-rw-r--r--Swiften/Client/Client.h66
-rw-r--r--Swiften/Client/ClientError.h32
-rw-r--r--Swiften/Client/Makefile.inc5
-rw-r--r--Swiften/Client/Session.cpp292
-rw-r--r--Swiften/Client/Session.h126
-rw-r--r--Swiften/Client/StanzaChannel.h22
-rw-r--r--Swiften/Client/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Client/UnitTest/SessionTest.cpp752
-rw-r--r--Swiften/Compress/Makefile.inc4
-rw-r--r--Swiften/Compress/UnitTest/Makefile.inc3
-rw-r--r--Swiften/Compress/UnitTest/ZLibCompressorTest.cpp35
-rw-r--r--Swiften/Compress/UnitTest/ZLibDecompressorTest.cpp71
-rw-r--r--Swiften/Compress/ZLibCodecompressor.cpp43
-rw-r--r--Swiften/Compress/ZLibCodecompressor.h22
-rw-r--r--Swiften/Compress/ZLibCompressor.h31
-rw-r--r--Swiften/Compress/ZLibDecompressor.h28
-rw-r--r--Swiften/Compress/ZLibException.h11
-rw-r--r--Swiften/Controllers/ChatController.cpp38
-rw-r--r--Swiften/Controllers/ChatController.h24
-rw-r--r--Swiften/Controllers/ChatControllerBase.cpp171
-rw-r--r--Swiften/Controllers/ChatControllerBase.h57
-rw-r--r--Swiften/Controllers/ChatWindow.h37
-rw-r--r--Swiften/Controllers/ChatWindowFactory.h20
-rw-r--r--Swiften/Controllers/EventController.cpp21
-rw-r--r--Swiften/Controllers/EventController.h24
-rw-r--r--Swiften/Controllers/LoginWindow.h22
-rw-r--r--Swiften/Controllers/LoginWindowFactory.h22
-rw-r--r--Swiften/Controllers/MUCController.cpp76
-rw-r--r--Swiften/Controllers/MUCController.h43
-rw-r--r--Swiften/Controllers/MainController.cpp259
-rw-r--r--Swiften/Controllers/MainController.h85
-rw-r--r--Swiften/Controllers/MainWindow.h26
-rw-r--r--Swiften/Controllers/MainWindowFactory.h20
-rw-r--r--Swiften/Controllers/Makefile.inc11
-rw-r--r--Swiften/Controllers/NickResolver.cpp22
-rw-r--r--Swiften/Controllers/NickResolver.h24
-rw-r--r--Swiften/Controllers/RosterController.cpp83
-rw-r--r--Swiften/Controllers/RosterController.h48
-rw-r--r--Swiften/Controllers/UnitTest/Makefile.inc4
-rw-r--r--Swiften/Controllers/UnitTest/NickResolverTest.cpp62
-rw-r--r--Swiften/Controllers/UnitTest/XMPPRosterControllerTest.cpp83
-rw-r--r--Swiften/Controllers/XMPPRosterController.cpp54
-rw-r--r--Swiften/Controllers/XMPPRosterController.h28
-rw-r--r--Swiften/Disco/CapsInfoGenerator.cpp34
-rw-r--r--Swiften/Disco/CapsInfoGenerator.h21
-rw-r--r--Swiften/Disco/Makefile.inc4
-rw-r--r--Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp35
-rw-r--r--Swiften/Disco/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Elements/AuthFailure.h13
-rw-r--r--Swiften/Elements/AuthRequest.h36
-rw-r--r--Swiften/Elements/AuthSuccess.h13
-rw-r--r--Swiften/Elements/Body.h26
-rw-r--r--Swiften/Elements/CapsInfo.h23
-rw-r--r--Swiften/Elements/CompressFailure.h13
-rw-r--r--Swiften/Elements/CompressRequest.h25
-rw-r--r--Swiften/Elements/Compressed.h14
-rw-r--r--Swiften/Elements/DiscoInfo.cpp25
-rw-r--r--Swiften/Elements/DiscoInfo.h83
-rw-r--r--Swiften/Elements/Element.cpp8
-rw-r--r--Swiften/Elements/Element.h11
-rw-r--r--Swiften/Elements/Error.h70
-rw-r--r--Swiften/Elements/IQ.cpp37
-rw-r--r--Swiften/Elements/IQ.h39
-rw-r--r--Swiften/Elements/MUCPayload.cpp0
-rw-r--r--Swiften/Elements/MUCPayload.h16
-rw-r--r--Swiften/Elements/Makefile.inc9
-rw-r--r--Swiften/Elements/Message.h46
-rw-r--r--Swiften/Elements/Payload.cpp8
-rw-r--r--Swiften/Elements/Payload.h11
-rw-r--r--Swiften/Elements/Presence.h58
-rw-r--r--Swiften/Elements/Priority.h25
-rw-r--r--Swiften/Elements/ResourceBind.h36
-rw-r--r--Swiften/Elements/RosterItemPayload.h43
-rw-r--r--Swiften/Elements/RosterPayload.cpp17
-rw-r--r--Swiften/Elements/RosterPayload.h33
-rw-r--r--Swiften/Elements/SecurityLabel.h57
-rw-r--r--Swiften/Elements/SecurityLabelsCatalog.h56
-rw-r--r--Swiften/Elements/SoftwareVersion.h47
-rw-r--r--Swiften/Elements/Stanza.cpp31
-rw-r--r--Swiften/Elements/Stanza.h60
-rw-r--r--Swiften/Elements/StartSession.h14
-rw-r--r--Swiften/Elements/StartTLSFailure.h13
-rw-r--r--Swiften/Elements/StartTLSRequest.h14
-rw-r--r--Swiften/Elements/Status.h26
-rw-r--r--Swiften/Elements/StatusShow.h27
-rw-r--r--Swiften/Elements/StreamFeatures.h77
-rw-r--r--Swiften/Elements/TLSProceed.h14
-rw-r--r--Swiften/Elements/UnitTest/IQTest.cpp52
-rw-r--r--Swiften/Elements/UnitTest/Makefile.inc3
-rw-r--r--Swiften/Elements/UnitTest/StanzaTest.cpp155
-rw-r--r--Swiften/Elements/UnitTest/StanzasTest.cpp3
-rw-r--r--Swiften/Elements/UnknownElement.h14
-rw-r--r--Swiften/Elements/Version.h24
-rw-r--r--Swiften/EventLoop/Deleter.h23
-rw-r--r--Swiften/EventLoop/DummyEventLoop.h37
-rw-r--r--Swiften/EventLoop/EventLoop.cpp49
-rw-r--r--Swiften/EventLoop/EventLoop.h52
-rw-r--r--Swiften/EventLoop/MainEventLoop.cpp35
-rw-r--r--Swiften/EventLoop/MainEventLoop.h40
-rw-r--r--Swiften/EventLoop/Makefile.inc6
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.cpp48
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.h31
-rw-r--r--Swiften/EventLoop/UnitTest/EventLoopTest.cpp63
-rw-r--r--Swiften/EventLoop/UnitTest/Makefile.inc3
-rw-r--r--Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp62
-rw-r--r--Swiften/Events/Makefile.inc0
-rw-r--r--Swiften/Events/MessageEvent.h31
-rw-r--r--Swiften/Examples/Makefile.inc4
-rw-r--r--Swiften/Examples/TuneBot/Makefile.inc11
-rw-r--r--Swiften/Examples/TuneBot/TuneBot.cpp72
-rw-r--r--Swiften/JID/JID.cpp116
-rw-r--r--Swiften/JID/JID.h68
-rw-r--r--Swiften/JID/Makefile.inc4
-rw-r--r--Swiften/JID/UnitTest/JIDTest.cpp310
-rw-r--r--Swiften/JID/UnitTest/Makefile.inc2
-rw-r--r--Swiften/MUC/MUC.cpp69
-rw-r--r--Swiften/MUC/MUC.h49
-rw-r--r--Swiften/MUC/MUCOccupant.cpp15
-rw-r--r--Swiften/MUC/MUCOccupant.h21
-rw-r--r--Swiften/MUC/Makefile.inc3
-rw-r--r--Swiften/Makefile.inc42
-rw-r--r--Swiften/Network/BoostConnection.cpp100
-rw-r--r--Swiften/Network/BoostConnection.h43
-rw-r--r--Swiften/Network/BoostConnectionFactory.cpp12
-rw-r--r--Swiften/Network/BoostConnectionFactory.h18
-rw-r--r--Swiften/Network/Connection.h40
-rw-r--r--Swiften/Network/ConnectionFactory.cpp8
-rw-r--r--Swiften/Network/ConnectionFactory.h16
-rw-r--r--Swiften/Network/DomainNameResolveException.h11
-rw-r--r--Swiften/Network/DomainNameResolver.cpp176
-rw-r--r--Swiften/Network/DomainNameResolver.h27
-rw-r--r--Swiften/Network/HostAddress.cpp49
-rw-r--r--Swiften/Network/HostAddress.h24
-rw-r--r--Swiften/Network/HostAddressPort.h26
-rw-r--r--Swiften/Network/Makefile.inc13
-rw-r--r--Swiften/Network/Timer.cpp40
-rw-r--r--Swiften/Network/Timer.h31
-rw-r--r--Swiften/Network/UnitTest/HostAddressTest.cpp33
-rw-r--r--Swiften/Network/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Notifier/GrowlNotifier.cpp91
-rw-r--r--Swiften/Notifier/GrowlNotifier.h28
-rw-r--r--Swiften/Notifier/Makefile.inc7
-rw-r--r--Swiften/Notifier/Notifier.cpp8
-rw-r--r--Swiften/Notifier/Notifier.h24
-rw-r--r--Swiften/Parser/AttributeMap.h25
-rw-r--r--Swiften/Parser/AuthFailureParser.h14
-rw-r--r--Swiften/Parser/AuthRequestParser.cpp27
-rw-r--r--Swiften/Parser/AuthRequestParser.h23
-rw-r--r--Swiften/Parser/AuthSuccessParser.h14
-rw-r--r--Swiften/Parser/CompressFailureParser.h14
-rw-r--r--Swiften/Parser/CompressParser.cpp28
-rw-r--r--Swiften/Parser/CompressParser.h25
-rw-r--r--Swiften/Parser/CompressedParser.h14
-rw-r--r--Swiften/Parser/ElementParser.cpp8
-rw-r--r--Swiften/Parser/ElementParser.h23
-rw-r--r--Swiften/Parser/ExpatParser.cpp66
-rw-r--r--Swiften/Parser/ExpatParser.h22
-rw-r--r--Swiften/Parser/GenericElementParser.h43
-rw-r--r--Swiften/Parser/GenericPayloadParser.h32
-rw-r--r--Swiften/Parser/GenericPayloadParserFactory.h28
-rw-r--r--Swiften/Parser/GenericStanzaParser.h33
-rw-r--r--Swiften/Parser/IQParser.cpp33
-rw-r--r--Swiften/Parser/IQParser.h17
-rw-r--r--Swiften/Parser/LibXMLParser.cpp63
-rw-r--r--Swiften/Parser/LibXMLParser.h23
-rw-r--r--Swiften/Parser/Makefile.inc32
-rw-r--r--Swiften/Parser/MessageParser.cpp33
-rw-r--r--Swiften/Parser/MessageParser.h17
-rw-r--r--Swiften/Parser/PayloadParser.cpp8
-rw-r--r--Swiften/Parser/PayloadParser.h24
-rw-r--r--Swiften/Parser/PayloadParserFactory.cpp8
-rw-r--r--Swiften/Parser/PayloadParserFactory.h19
-rw-r--r--Swiften/Parser/PayloadParserFactoryCollection.cpp27
-rw-r--r--Swiften/Parser/PayloadParserFactoryCollection.h25
-rw-r--r--Swiften/Parser/PayloadParsers/BodyParser.cpp23
-rw-r--r--Swiften/Parser/PayloadParsers/BodyParser.h22
-rw-r--r--Swiften/Parser/PayloadParsers/BodyParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp27
-rw-r--r--Swiften/Parser/PayloadParsers/DiscoInfoParser.h25
-rw-r--r--Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/ErrorParser.cpp109
-rw-r--r--Swiften/Parser/PayloadParsers/ErrorParser.h26
-rw-r--r--Swiften/Parser/PayloadParsers/ErrorParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp46
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h21
-rw-r--r--Swiften/Parser/PayloadParsers/Makefile.inc15
-rw-r--r--Swiften/Parser/PayloadParsers/PriorityParser.cpp25
-rw-r--r--Swiften/Parser/PayloadParsers/PriorityParser.h22
-rw-r--r--Swiften/Parser/PayloadParsers/PriorityParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/ResourceBindParser.cpp37
-rw-r--r--Swiften/Parser/PayloadParsers/ResourceBindParser.h24
-rw-r--r--Swiften/Parser/PayloadParsers/ResourceBindParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/RosterParser.cpp66
-rw-r--r--Swiften/Parser/PayloadParsers/RosterParser.h29
-rw-r--r--Swiften/Parser/PayloadParsers/RosterParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/SecurityLabelParser.cpp59
-rw-r--r--Swiften/Parser/PayloadParsers/SecurityLabelParser.h31
-rw-r--r--Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.cpp55
-rw-r--r--Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h32
-rw-r--r--Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/SoftwareVersionParser.cpp32
-rw-r--r--Swiften/Parser/PayloadParsers/SoftwareVersionParser.h26
-rw-r--r--Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/StartSessionParser.h18
-rw-r--r--Swiften/Parser/PayloadParsers/StartSessionParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/StatusParser.cpp23
-rw-r--r--Swiften/Parser/PayloadParsers/StatusParser.h22
-rw-r--r--Swiften/Parser/PayloadParsers/StatusParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/StatusShowParser.cpp37
-rw-r--r--Swiften/Parser/PayloadParsers/StatusShowParser.h22
-rw-r--r--Swiften/Parser/PayloadParsers/StatusShowParserFactory.h14
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp29
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp48
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp35
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/Makefile.inc12
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h10
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp29
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp40
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp51
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp46
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp46
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp36
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp29
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp73
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/XMLPayloadParser.h42
-rw-r--r--Swiften/Parser/PlatformXMLParserFactory.cpp28
-rw-r--r--Swiften/Parser/PlatformXMLParserFactory.h15
-rw-r--r--Swiften/Parser/PresenceParser.cpp45
-rw-r--r--Swiften/Parser/PresenceParser.h17
-rw-r--r--Swiften/Parser/SerializingParser.cpp41
-rw-r--r--Swiften/Parser/SerializingParser.h25
-rw-r--r--Swiften/Parser/StanzaParser.cpp78
-rw-r--r--Swiften/Parser/StanzaParser.h48
-rw-r--r--Swiften/Parser/StartTLSFailureParser.h14
-rw-r--r--Swiften/Parser/StartTLSParser.h14
-rw-r--r--Swiften/Parser/StreamFeaturesParser.cpp61
-rw-r--r--Swiften/Parser/StreamFeaturesParser.h28
-rw-r--r--Swiften/Parser/TLSProceedParser.h14
-rw-r--r--Swiften/Parser/UnitTest/ElementParserTester.h10
-rw-r--r--Swiften/Parser/UnitTest/IQParserTest.cpp70
-rw-r--r--Swiften/Parser/UnitTest/Makefile.inc9
-rw-r--r--Swiften/Parser/UnitTest/MessageParserTest.cpp80
-rw-r--r--Swiften/Parser/UnitTest/ParserTester.h44
-rw-r--r--Swiften/Parser/UnitTest/PresenceParserTest.cpp110
-rw-r--r--Swiften/Parser/UnitTest/SerializingParserTest.cpp58
-rw-r--r--Swiften/Parser/UnitTest/StanzaParserTest.cpp209
-rw-r--r--Swiften/Parser/UnitTest/StanzaParserTester.h11
-rw-r--r--Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp63
-rw-r--r--Swiften/Parser/UnitTest/XMLParserTest.cpp194
-rw-r--r--Swiften/Parser/UnitTest/XMPPParserTest.cpp166
-rw-r--r--Swiften/Parser/UnknownElementParser.h14
-rw-r--r--Swiften/Parser/UnknownPayloadParser.h25
-rw-r--r--Swiften/Parser/XMLParser.cpp11
-rw-r--r--Swiften/Parser/XMLParser.h25
-rw-r--r--Swiften/Parser/XMLParserClient.cpp9
-rw-r--r--Swiften/Parser/XMLParserClient.h19
-rw-r--r--Swiften/Parser/XMLParserFactory.cpp8
-rw-r--r--Swiften/Parser/XMLParserFactory.h16
-rw-r--r--Swiften/Parser/XMPPParser.cpp145
-rw-r--r--Swiften/Parser/XMPPParser.h54
-rw-r--r--Swiften/Parser/XMPPParserClient.cpp8
-rw-r--r--Swiften/Parser/XMPPParserClient.h20
-rw-r--r--Swiften/Presence/Makefile.inc4
-rw-r--r--Swiften/Presence/PresenceOracle.cpp29
-rw-r--r--Swiften/Presence/PresenceOracle.h25
-rw-r--r--Swiften/Presence/UnitTest/Makefile.inc0
-rw-r--r--Swiften/QA/ClientTest/ClientTest.cpp63
-rw-r--r--Swiften/QA/ClientTest/Makefile.inc16
-rw-r--r--Swiften/QA/Makefile.inc11
-rw-r--r--Swiften/QA/NetworkTest/BoostConnectionTest.cpp51
-rw-r--r--Swiften/QA/NetworkTest/DomainNameResolverTest.cpp64
-rw-r--r--Swiften/QA/NetworkTest/Makefile.inc18
-rw-r--r--Swiften/QA/UnitTest/Makefile.inc17
-rw-r--r--Swiften/QA/UnitTest/checker.cpp16
-rw-r--r--Swiften/QA/UnitTest/template/FooTest.cpp24
-rw-r--r--Swiften/QA/valgrind.supp51
-rw-r--r--Swiften/Queries/DummyIQChannel.h25
-rw-r--r--Swiften/Queries/GenericRequest.h30
-rw-r--r--Swiften/Queries/IQChannel.cpp8
-rw-r--r--Swiften/Queries/IQChannel.h22
-rw-r--r--Swiften/Queries/IQHandler.cpp14
-rw-r--r--Swiften/Queries/IQHandler.h28
-rw-r--r--Swiften/Queries/IQRouter.cpp46
-rw-r--r--Swiften/Queries/IQRouter.h33
-rw-r--r--Swiften/Queries/Makefile.inc8
-rw-r--r--Swiften/Queries/Request.cpp39
-rw-r--r--Swiften/Queries/Request.h46
-rw-r--r--Swiften/Queries/Requests/GetDiscoInfoRequest.h16
-rw-r--r--Swiften/Queries/Requests/GetRosterRequest.h16
-rw-r--r--Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h20
-rw-r--r--Swiften/Queries/Responder.h49
-rw-r--r--Swiften/Queries/Responders/DiscoInfoResponder.cpp40
-rw-r--r--Swiften/Queries/Responders/DiscoInfoResponder.h29
-rw-r--r--Swiften/Queries/Responders/Makefile.inc5
-rw-r--r--Swiften/Queries/Responders/SoftwareVersionResponder.cpp20
-rw-r--r--Swiften/Queries/Responders/SoftwareVersionResponder.h24
-rw-r--r--Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp84
-rw-r--r--Swiften/Queries/Responders/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Queries/UnitTest/Makefile.inc3
-rw-r--r--Swiften/Queries/UnitTest/RequestTest.cpp139
-rw-r--r--Swiften/Queries/UnitTest/ResponderTest.cpp132
-rw-r--r--Swiften/Roster/ContactRosterItem.cpp51
-rw-r--r--Swiften/Roster/ContactRosterItem.h39
-rw-r--r--Swiften/Roster/GroupRosterItem.h70
-rw-r--r--Swiften/Roster/Makefile.inc6
-rw-r--r--Swiften/Roster/OfflineRosterFilter.h24
-rw-r--r--Swiften/Roster/OpenChatRosterAction.h20
-rw-r--r--Swiften/Roster/Roster.cpp127
-rw-r--r--Swiften/Roster/Roster.h48
-rw-r--r--Swiften/Roster/RosterFilter.h17
-rw-r--r--Swiften/Roster/RosterItem.h24
-rw-r--r--Swiften/Roster/RosterItemOperation.h16
-rw-r--r--Swiften/Roster/SetPresence.h36
-rw-r--r--Swiften/Roster/TreeWidget.h13
-rw-r--r--Swiften/Roster/TreeWidgetFactory.h20
-rw-r--r--Swiften/Roster/TreeWidgetItem.h30
-rw-r--r--Swiften/Roster/UnitTest/Makefile.inc4
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidget.h14
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidgetFactory.h31
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidgetItem.h26
-rw-r--r--Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp0
-rw-r--r--Swiften/Roster/UnitTest/RosterTest.cpp57
-rw-r--r--Swiften/Roster/UserRosterAction.h32
-rw-r--r--Swiften/Roster/XMPPRoster.cpp37
-rw-r--r--Swiften/Roster/XMPPRoster.h34
-rw-r--r--Swiften/SASL/Makefile.inc4
-rw-r--r--Swiften/SASL/PLAINMessage.cpp10
-rw-r--r--Swiften/SASL/PLAINMessage.h22
-rw-r--r--Swiften/SASL/UnitTest/Makefile.inc2
-rw-r--r--Swiften/SASL/UnitTest/PLAINMessageTest.cpp29
-rw-r--r--Swiften/Serializer/AuthFailureSerializer.h22
-rw-r--r--Swiften/Serializer/AuthRequestSerializer.cpp17
-rw-r--r--Swiften/Serializer/AuthRequestSerializer.h18
-rw-r--r--Swiften/Serializer/AuthSuccessSerializer.h22
-rw-r--r--Swiften/Serializer/CompressFailureSerializer.h22
-rw-r--r--Swiften/Serializer/CompressRequestSerializer.cpp19
-rw-r--r--Swiften/Serializer/CompressRequestSerializer.h18
-rw-r--r--Swiften/Serializer/ElementSerializer.cpp8
-rw-r--r--Swiften/Serializer/ElementSerializer.h19
-rw-r--r--Swiften/Serializer/GenericElementSerializer.h18
-rw-r--r--Swiften/Serializer/GenericPayloadSerializer.h25
-rw-r--r--Swiften/Serializer/GenericStanzaSerializer.h29
-rw-r--r--Swiften/Serializer/IQSerializer.h30
-rw-r--r--Swiften/Serializer/Makefile.inc15
-rw-r--r--Swiften/Serializer/MessageSerializer.cpp27
-rw-r--r--Swiften/Serializer/MessageSerializer.h21
-rw-r--r--Swiften/Serializer/PayloadSerializer.cpp8
-rw-r--r--Swiften/Serializer/PayloadSerializer.h19
-rw-r--r--Swiften/Serializer/PayloadSerializerCollection.cpp27
-rw-r--r--Swiften/Serializer/PayloadSerializerCollection.h26
-rw-r--r--Swiften/Serializer/PayloadSerializers/BodySerializer.h20
-rw-r--r--Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.cpp20
-rw-r--r--Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.cpp36
-rw-r--r--Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/ErrorSerializer.cpp56
-rw-r--r--Swiften/Serializer/PayloadSerializers/ErrorSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp49
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h19
-rw-r--r--Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp13
-rw-r--r--Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/Makefile.inc13
-rw-r--r--Swiften/Serializer/PayloadSerializers/PrioritySerializer.h20
-rw-r--r--Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.cpp28
-rw-r--r--Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp45
-rw-r--r--Swiften/Serializer/PayloadSerializers/RosterSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.cpp38
-rw-r--r--Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp30
-rw-r--r--Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp23
-rw-r--r--Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h20
-rw-r--r--Swiften/Serializer/PayloadSerializers/StatusSerializer.h22
-rw-r--r--Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h33
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp25
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp38
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp25
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/Makefile.inc12
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp25
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp49
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp48
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp43
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp52
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp25
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp25
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp58
-rw-r--r--Swiften/Serializer/PresenceSerializer.cpp27
-rw-r--r--Swiften/Serializer/PresenceSerializer.h21
-rw-r--r--Swiften/Serializer/StanzaSerializer.cpp50
-rw-r--r--Swiften/Serializer/StanzaSerializer.h25
-rw-r--r--Swiften/Serializer/StartTLSFailureSerializer.h22
-rw-r--r--Swiften/Serializer/StartTLSRequestSerializer.h22
-rw-r--r--Swiften/Serializer/StreamFeaturesSerializer.cpp46
-rw-r--r--Swiften/Serializer/StreamFeaturesSerializer.h18
-rw-r--r--Swiften/Serializer/TLSProceedSerializer.h22
-rw-r--r--Swiften/Serializer/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp46
-rw-r--r--Swiften/Serializer/XML/Makefile.inc5
-rw-r--r--Swiften/Serializer/XML/UnitTest/Makefile.inc2
-rw-r--r--Swiften/Serializer/XML/UnitTest/XMLElementTest.cpp62
-rw-r--r--Swiften/Serializer/XML/XMLElement.cpp49
-rw-r--r--Swiften/Serializer/XML/XMLElement.h27
-rw-r--r--Swiften/Serializer/XML/XMLNode.cpp8
-rw-r--r--Swiften/Serializer/XML/XMLNode.h15
-rw-r--r--Swiften/Serializer/XML/XMLRawTextNode.h21
-rw-r--r--Swiften/Serializer/XML/XMLTextNode.h23
-rw-r--r--Swiften/Serializer/XMPPSerializer.cpp58
-rw-r--r--Swiften/Serializer/XMPPSerializer.h28
-rw-r--r--Swiften/Settings/SettingsProvider.h18
-rw-r--r--Swiften/StreamStack/CompressionLayer.h48
-rw-r--r--Swiften/StreamStack/ConnectionLayer.h25
-rw-r--r--Swiften/StreamStack/HighLayer.cpp8
-rw-r--r--Swiften/StreamStack/HighLayer.h19
-rw-r--r--Swiften/StreamStack/LowLayer.cpp8
-rw-r--r--Swiften/StreamStack/LowLayer.h19
-rw-r--r--Swiften/StreamStack/Makefile.inc15
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.cpp28
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.h27
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.cpp34
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.h16
-rw-r--r--Swiften/StreamStack/StreamLayer.h16
-rw-r--r--Swiften/StreamStack/StreamStack.cpp29
-rw-r--r--Swiften/StreamStack/StreamStack.h45
-rw-r--r--Swiften/StreamStack/TLSLayer.h19
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.cpp8
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.h16
-rw-r--r--Swiften/StreamStack/UnitTest/Makefile.inc3
-rw-r--r--Swiften/StreamStack/UnitTest/StreamStackTest.cpp167
-rw-r--r--Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp116
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.cpp30
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.h27
-rw-r--r--Swiften/StreamStack/XMPPLayer.cpp79
-rw-r--r--Swiften/StreamStack/XMPPLayer.h57
-rw-r--r--Swiften/StringCodecs/Base64.cpp109
-rw-r--r--Swiften/StringCodecs/Base64.h18
-rw-r--r--Swiften/StringCodecs/Makefile.inc5
-rw-r--r--Swiften/StringCodecs/SHA1.cpp196
-rw-r--r--Swiften/StringCodecs/SHA1.h13
-rw-r--r--Swiften/StringCodecs/UnitTest/Base64Test.cpp35
-rw-r--r--Swiften/StringCodecs/UnitTest/Makefile.inc3
-rw-r--r--Swiften/StringCodecs/UnitTest/SHA1Test.cpp23
-rw-r--r--Swiften/Swift.pri322
-rw-r--r--Swiften/TLS/Makefile.inc3
-rw-r--r--Swiften/TLS/OpenSSL/Makefile.inc2
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp159
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h43
-rw-r--r--Swiften/TLS/PKCS12Certificate.h37
483 files changed, 17831 insertions, 0 deletions
diff --git a/Swiften/Application/Application.cpp b/Swiften/Application/Application.cpp
new file mode 100644
index 0000000..52fd14c
--- /dev/null
+++ b/Swiften/Application/Application.cpp
@@ -0,0 +1,27 @@
+#include "Swiften/Application/Application.h"
+
+#include <boost/filesystem.hpp>
+#include <stdlib.h>
+
+#include "Swiften/Application/ApplicationMessageDisplay.h"
+
+namespace Swift {
+
+Application::Application(const String& name) : name_(name) {
+}
+
+Application::~Application() {
+}
+
+boost::filesystem::path Application::getSettingsFileName() const {
+ return getSettingsDir() / "settings";
+}
+
+boost::filesystem::path Application::getHomeDir() const {
+ // FIXME: Does this work on windows?
+ char* homeDirRaw = getenv("HOME");
+ boost::filesystem::path homeDir(homeDirRaw);
+ return homeDir;
+}
+
+}
diff --git a/Swiften/Application/Application.h b/Swiften/Application/Application.h
new file mode 100644
index 0000000..4900107
--- /dev/null
+++ b/Swiften/Application/Application.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_Application_H
+#define SWIFTEN_Application_H
+
+#include <boost/filesystem.hpp>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class ApplicationMessageDisplay;
+
+ class Application {
+ public:
+ Application(const String& name);
+ virtual ~Application();
+
+ boost::filesystem::path getSettingsFileName() const;
+ boost::filesystem::path getHomeDir() const;
+ virtual boost::filesystem::path getSettingsDir() const = 0;
+
+ const String& getName() const {
+ return name_;
+ }
+
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay() = 0;
+
+ private:
+ String name_;
+ };
+}
+
+#endif
diff --git a/Swiften/Application/ApplicationMessageDisplay.cpp b/Swiften/Application/ApplicationMessageDisplay.cpp
new file mode 100644
index 0000000..48db37d
--- /dev/null
+++ b/Swiften/Application/ApplicationMessageDisplay.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Application/ApplicationMessageDisplay.h"
+
+namespace Swift {
+
+ApplicationMessageDisplay::~ApplicationMessageDisplay() {
+}
+
+}
diff --git a/Swiften/Application/ApplicationMessageDisplay.h b/Swiften/Application/ApplicationMessageDisplay.h
new file mode 100644
index 0000000..df7f0cb
--- /dev/null
+++ b/Swiften/Application/ApplicationMessageDisplay.h
@@ -0,0 +1,15 @@
+#ifndef SWIFTEN_ApplicationMessageDisplay_H
+#define SWIFTEN_ApplicationMessageDisplay_H
+
+namespace Swift {
+ class String;
+
+ class ApplicationMessageDisplay {
+ public:
+ virtual ~ApplicationMessageDisplay();
+
+ virtual void setMessage(const String& message) = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Application/MacOSX/MacOSXApplication.cpp b/Swiften/Application/MacOSX/MacOSXApplication.cpp
new file mode 100644
index 0000000..1df2bfa
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplication.cpp
@@ -0,0 +1,18 @@
+#include "Swiften/Application/MacOSX/MacOSXApplication.h"
+
+namespace Swift {
+
+MacOSXApplication::MacOSXApplication(const String& name) : Application(name) {
+}
+
+ApplicationMessageDisplay* MacOSXApplication::getApplicationMessageDisplay() {
+ return &messageDisplay_;
+}
+
+boost::filesystem::path MacOSXApplication::getSettingsDir() const {
+ boost::filesystem::path result(getHomeDir() / "Library/Application Support" / getName().getUTF8String());
+ boost::filesystem::create_directory(result);
+ return result;
+}
+
+}
diff --git a/Swiften/Application/MacOSX/MacOSXApplication.h b/Swiften/Application/MacOSX/MacOSXApplication.h
new file mode 100644
index 0000000..9e77c54
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplication.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_MacOSXApplication_H
+#define SWIFTEN_MacOSXApplication_H
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h"
+
+namespace Swift {
+ class ApplicationMessageDisplay;
+
+ class MacOSXApplication : public Application {
+ public:
+ MacOSXApplication(const String& name);
+
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay();
+ boost::filesystem::path getSettingsDir() const;
+
+ private:
+ MacOSXApplicationMessageDisplay messageDisplay_;
+ };
+}
+
+#endif
diff --git a/Swiften/Application/MacOSX/MacOSXApplicationInitializer.h b/Swiften/Application/MacOSX/MacOSXApplicationInitializer.h
new file mode 100644
index 0000000..db551eb
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplicationInitializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_MacOSXApplicationInitializer_H
+#define SWIFTEN_MacOSXApplicationInitializer_H
+
+namespace Swift {
+ class MacOSXApplicationInitializer {
+ public:
+ MacOSXApplicationInitializer();
+ ~MacOSXApplicationInitializer();
+
+ private:
+ class Private;
+ Private* d;
+ };
+}
+
+#endif
diff --git a/Swiften/Application/MacOSX/MacOSXApplicationInitializer.mm b/Swiften/Application/MacOSX/MacOSXApplicationInitializer.mm
new file mode 100644
index 0000000..e401697
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplicationInitializer.mm
@@ -0,0 +1,24 @@
+#include "Swiften/Application/MacOSX/MacOSXApplicationInitializer.h"
+
+#include <AppKit/AppKit.h>
+#include <Cocoa/Cocoa.h>
+
+namespace Swift {
+
+class MacOSXApplicationInitializer::Private {
+ public:
+ NSAutoreleasePool* autoReleasePool_;
+};
+
+MacOSXApplicationInitializer::MacOSXApplicationInitializer() {
+ d = new MacOSXApplicationInitializer::Private();
+ NSApplicationLoad();
+ d->autoReleasePool_ = [[NSAutoreleasePool alloc] init];
+}
+
+MacOSXApplicationInitializer::~MacOSXApplicationInitializer() {
+ [d->autoReleasePool_ release];
+ delete d;
+}
+
+}
diff --git a/Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h b/Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h
new file mode 100644
index 0000000..98af1fa
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h
@@ -0,0 +1,17 @@
+#ifndef SWIFTEN_MacOSXApplicationMessageDisplay_H
+#define SWIFTEN_MacOSXApplicationMessageDisplay_H
+
+#include "Swiften/Application/ApplicationMessageDisplay.h"
+
+namespace Swift {
+ class String;
+
+ class MacOSXApplicationMessageDisplay : public ApplicationMessageDisplay {
+ public:
+ MacOSXApplicationMessageDisplay();
+
+ void setMessage(const String& label);
+ };
+}
+
+#endif
diff --git a/Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.mm b/Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.mm
new file mode 100644
index 0000000..c10c707
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.mm
@@ -0,0 +1,20 @@
+#include "Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h"
+
+#include <AppKit/AppKit.h>
+#include <Cocoa/Cocoa.h>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+
+MacOSXApplicationMessageDisplay::MacOSXApplicationMessageDisplay() {
+}
+
+void MacOSXApplicationMessageDisplay::setMessage(const String& label) {
+ NSString *labelString = [[NSString alloc] initWithUTF8String: label.getUTF8Data()];
+ [[NSApp dockTile] setBadgeLabel: labelString];
+ [labelString release];
+ [NSApp requestUserAttention: NSInformationalRequest];
+}
+
+}
diff --git a/Swiften/Application/MacOSX/Makefile.inc b/Swiften/Application/MacOSX/Makefile.inc
new file mode 100644
index 0000000..9443267
--- /dev/null
+++ b/Swiften/Application/MacOSX/Makefile.inc
@@ -0,0 +1,6 @@
+SWIFTEN_OBJECTIVE_SOURCES += \
+ Swiften/Application/MacOSX/MacOSXApplicationInitializer.mm \
+ Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.mm
+
+SWIFTEN_SOURCES += \
+ Swiften/Application/MacOSX/MacOSXApplication.cpp
diff --git a/Swiften/Application/Makefile.inc b/Swiften/Application/Makefile.inc
new file mode 100644
index 0000000..7cc7fcb
--- /dev/null
+++ b/Swiften/Application/Makefile.inc
@@ -0,0 +1,9 @@
+SWIFTEN_SOURCES += \
+ Swiften/Application/Application.cpp \
+ Swiften/Application/ApplicationMessageDisplay.cpp
+
+ifeq ($(MACOSX),1)
+include Swiften/Application/MacOSX/Makefile.inc
+endif
+
+include Swiften/Application/UnitTest/Makefile.inc
diff --git a/Swiften/Application/NullApplicationMessageDisplay.h b/Swiften/Application/NullApplicationMessageDisplay.h
new file mode 100644
index 0000000..03e0b42
--- /dev/null
+++ b/Swiften/Application/NullApplicationMessageDisplay.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_NullApplicationMessageDisplay_H
+#define SWIFTEN_NullApplicationMessageDisplay_H
+
+#include "Swiften/Application/ApplicationMessageDisplay.h"
+
+namespace Swift {
+ class NullApplicationMessageDisplay : public ApplicationMessageDisplay {
+ public:
+ NullApplicationMessageDisplay() {}
+
+ virtual void setMessage(const String&) {
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Application/Platform/PlatformApplication.h b/Swiften/Application/Platform/PlatformApplication.h
new file mode 100644
index 0000000..749bce4
--- /dev/null
+++ b/Swiften/Application/Platform/PlatformApplication.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_PlatformApplication_H
+#define SWIFTEN_PlatformApplication_H
+
+#include "Swiften/Base/Platform.h"
+
+
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+#include "Swiften/Application/MacOSX/MacOSXApplication.h"
+namespace Swift {
+ typedef MacOSXApplication PlatformApplication;
+}
+#elif defined(SWIFTEN_PLATFORM_WIN32)
+#include "Swiften/Application/Windows/WindowsApplication.h"
+namespace Swift {
+ typedef WindowsApplication PlatformApplication;
+}
+#else
+#include "Swiften/Application/Unix/UnixApplication.h"
+namespace Swift {
+ typedef UnixApplication PlatformApplication;
+}
+#endif
+
+#endif
diff --git a/Swiften/Application/UnitTest/ApplicationTest.cpp b/Swiften/Application/UnitTest/ApplicationTest.cpp
new file mode 100644
index 0000000..e13c94b
--- /dev/null
+++ b/Swiften/Application/UnitTest/ApplicationTest.cpp
@@ -0,0 +1,39 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/Platform/PlatformApplication.h"
+
+using namespace Swift;
+
+class ApplicationTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ApplicationTest);
+ CPPUNIT_TEST(testGetSettingsDir);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ApplicationTest() {}
+
+ void setUp() {
+ testling_ = new PlatformApplication("SwiftTest");
+ }
+
+ void tearDown() {
+ delete testling_;
+ }
+
+ void testGetSettingsDir() {
+ boost::filesystem::path dir = testling_->getSettingsDir();
+
+ CPPUNIT_ASSERT(boost::filesystem::exists(dir));
+ CPPUNIT_ASSERT(boost::filesystem::is_directory(dir));
+
+ boost::filesystem::remove(dir);
+ }
+
+ private:
+ Application* testling_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ApplicationTest);
diff --git a/Swiften/Application/UnitTest/Makefile.inc b/Swiften/Application/UnitTest/Makefile.inc
new file mode 100644
index 0000000..47c8691
--- /dev/null
+++ b/Swiften/Application/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+ Swiften/Application/UnitTest/ApplicationTest.cpp
diff --git a/Swiften/Application/Unix/UnixApplication.h b/Swiften/Application/Unix/UnixApplication.h
new file mode 100644
index 0000000..8cf6feb
--- /dev/null
+++ b/Swiften/Application/Unix/UnixApplication.h
@@ -0,0 +1,29 @@
+#ifndef SWIFTEN_UnixApplication_H
+#define SWIFTEN_UnixApplication_H
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/NullApplicationMessageDisplay.h"
+
+namespace Swift {
+ class UnixApplication : public Application {
+ public:
+ UnixApplication(const String& name) : Application(name) {
+ }
+
+ private:
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay() {
+ return &messageDisplay_;
+ }
+
+ boost::filesystem::path getSettingsDir() const {
+ boost::filesystem::path result(getHomeDir() / ("." + getName().getUTF8String()));
+ boost::filesystem::create_directory(result);
+ return result;
+ }
+
+ private:
+ NullApplicationMessageDisplay messageDisplay_;
+ };
+}
+
+#endif
diff --git a/Swiften/Application/Windows/WindowsApplication.h b/Swiften/Application/Windows/WindowsApplication.h
new file mode 100644
index 0000000..0fc4b12
--- /dev/null
+++ b/Swiften/Application/Windows/WindowsApplication.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_WindowsApplication_H
+#define SWIFTEN_WindowsApplication_H
+
+#include <cassert>
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/NullApplicationMessageDisplay.h"
+
+namespace Swift {
+ class WindowsApplication : public Application {
+ public:
+ WindowsApplication(const String& name) : Application(name) {
+ }
+
+ protected:
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay() {
+ return &messageDisplay_;
+ }
+
+ boost::filesystem::path getSettingsDir() const {
+ assert(false);
+ // FIXME: This is wrong
+ boost::filesystem::path result(getHomeDir() / ("." + getName().getUTF8String()));
+ boost::filesystem::create_directory(result);
+ return result;
+ }
+
+ private:
+ NullApplicationMessageDisplay messageDisplay_;
+ };
+}
+
+#endif
diff --git a/Swiften/Base/ByteArray.cpp b/Swiften/Base/ByteArray.cpp
new file mode 100644
index 0000000..b3a5d8d
--- /dev/null
+++ b/Swiften/Base/ByteArray.cpp
@@ -0,0 +1,34 @@
+#include "Swiften/Base/ByteArray.h"
+
+#include <fstream>
+
+std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s) {
+ std::ios::fmtflags oldFlags = os.flags();
+ os << std::hex;
+ for (Swift::ByteArray::const_iterator i = s.begin(); i != s.end(); ++i) {
+ os << "0x" << static_cast<unsigned int>(static_cast<unsigned char>(*i));
+ if (i + 1 < s.end()) {
+ os << " ";
+ }
+ }
+ os << std::endl;
+ os.flags(oldFlags);
+ return os;
+}
+
+namespace Swift {
+
+static const int BUFFER_SIZE = 4096;
+
+void ByteArray::readFromFile(const String& file) {
+ std::ifstream input(file.getUTF8Data(), std::ios_base::in|std::ios_base::binary);
+ while (input.good()) {
+ size_t oldSize = data_.size();
+ data_.resize(oldSize + BUFFER_SIZE);
+ input.read(&data_[oldSize], BUFFER_SIZE);
+ data_.resize(oldSize + input.gcount());
+ }
+ input.close();
+}
+
+}
diff --git a/Swiften/Base/ByteArray.h b/Swiften/Base/ByteArray.h
new file mode 100644
index 0000000..88e3fae
--- /dev/null
+++ b/Swiften/Base/ByteArray.h
@@ -0,0 +1,92 @@
+#ifndef SWIFTEN_BYTEARRAY_H
+#define SWIFTEN_BYTEARRAY_H
+
+#include <cstring>
+#include <vector>
+#include <iostream>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class ByteArray
+ {
+ public:
+ typedef std::vector<char>::const_iterator const_iterator;
+
+ ByteArray() : data_() {}
+
+ ByteArray(const String& s) : data_(s.getUTF8String().begin(), s.getUTF8String().end()) {}
+
+ ByteArray(const char* c) {
+ while (*c) {
+ data_.push_back(*c);
+ ++c;
+ }
+ }
+
+ ByteArray(const char* c, size_t n) {
+ data_.resize(n);
+ memcpy(&data_[0], c, n);
+ }
+
+ const char* getData() const {
+ return &data_[0];
+ }
+
+ char* getData() {
+ return &data_[0];
+ }
+
+ size_t getSize() const {
+ return data_.size();
+ }
+
+ bool isEmpty() const {
+ return data_.empty();
+ }
+
+ void resize(size_t size) {
+ return data_.resize(size);
+ }
+
+ friend ByteArray operator+(const ByteArray& a, const ByteArray&b) {
+ ByteArray result(a);
+ result.data_.insert(result.data_.end(), b.data_.begin(), b.data_.end());
+ return result;
+ }
+
+ friend bool operator==(const ByteArray& a, const ByteArray& b) {
+ return a.data_ == b.data_;
+ }
+
+
+ const char& operator[](size_t i) const {
+ return data_[i];
+ }
+
+ char& operator[](size_t i) {
+ return data_[i];
+ }
+
+ const_iterator begin() const {
+ return data_.begin();
+ }
+
+ const_iterator end() const {
+ return data_.end();
+ }
+
+ String toString() const {
+ return String(getData(), getSize());
+ }
+
+ void readFromFile(const String& file);
+
+ private:
+ std::vector<char> data_;
+ };
+}
+
+std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s);
+
+#endif
diff --git a/Swiften/Base/IDGenerator.cpp b/Swiften/Base/IDGenerator.cpp
new file mode 100644
index 0000000..07ead43
--- /dev/null
+++ b/Swiften/Base/IDGenerator.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Base/IDGenerator.h"
+
+namespace Swift {
+
+IDGenerator::IDGenerator() {
+}
+
+String IDGenerator::generateID() {
+ bool carry = true;
+ size_t i = 0;
+ while (carry && i < currentID_.getUTF8Size()) {
+ char c = currentID_.getUTF8String()[i];
+ if (c >= 'z') {
+ currentID_.getUTF8String()[i] = 'a';
+ }
+ else {
+ currentID_.getUTF8String()[i] = c+1;
+ carry = false;
+ }
+ ++i;
+ }
+ if (carry) {
+ currentID_ += 'a';
+ }
+ return currentID_;
+}
+
+}
diff --git a/Swiften/Base/IDGenerator.h b/Swiften/Base/IDGenerator.h
new file mode 100644
index 0000000..db7b80d
--- /dev/null
+++ b/Swiften/Base/IDGenerator.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_IDGenerator_H
+#define SWIFTEN_IDGenerator_H
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class IDGenerator {
+ public:
+ IDGenerator();
+
+ String generateID();
+
+ private:
+ String currentID_;
+ };
+}
+
+#endif
diff --git a/Swiften/Base/Makefile.inc b/Swiften/Base/Makefile.inc
new file mode 100644
index 0000000..cf42910
--- /dev/null
+++ b/Swiften/Base/Makefile.inc
@@ -0,0 +1,7 @@
+SWIFTEN_SOURCES += \
+ Swiften/Base/String.cpp \
+ Swiften/Base/ByteArray.cpp \
+ Swiften/Base/IDGenerator.cpp \
+ Swiften/Base/sleep.cpp
+
+include Swiften/Base/UnitTest/Makefile.inc
diff --git a/Swiften/Base/Platform.h b/Swiften/Base/Platform.h
new file mode 100644
index 0000000..9e4c398
--- /dev/null
+++ b/Swiften/Base/Platform.h
@@ -0,0 +1,44 @@
+#ifndef SWIFTEN_Platform_H
+#define SWIFTEN_Platform_H
+
+// Base platforms
+#if defined(linux) || defined(__linux) || defined(__linux__)
+#define SWIFTEN_PLATFORM_LINUX
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+#define SWIFTEN_PLATFORM_BSD
+#elif defined(sun) || defined(__sun)
+#define SWIFTEN_PLATFORM_SOLARIS
+#elif defined(__sgi)
+#define SWIFTEN_PLATFORM_SGI
+#elif defined(__hpux)
+#define SWIFTEN_PLATFORM_HPUX
+#elif defined(__CYGWIN__)
+#define SWIFTEN_PLATFORM_CYGWIN
+#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+#define SWIFTEN_PLATFORM_WIN32
+#elif defined(__BEOS__)
+#define SWIFTEN_PLATFORM_BEOS
+#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
+#define SWIFTEN_PLATFORM_MACOSX
+#elif defined(__IBMCPP__) || defined(_AIX)
+#define SWIFTEN_PLATFORM_AIX
+#elif defined(__amigaos__)
+#define SWIFTEN_PLATFORM_AMIGAOS
+#elif defined(__QNXNTO__)
+#define SWIFTEN_PLATFORM_QNX
+#endif
+
+// Derived platforms
+#if defined(SWIFTEN_PLATFORM_CYGWIN) || defined(SWIFTEN_PLATFORM_WIN32)
+#define SWIFTEN_PLATFORM_WINDOWS
+#endif
+
+// Endianness
+#include <boost/detail/endian.hpp>
+#if defined(BOOST_LITTLE_ENDIAN)
+#define SWIFTEN_LITTLE_ENDIAN
+#elif defined(BOOST_BIG_ENDIAN)
+#define SWIFTEN_BIG_ENDIAN
+#endif
+
+#endif
diff --git a/Swiften/Base/String.cpp b/Swiften/Base/String.cpp
new file mode 100644
index 0000000..88692b7
--- /dev/null
+++ b/Swiften/Base/String.cpp
@@ -0,0 +1,93 @@
+#include <cassert>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+
+static inline size_t sequenceLength(char firstByte) {
+ if ((firstByte & 0x80) == 0) {
+ return 1;
+ }
+ if ((firstByte & 0xE0) == 0xC0) {
+ return 2;
+ }
+ if ((firstByte & 0xF0) == 0xE0) {
+ return 3;
+ }
+ if ((firstByte & 0xF8) == 0xF0) {
+ return 4;
+ }
+ if ((firstByte & 0xFC) == 0xF8) {
+ return 5;
+ }
+ if ((firstByte & 0xFE) == 0xFC) {
+ return 6;
+ }
+ assert(false);
+ return 1;
+}
+
+std::vector<unsigned int> String::getUnicodeCodePoints() const {
+ std::vector<unsigned int> result;
+ for (size_t i = 0; i < data_.size();) {
+ unsigned int codePoint = 0;
+ char firstChar = data_[i];
+ size_t length = sequenceLength(firstChar);
+
+ // First character is special
+ size_t firstCharBitSize = 7 - length;
+ if (length == 1) {
+ firstCharBitSize = 7;
+ }
+ codePoint = firstChar & ((1<<(firstCharBitSize+1)) - 1);
+
+ for (size_t j = 1; j < length; ++j) {
+ codePoint = (codePoint<<6) | (data_[i+j] & 0x3F);
+ }
+ result.push_back(codePoint);
+ i += length;
+ }
+ return result;
+}
+
+
+std::pair<String,String> String::getSplittedAtFirst(char c) const {
+ assert((c & 0x80) == 0);
+ size_t firstMatch = data_.find(c);
+ if (firstMatch != data_.npos) {
+ return std::make_pair(data_.substr(0,firstMatch),data_.substr(firstMatch+1,data_.npos));
+ }
+ else {
+ return std::make_pair(*this, "");
+ }
+}
+
+size_t String::getLength() const {
+ size_t size = 0, current = 0, end = data_.size();
+ while (current < end) {
+ size++;
+ current += sequenceLength(data_[current]);
+ }
+ return size;
+}
+
+void String::removeAll(char c) {
+ size_t lastPos = 0;
+ size_t matchingIndex = 0;
+ while ((matchingIndex = data_.find(c, lastPos)) != data_.npos) {
+ data_.erase(matchingIndex, 1);
+ lastPos = matchingIndex;
+ }
+}
+
+void String::replaceAll(char c, const String& s) {
+ size_t lastPos = 0;
+ size_t matchingIndex = 0;
+ while ((matchingIndex = data_.find(c, lastPos)) != data_.npos) {
+ data_.replace(matchingIndex, 1, s.data_);
+ lastPos = matchingIndex + s.data_.size();
+ }
+}
+
+
+}
diff --git a/Swiften/Base/String.h b/Swiften/Base/String.h
new file mode 100644
index 0000000..0bc79bf
--- /dev/null
+++ b/Swiften/Base/String.h
@@ -0,0 +1,115 @@
+#ifndef SWIFTEN_STRING_H
+#define SWIFTEN_STRING_H
+
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+#include <cassert>
+
+#define SWIFTEN_STRING_TO_CFSTRING(a) \
+ CFStringCreateWithBytes(NULL, reinterpret_cast<const UInt8*>(a.getUTF8Data()), a.getUTF8Size(), kCFStringEncodingUTF8, false)
+
+namespace Swift {
+ class ByteArray;
+
+ class String {
+ friend class ByteArray;
+
+ public:
+ String() {}
+ String(const char* data) : data_(data) {}
+ String(const char* data, size_t len) : data_(data, len) {}
+ String(const std::string& data) : data_(data) {}
+
+ bool isEmpty() const { return data_.empty(); }
+
+ const char* getUTF8Data() const { return data_.c_str(); }
+ const std::string& getUTF8String() const { return data_; }
+ std::string& getUTF8String() { return data_; }
+ size_t getUTF8Size() const { return data_.size(); }
+ std::vector<unsigned int> getUnicodeCodePoints() const;
+
+ /**
+ * Returns the part before and after 'c'.
+ * If the given splitter does not occur in the string, the second
+ * component is the empty string.
+ */
+ std::pair<String,String> getSplittedAtFirst(char c) const;
+
+ size_t getLength() const;
+
+ void removeAll(char c);
+
+ void replaceAll(char c, const String& s);
+
+ bool beginsWith(char c) const {
+ return data_.size() > 0 && data_[0] == c;
+ }
+
+ bool beginsWith(const String& s) const {
+ return data_.substr(0, s.data_.size()) == s;
+ }
+
+ bool endsWith(char c) const {
+ return data_.size() > 0 && data_[data_.size()-1] == c;
+ }
+
+ String getSubstring(size_t begin, size_t end) const {
+ return String(data_.substr(begin, end));
+ }
+
+ size_t find(char c) const {
+ assert((c & 0x80) == 0);
+ return data_.find(c);
+ }
+
+ size_t npos() const {
+ return data_.npos;
+ }
+
+ friend String operator+(const String& a, const String& b) {
+ return String(a.data_ + b.data_);
+ }
+
+ friend String operator+(const String& a, char b) {
+ return String(a.data_ + b);
+ }
+
+ String& operator+=(const String& o) {
+ data_ += o.data_;
+ return *this;
+ }
+
+ String& operator+=(char c) {
+ data_ += c;
+ return *this;
+ }
+
+ friend bool operator>(const String& a, const String& b) {
+ return a.data_ > b.data_;
+ }
+
+ friend bool operator<(const String& a, const String& b) {
+ return a.data_ < b.data_;
+ }
+
+ friend bool operator!=(const String& a, const String& b) {
+ return a.data_ != b.data_;
+ }
+
+ friend bool operator==(const String& a, const String& b) {
+ return a.data_ == b.data_;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const String& s) {
+ os << s.data_;
+ return os;
+ }
+
+ private:
+ std::string data_;
+ };
+}
+
+#endif
diff --git a/Swiften/Base/UnitTest/IDGeneratorTest.cpp b/Swiften/Base/UnitTest/IDGeneratorTest.cpp
new file mode 100644
index 0000000..bd96d91
--- /dev/null
+++ b/Swiften/Base/UnitTest/IDGeneratorTest.cpp
@@ -0,0 +1,34 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <set>
+
+#include "Swiften/Base/IDGenerator.h"
+
+using namespace Swift;
+
+class IDGeneratorTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(IDGeneratorTest);
+ CPPUNIT_TEST(testGenerate);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ IDGeneratorTest() {}
+
+ void setUp() {
+ generatedIDs_.clear();
+ }
+
+ void testGenerate() {
+ IDGenerator testling;
+ for (unsigned int i = 0; i < 26*4; ++i) {
+ String id = testling.generateID();
+ CPPUNIT_ASSERT(generatedIDs_.insert(id).second);
+ }
+ }
+
+ private:
+ std::set<String> generatedIDs_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IDGeneratorTest);
diff --git a/Swiften/Base/UnitTest/Makefile.inc b/Swiften/Base/UnitTest/Makefile.inc
new file mode 100644
index 0000000..f724330
--- /dev/null
+++ b/Swiften/Base/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/Base/UnitTest/StringTest.cpp \
+ Swiften/Base/UnitTest/IDGeneratorTest.cpp
diff --git a/Swiften/Base/UnitTest/StringTest.cpp b/Swiften/Base/UnitTest/StringTest.cpp
new file mode 100644
index 0000000..0b7d207
--- /dev/null
+++ b/Swiften/Base/UnitTest/StringTest.cpp
@@ -0,0 +1,146 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/String.h"
+
+using namespace Swift;
+
+class StringTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StringTest);
+ CPPUNIT_TEST(testGetLength);
+ CPPUNIT_TEST(testGetLength_EncodedLength2);
+ CPPUNIT_TEST(testGetLength_EncodedLength3);
+ CPPUNIT_TEST(testGetLength_EncodedLength4);
+ CPPUNIT_TEST(testGetUnicodeCodePoints);
+ CPPUNIT_TEST(testGetSplittedAtFirst);
+ CPPUNIT_TEST(testGetSplittedAtFirst_CharacterAtEnd);
+ CPPUNIT_TEST(testGetSplittedAtFirst_NoSuchCharacter);
+ CPPUNIT_TEST(testRemoveAll);
+ CPPUNIT_TEST(testRemoveAll_LastChar);
+ CPPUNIT_TEST(testRemoveAll_ConsecutiveChars);
+ CPPUNIT_TEST(testReplaceAll);
+ CPPUNIT_TEST(testReplaceAll_LastChar);
+ CPPUNIT_TEST(testReplaceAll_ConsecutiveChars);
+ CPPUNIT_TEST(testReplaceAll_MatchingReplace);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StringTest() {}
+
+ void testGetLength() {
+ String testling("xyz$xyz");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(7), testling.getLength());
+ }
+
+ void testGetLength_EncodedLength2() {
+ String testling("xyz\xC2\xA2xyz");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(7), testling.getLength());
+ }
+
+ void testGetLength_EncodedLength3() {
+ String testling("xyz\xE2\x82\xACxyz");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(7), testling.getLength());
+ }
+
+ void testGetLength_EncodedLength4() {
+ String testling("xyz\xf4\x8a\xaf\x8dxyz");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(7), testling.getLength());
+ }
+
+ void testGetUnicodeCodePoints() {
+ String testling("$\xc2\xa2\xe2\x82\xac\xf4\x8a\xaf\x8d");
+ std::vector<unsigned int> points = testling.getUnicodeCodePoints();
+
+ CPPUNIT_ASSERT_EQUAL(0x24U, points[0]);
+ CPPUNIT_ASSERT_EQUAL(0xA2U, points[1]);
+ CPPUNIT_ASSERT_EQUAL(0x20ACU, points[2]);
+ CPPUNIT_ASSERT_EQUAL(0x10ABCDU, points[3]);
+ }
+
+ void testGetSplittedAtFirst() {
+ String testling("ab@cd@ef");
+
+ std::pair<String,String> result = testling.getSplittedAtFirst('@');
+ CPPUNIT_ASSERT_EQUAL(String("ab"), result.first);
+ CPPUNIT_ASSERT_EQUAL(String("cd@ef"), result.second);
+ }
+
+ void testGetSplittedAtFirst_CharacterAtEnd() {
+ String testling("ab@");
+
+ std::pair<String,String> result = testling.getSplittedAtFirst('@');
+ CPPUNIT_ASSERT_EQUAL(String("ab"), result.first);
+ CPPUNIT_ASSERT(result.second.isEmpty());
+ }
+
+ void testGetSplittedAtFirst_NoSuchCharacter() {
+ String testling("ab");
+
+ std::pair<String,String> result = testling.getSplittedAtFirst('@');
+ CPPUNIT_ASSERT_EQUAL(String("ab"), result.first);
+ CPPUNIT_ASSERT(result.second.isEmpty());
+ }
+
+ void testRemoveAll() {
+ String testling("ab c de");
+
+ testling.removeAll(' ');
+
+ CPPUNIT_ASSERT_EQUAL(String("abcde"), testling);
+ }
+
+ void testRemoveAll_LastChar() {
+ String testling("abcde ");
+
+ testling.removeAll(' ');
+
+ CPPUNIT_ASSERT_EQUAL(String("abcde"), testling);
+ }
+
+ void testRemoveAll_ConsecutiveChars() {
+ String testling("ab cde");
+
+ testling.removeAll(' ');
+
+ CPPUNIT_ASSERT_EQUAL(String("abcde"), testling);
+ }
+
+ void testReplaceAll() {
+ String testling("abcbd");
+
+ testling.replaceAll('b', "xyz");
+
+ CPPUNIT_ASSERT_EQUAL(String("axyzcxyzd"), testling);
+ }
+
+ void testReplaceAll_LastChar() {
+ String testling("abc");
+
+ testling.replaceAll('c', "xyz");
+
+ CPPUNIT_ASSERT_EQUAL(String("abxyz"), testling);
+ }
+
+ void testReplaceAll_ConsecutiveChars() {
+ String testling("abbc");
+
+ testling.replaceAll('b',"xyz");
+
+ CPPUNIT_ASSERT_EQUAL(String("axyzxyzc"), testling);
+ }
+
+ void testReplaceAll_MatchingReplace() {
+ String testling("abc");
+
+ testling.replaceAll('b',"bbb");
+
+ CPPUNIT_ASSERT_EQUAL(String("abbbc"), testling);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StringTest);
diff --git a/Swiften/Base/foreach.h b/Swiften/Base/foreach.h
new file mode 100644
index 0000000..b2bee66
--- /dev/null
+++ b/Swiften/Base/foreach.h
@@ -0,0 +1,9 @@
+#ifndef SWIFTEN_FOREACH_H
+#define SWIFTEN_FOREACH_H
+
+#include <boost/foreach.hpp>
+
+#undef foreach
+#define foreach BOOST_FOREACH
+
+#endif
diff --git a/Swiften/Base/sleep.cpp b/Swiften/Base/sleep.cpp
new file mode 100644
index 0000000..cecfd72
--- /dev/null
+++ b/Swiften/Base/sleep.cpp
@@ -0,0 +1,14 @@
+#include "Swiften/Base/sleep.h"
+
+#include <boost/thread.hpp>
+
+namespace Swift {
+
+void sleep(unsigned int msecs) {
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.nsec += msecs*1000000;
+ boost::thread::sleep(xt);
+}
+
+}
diff --git a/Swiften/Base/sleep.h b/Swiften/Base/sleep.h
new file mode 100644
index 0000000..b2a4ef1
--- /dev/null
+++ b/Swiften/Base/sleep.h
@@ -0,0 +1,8 @@
+#ifndef SWIFTEN_sleep_H
+#define SWIFTEN_sleep_H
+
+namespace Swift {
+ void sleep(unsigned int msecs);
+}
+
+#endif
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
new file mode 100644
index 0000000..e5bbf9d
--- /dev/null
+++ b/Swiften/Client/Client.cpp
@@ -0,0 +1,147 @@
+#include "Swiften/Client/Client.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/Session.h"
+#include "Swiften/StreamStack/PlatformTLSLayerFactory.h"
+#include "Swiften/Network/BoostConnectionFactory.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+namespace Swift {
+
+Client::Client(const JID& jid, const String& password) :
+ IQRouter(this), jid_(jid), password_(password), session_(0) {
+ connectionFactory_ = new BoostConnectionFactory();
+ tlsLayerFactory_ = new PlatformTLSLayerFactory();
+}
+
+Client::~Client() {
+ delete session_;
+ delete tlsLayerFactory_;
+ delete connectionFactory_;
+}
+
+void Client::connect() {
+ delete session_;
+ session_ = new Session(jid_, connectionFactory_, tlsLayerFactory_, &payloadParserFactories_, &payloadSerializers_);
+ if (!certificate_.isEmpty()) {
+ session_->setCertificate(PKCS12Certificate(certificate_, password_));
+ }
+ session_->onSessionStarted.connect(boost::bind(boost::ref(onConnected)));
+ session_->onError.connect(boost::bind(&Client::handleSessionError, this, _1));
+ session_->onNeedCredentials.connect(boost::bind(&Client::handleNeedCredentials, this));
+ session_->onDataRead.connect(boost::bind(&Client::handleDataRead, this, _1));
+ session_->onDataWritten.connect(boost::bind(&Client::handleDataWritten, this, _1));
+ session_->onElementReceived.connect(boost::bind(&Client::handleElement, this, _1));
+ session_->start();
+}
+
+void Client::disconnect() {
+ if (session_) {
+ session_->stop();
+ }
+}
+
+void Client::send(boost::shared_ptr<Stanza> stanza) {
+ session_->sendElement(stanza);
+}
+
+void Client::sendIQ(boost::shared_ptr<IQ> iq) {
+ send(iq);
+}
+
+void Client::sendMessage(boost::shared_ptr<Message> message) {
+ send(message);
+}
+
+void Client::sendPresence(boost::shared_ptr<Presence> presence) {
+ send(presence);
+}
+
+String Client::getNewIQID() {
+ return idGenerator_.generateID();
+}
+
+void Client::handleElement(boost::shared_ptr<Element> element) {
+ boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(element);
+ if (message) {
+ onMessageReceived(message);
+ return;
+ }
+
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(element);
+ if (presence) {
+ onPresenceReceived(presence);
+ return;
+ }
+
+ boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(element);
+ if (iq) {
+ onIQReceived(iq);
+ return;
+ }
+}
+
+void Client::setCertificate(const String& certificate) {
+ certificate_ = certificate;
+}
+
+void Client::handleSessionError(Session::SessionError error) {
+ ClientError clientError;
+ switch (error) {
+ case Session::NoError:
+ assert(false);
+ break;
+ case Session::DomainNameResolveError:
+ clientError = ClientError(ClientError::DomainNameResolveError);
+ break;
+ case Session::ConnectionError:
+ clientError = ClientError(ClientError::ConnectionError);
+ break;
+ case Session::ConnectionReadError:
+ clientError = ClientError(ClientError::ConnectionReadError);
+ break;
+ case Session::XMLError:
+ clientError = ClientError(ClientError::XMLError);
+ break;
+ case Session::AuthenticationFailedError:
+ clientError = ClientError(ClientError::AuthenticationFailedError);
+ break;
+ case Session::NoSupportedAuthMechanismsError:
+ clientError = ClientError(ClientError::NoSupportedAuthMechanismsError);
+ break;
+ case Session::UnexpectedElementError:
+ clientError = ClientError(ClientError::UnexpectedElementError);
+ break;
+ case Session::ResourceBindError:
+ clientError = ClientError(ClientError::ResourceBindError);
+ break;
+ case Session::SessionStartError:
+ clientError = ClientError(ClientError::SessionStartError);
+ break;
+ case Session::TLSError:
+ clientError = ClientError(ClientError::TLSError);
+ break;
+ case Session::ClientCertificateLoadError:
+ clientError = ClientError(ClientError::ClientCertificateLoadError);
+ break;
+ case Session::ClientCertificateError:
+ clientError = ClientError(ClientError::ClientCertificateError);
+ break;
+ }
+ onError(clientError);
+}
+
+void Client::handleNeedCredentials() {
+ session_->sendCredentials(password_);
+}
+
+void Client::handleDataRead(const ByteArray& data) {
+ onDataRead(String(data.getData(), data.getSize()));
+}
+
+void Client::handleDataWritten(const ByteArray& data) {
+ onDataWritten(String(data.getData(), data.getSize()));
+}
+
+}
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
new file mode 100644
index 0000000..946bdbd
--- /dev/null
+++ b/Swiften/Client/Client.h
@@ -0,0 +1,66 @@
+#ifndef SWIFTEN_Client_H
+#define SWIFTEN_Client_H
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Client/Session.h"
+#include "Swiften/Client/ClientError.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/IDGenerator.h"
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+namespace Swift {
+ class TLSLayerFactory;
+ class ConnectionFactory;
+ class Session;
+
+ class Client : public StanzaChannel, public IQRouter {
+ public:
+ Client(const JID& jid, const String& password);
+ ~Client();
+
+ void setCertificate(const String& certificate);
+
+ void connect();
+ void disconnect();
+
+ virtual void sendIQ(boost::shared_ptr<IQ>);
+ virtual void sendMessage(boost::shared_ptr<Message>);
+ virtual void sendPresence(boost::shared_ptr<Presence>);
+
+ public:
+ boost::signal<void (ClientError)> onError;
+ boost::signal<void ()> onConnected;
+ boost::signal<void (const String&)> onDataRead;
+ boost::signal<void (const String&)> onDataWritten;
+
+ private:
+ void send(boost::shared_ptr<Stanza>);
+ virtual String getNewIQID();
+ void handleElement(boost::shared_ptr<Element>);
+ void handleSessionError(Session::SessionError error);
+ void handleNeedCredentials();
+ void handleDataRead(const ByteArray&);
+ void handleDataWritten(const ByteArray&);
+
+ private:
+ JID jid_;
+ String password_;
+ IDGenerator idGenerator_;
+ ConnectionFactory* connectionFactory_;
+ TLSLayerFactory* tlsLayerFactory_;
+ FullPayloadParserFactoryCollection payloadParserFactories_;
+ FullPayloadSerializerCollection payloadSerializers_;
+ Session* session_;
+ String certificate_;
+ };
+}
+
+#endif
diff --git a/Swiften/Client/ClientError.h b/Swiften/Client/ClientError.h
new file mode 100644
index 0000000..38f20c0
--- /dev/null
+++ b/Swiften/Client/ClientError.h
@@ -0,0 +1,32 @@
+#ifndef SWIFTEN_ClientError_H
+#define SWIFTEN_ClientError_H
+
+namespace Swift {
+ class ClientError {
+ public:
+ enum Type {
+ NoError,
+ DomainNameResolveError,
+ ConnectionError,
+ ConnectionReadError,
+ XMLError,
+ AuthenticationFailedError,
+ NoSupportedAuthMechanismsError,
+ UnexpectedElementError,
+ ResourceBindError,
+ SessionStartError,
+ TLSError,
+ ClientCertificateLoadError,
+ ClientCertificateError
+ };
+
+ ClientError(Type type = NoError) : type_(type) {}
+
+ Type getType() const { return type_; }
+
+ private:
+ Type type_;
+ };
+}
+
+#endif
diff --git a/Swiften/Client/Makefile.inc b/Swiften/Client/Makefile.inc
new file mode 100644
index 0000000..75eb08f
--- /dev/null
+++ b/Swiften/Client/Makefile.inc
@@ -0,0 +1,5 @@
+SWIFTEN_SOURCES += \
+ Swiften/Client/Client.cpp \
+ Swiften/Client/Session.cpp
+
+include Swiften/Client/UnitTest/Makefile.inc
diff --git a/Swiften/Client/Session.cpp b/Swiften/Client/Session.cpp
new file mode 100644
index 0000000..aa3cc62
--- /dev/null
+++ b/Swiften/Client/Session.cpp
@@ -0,0 +1,292 @@
+#include "Swiften/Client/Session.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/StreamStack/StreamStack.h"
+#include "Swiften/StreamStack/ConnectionLayer.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/TLSLayer.h"
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/StartTLSRequest.h"
+#include "Swiften/Elements/StartTLSFailure.h"
+#include "Swiften/Elements/TLSProceed.h"
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Elements/AuthFailure.h"
+#include "Swiften/Elements/StartSession.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/ResourceBind.h"
+#include "Swiften/SASL/PLAINMessage.h"
+#include "Swiften/StreamStack/WhitespacePingLayer.h"
+
+namespace Swift {
+
+Session::Session(const JID& jid, ConnectionFactory* connectionFactory, TLSLayerFactory* tlsLayerFactory, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers) :
+ jid_(jid),
+ connectionFactory_(connectionFactory),
+ tlsLayerFactory_(tlsLayerFactory),
+ payloadParserFactories_(payloadParserFactories),
+ payloadSerializers_(payloadSerializers),
+ state_(Initial),
+ error_(NoError),
+ connection_(0),
+ xmppLayer_(0),
+ tlsLayer_(0),
+ connectionLayer_(0),
+ whitespacePingLayer_(0),
+ streamStack_(0),
+ needSessionStart_(false) {
+}
+
+Session::~Session() {
+ delete streamStack_;
+ delete whitespacePingLayer_;
+ delete connectionLayer_;
+ delete tlsLayer_;
+ delete xmppLayer_;
+ delete connection_;
+}
+
+void Session::start() {
+ assert(state_ == Initial);
+ state_ = Connecting;
+ connection_ = connectionFactory_->createConnection(jid_.getDomain());
+ connection_->onConnected.connect(boost::bind(&Session::handleConnected, this));
+ connection_->onError.connect(boost::bind(&Session::handleConnectionError, this, _1));
+ connection_->connect();
+}
+
+void Session::stop() {
+ // TODO: Send end stream header if applicable
+ connection_->disconnect();
+}
+
+void Session::handleConnected() {
+ assert(state_ == Connecting);
+ initializeStreamStack();
+ state_ = WaitingForStreamStart;
+ sendStreamHeader();
+}
+
+void Session::sendStreamHeader() {
+ xmppLayer_->writeHeader(jid_.getDomain());
+}
+
+void Session::initializeStreamStack() {
+ xmppLayer_ = new XMPPLayer(payloadParserFactories_, payloadSerializers_);
+ xmppLayer_->onStreamStart.connect(boost::bind(&Session::handleStreamStart, this));
+ xmppLayer_->onElement.connect(boost::bind(&Session::handleElement, this, _1));
+ xmppLayer_->onError.connect(boost::bind(&Session::setError, this, XMLError));
+ xmppLayer_->onDataRead.connect(boost::bind(boost::ref(onDataRead), _1));
+ xmppLayer_->onWriteData.connect(boost::bind(boost::ref(onDataWritten), _1));
+ connectionLayer_ = new ConnectionLayer(connection_);
+ streamStack_ = new StreamStack(xmppLayer_, connectionLayer_);
+}
+
+void Session::handleConnectionError(Connection::Error error) {
+ switch (error) {
+ case Connection::DomainNameResolveError:
+ setError(DomainNameResolveError);
+ break;
+ case Connection::ReadError:
+ setError(ConnectionReadError);
+ break;
+ case Connection::ConnectionError:
+ setError(ConnectionError);
+ break;
+ }
+}
+
+void Session::setCertificate(const PKCS12Certificate& certificate) {
+ certificate_ = certificate;
+}
+
+void Session::handleStreamStart() {
+ checkState(WaitingForStreamStart);
+ state_ = Negotiating;
+}
+
+void Session::handleElement(boost::shared_ptr<Element> element) {
+ if (getState() == SessionStarted) {
+ onElementReceived(element);
+ }
+ else {
+ StreamFeatures* streamFeatures = dynamic_cast<StreamFeatures*>(element.get());
+ if (streamFeatures) {
+ if (!checkState(Negotiating)) {
+ return;
+ }
+
+ if (streamFeatures->hasStartTLS() && tlsLayerFactory_->canCreate()) {
+ state_ = Encrypting;
+ xmppLayer_->writeElement(boost::shared_ptr<StartTLSRequest>(new StartTLSRequest()));
+ }
+ else if (streamFeatures->hasAuthenticationMechanisms()) {
+ if (!certificate_.isNull()) {
+ if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) {
+ state_ = Authenticating;
+ xmppLayer_->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", "")));
+ }
+ else {
+ setError(ClientCertificateError);
+ }
+ }
+ else if (streamFeatures->hasAuthenticationMechanism("PLAIN")) {
+ state_ = WaitingForCredentials;
+ onNeedCredentials();
+ }
+ else {
+ setError(NoSupportedAuthMechanismsError);
+ }
+ }
+ else {
+ // Start the session
+
+ // Add a whitespace ping layer
+ whitespacePingLayer_ = new WhitespacePingLayer();
+ streamStack_->addLayer(whitespacePingLayer_);
+
+ if (streamFeatures->hasSession()) {
+ needSessionStart_ = true;
+ }
+
+ if (streamFeatures->hasResourceBind()) {
+ state_ = BindingResource;
+ boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind());
+ if (!jid_.getResource().isEmpty()) {
+ resourceBind->setResource(jid_.getResource());
+ }
+ xmppLayer_->writeElement(IQ::createRequest(IQ::Set, JID(), "session-bind", resourceBind));
+ }
+ else if (needSessionStart_) {
+ sendSessionStart();
+ }
+ else {
+ state_ = SessionStarted;
+ onSessionStarted();
+ }
+ }
+ }
+ else {
+ AuthSuccess* authSuccess = dynamic_cast<AuthSuccess*>(element.get());
+ if (authSuccess) {
+ checkState(Authenticating);
+ state_ = WaitingForStreamStart;
+ xmppLayer_->resetParser();
+ sendStreamHeader();
+ }
+ else if (dynamic_cast<AuthFailure*>(element.get())) {
+ setError(AuthenticationFailedError);
+ }
+ else if (dynamic_cast<TLSProceed*>(element.get())) {
+ tlsLayer_ = tlsLayerFactory_->createTLSLayer();
+ streamStack_->addLayer(tlsLayer_);
+ if (!certificate_.isNull() && !tlsLayer_->setClientCertificate(certificate_)) {
+ setError(ClientCertificateLoadError);
+ }
+ else {
+ tlsLayer_->onConnected.connect(boost::bind(&Session::handleTLSConnected, this));
+ tlsLayer_->onError.connect(boost::bind(&Session::handleTLSError, this));
+ tlsLayer_->connect();
+ }
+ }
+ else if (dynamic_cast<StartTLSFailure*>(element.get())) {
+ setError(TLSError);
+ }
+ else {
+ IQ* iq = dynamic_cast<IQ*>(element.get());
+ if (iq) {
+ if (state_ == BindingResource) {
+ boost::shared_ptr<ResourceBind> resourceBind(iq->getPayload<ResourceBind>());
+ if (iq->getType() == IQ::Error && iq->getID() == "session-bind") {
+ setError(ResourceBindError);
+ }
+ else if (!resourceBind) {
+ setError(UnexpectedElementError);
+ }
+ else if (iq->getType() == IQ::Result) {
+ jid_ = resourceBind->getJID();
+ if (!jid_.isValid()) {
+ setError(ResourceBindError);
+ }
+ if (needSessionStart_) {
+ sendSessionStart();
+ }
+ else {
+ state_ = SessionStarted;
+ }
+ }
+ else {
+ setError(UnexpectedElementError);
+ }
+ }
+ else if (state_ == StartingSession) {
+ if (iq->getType() == IQ::Result) {
+ state_ = SessionStarted;
+ onSessionStarted();
+ }
+ else if (iq->getType() == IQ::Error) {
+ setError(SessionStartError);
+ }
+ else {
+ setError(UnexpectedElementError);
+ }
+ }
+ else {
+ setError(UnexpectedElementError);
+ }
+ }
+ else {
+ // FIXME Not correct?
+ state_ = SessionStarted;
+ onSessionStarted();
+ }
+ }
+ }
+ }
+}
+
+void Session::sendSessionStart() {
+ state_ = StartingSession;
+ xmppLayer_->writeElement(IQ::createRequest(IQ::Set, JID(), "session-start", boost::shared_ptr<StartSession>(new StartSession())));
+}
+
+void Session::setError(SessionError error) {
+ assert(error != NoError);
+ state_ = Error;
+ error_ = error;
+ onError(error);
+}
+
+bool Session::checkState(State state) {
+ if (state_ != state) {
+ setError(UnexpectedElementError);
+ return false;
+ }
+ return true;
+}
+
+void Session::sendCredentials(const String& password) {
+ assert(WaitingForCredentials);
+ state_ = Authenticating;
+ xmppLayer_->writeElement(boost::shared_ptr<Element>(new AuthRequest("PLAIN", PLAINMessage(jid_.getNode(), password).getValue())));
+}
+
+void Session::sendElement(boost::shared_ptr<Element> element) {
+ assert(SessionStarted);
+ xmppLayer_->writeElement(element);
+}
+
+void Session::handleTLSConnected() {
+ state_ = WaitingForStreamStart;
+ xmppLayer_->resetParser();
+ sendStreamHeader();
+}
+
+void Session::handleTLSError() {
+ setError(TLSError);
+}
+
+}
diff --git a/Swiften/Client/Session.h b/Swiften/Client/Session.h
new file mode 100644
index 0000000..c49d877
--- /dev/null
+++ b/Swiften/Client/Session.h
@@ -0,0 +1,126 @@
+#ifndef SWIFTEN_Session_H
+#define SWIFTEN_Session_H
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+namespace Swift {
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+ class ConnectionFactory;
+ class Connection;
+ class StreamStack;
+ class XMPPLayer;
+ class ConnectionLayer;
+ class TLSLayerFactory;
+ class TLSLayer;
+ class WhitespacePingLayer;
+
+ class Session {
+ public:
+ enum State {
+ Initial,
+ Connecting,
+ WaitingForStreamStart,
+ Negotiating,
+ Compressing,
+ Encrypting,
+ WaitingForCredentials,
+ Authenticating,
+ BindingResource,
+ StartingSession,
+ SessionStarted,
+ Error
+ };
+ enum SessionError {
+ NoError,
+ DomainNameResolveError,
+ ConnectionError,
+ ConnectionReadError,
+ XMLError,
+ AuthenticationFailedError,
+ NoSupportedAuthMechanismsError,
+ UnexpectedElementError,
+ ResourceBindError,
+ SessionStartError,
+ TLSError,
+ ClientCertificateLoadError,
+ ClientCertificateError
+ };
+
+ Session(const JID& jid, ConnectionFactory*, TLSLayerFactory*, PayloadParserFactoryCollection*, PayloadSerializerCollection*);
+ ~Session();
+
+ State getState() const {
+ return state_;
+ }
+
+ SessionError getError() const {
+ return error_;
+ }
+
+ const JID& getJID() const {
+ return jid_;
+ }
+
+ void start();
+ void stop();
+ void sendCredentials(const String& password);
+ void sendElement(boost::shared_ptr<Element>);
+ void setCertificate(const PKCS12Certificate& certificate);
+
+ protected:
+ StreamStack* getStreamStack() const {
+ return streamStack_;
+ }
+
+ private:
+ void initializeStreamStack();
+ void sendStreamHeader();
+ void sendSessionStart();
+
+ void handleConnected();
+ void handleConnectionError(Connection::Error);
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamStart();
+ void handleTLSConnected();
+ void handleTLSError();
+
+ void setError(SessionError);
+ bool checkState(State);
+
+ public:
+ boost::signal<void ()> onSessionStarted;
+ boost::signal<void (SessionError)> onError;
+ boost::signal<void ()> onNeedCredentials;
+ boost::signal<void (boost::shared_ptr<Element>) > onElementReceived;
+ boost::signal<void (const ByteArray&)> onDataWritten;
+ boost::signal<void (const ByteArray&)> onDataRead;
+
+ private:
+ JID jid_;
+ ConnectionFactory* connectionFactory_;
+ TLSLayerFactory* tlsLayerFactory_;
+ PayloadParserFactoryCollection* payloadParserFactories_;
+ PayloadSerializerCollection* payloadSerializers_;
+ State state_;
+ SessionError error_;
+ Connection* connection_;
+ XMPPLayer* xmppLayer_;
+ TLSLayer* tlsLayer_;
+ ConnectionLayer* connectionLayer_;
+ WhitespacePingLayer* whitespacePingLayer_;
+ StreamStack* streamStack_;
+ bool needSessionStart_;
+ PKCS12Certificate certificate_;
+ };
+
+}
+
+#endif
diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h
new file mode 100644
index 0000000..719ed10
--- /dev/null
+++ b/Swiften/Client/StanzaChannel.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_MessageChannel_H
+#define SWIFTEN_MessageChannel_H
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Queries/IQChannel.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+ class StanzaChannel : public IQChannel {
+ public:
+ virtual void sendMessage(boost::shared_ptr<Message>) = 0;
+ virtual void sendPresence(boost::shared_ptr<Presence>) = 0;
+
+ boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;
+ boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived;
+ };
+}
+
+#endif
diff --git a/Swiften/Client/UnitTest/Makefile.inc b/Swiften/Client/UnitTest/Makefile.inc
new file mode 100644
index 0000000..3ef87e5
--- /dev/null
+++ b/Swiften/Client/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+ Swiften/Client/UnitTest/SessionTest.cpp
diff --git a/Swiften/Client/UnitTest/SessionTest.cpp b/Swiften/Client/UnitTest/SessionTest.cpp
new file mode 100644
index 0000000..7b7a916
--- /dev/null
+++ b/Swiften/Client/UnitTest/SessionTest.cpp
@@ -0,0 +1,752 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Parser/XMPPParser.h"
+#include "Swiften/Parser/XMPPParserClient.h"
+#include "Swiften/Serializer/XMPPSerializer.h"
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+#include "Swiften/StreamStack/TLSLayer.h"
+#include "Swiften/StreamStack/StreamStack.h"
+#include "Swiften/StreamStack/WhitespacePingLayer.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Elements/Error.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Elements/AuthFailure.h"
+#include "Swiften/Elements/ResourceBind.h"
+#include "Swiften/Elements/StartSession.h"
+#include "Swiften/Elements/StartTLSRequest.h"
+#include "Swiften/Elements/StartTLSFailure.h"
+#include "Swiften/Elements/TLSProceed.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/Client/Session.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+using namespace Swift;
+
+class SessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SessionTest);
+ CPPUNIT_TEST(testConstructor);
+ CPPUNIT_TEST(testConnect);
+ CPPUNIT_TEST(testConnect_Error);
+ CPPUNIT_TEST(testConnect_ErrorAfterSuccesfulConnect);
+ CPPUNIT_TEST(testConnect_XMLError);
+ CPPUNIT_TEST(testStartTLS);
+ CPPUNIT_TEST(testStartTLS_ServerError);
+ CPPUNIT_TEST(testStartTLS_NoTLSSupport);
+ CPPUNIT_TEST(testStartTLS_ConnectError);
+ CPPUNIT_TEST(testStartTLS_ErrorAfterConnect);
+ CPPUNIT_TEST(testAuthenticate);
+ CPPUNIT_TEST(testAuthenticate_Unauthorized);
+ CPPUNIT_TEST(testAuthenticate_NoValidAuthMechanisms);
+ CPPUNIT_TEST(testResourceBind);
+ CPPUNIT_TEST(testResourceBind_ChangeResource);
+ CPPUNIT_TEST(testResourceBind_EmptyResource);
+ CPPUNIT_TEST(testResourceBind_Error);
+ CPPUNIT_TEST(testSessionStart);
+ CPPUNIT_TEST(testSessionStart_Error);
+ CPPUNIT_TEST(testSessionStart_AfterResourceBind);
+ CPPUNIT_TEST(testWhitespacePing);
+ CPPUNIT_TEST(testReceiveElementAfterSessionStarted);
+ CPPUNIT_TEST(testSendElement);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SessionTest() {}
+
+ void setUp() {
+ eventLoop_ = new DummyEventLoop();
+ connectionFactory_ = new MockConnectionFactory();
+ tlsLayerFactory_ = new MockTLSLayerFactory();
+ sessionStarted_ = false;
+ needCredentials_ = false;
+ }
+
+ void tearDown() {
+ delete tlsLayerFactory_;
+ delete connectionFactory_;
+ delete eventLoop_;
+ }
+
+ void testConstructor() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ CPPUNIT_ASSERT_EQUAL(Session::Initial, session->getState());
+ }
+
+ void testConnect() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+
+ session->start();
+ CPPUNIT_ASSERT_EQUAL(Session::Connecting, session->getState());
+
+ getMockServer()->expectStreamStart();
+
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::WaitingForStreamStart, session->getState());
+ }
+
+ void testConnect_Error() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onSessionStarted.connect(boost::bind(&SessionTest::setSessionStarted, this));
+
+ connectionFactory_->setCreateFailingConnections();
+ session->start();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT(!sessionStarted_);
+ CPPUNIT_ASSERT_EQUAL(Session::ConnectionError, session->getError());
+ }
+
+ void testConnect_ErrorAfterSuccesfulConnect() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+
+ session->start();
+ getMockServer()->expectStreamStart();
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::WaitingForStreamStart, session->getState());
+
+ connectionFactory_->connections_[0]->setError();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::ConnectionError, session->getError());
+ }
+
+ void testConnect_XMLError() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+
+ session->start();
+ getMockServer()->expectStreamStart();
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::WaitingForStreamStart, session->getState());
+
+ getMockServer()->sendInvalidXML();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::XMLError, session->getError());
+ }
+
+ void testStartTLS_NoTLSSupport() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ tlsLayerFactory_->setTLSSupported(false);
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithStartTLS();
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStarted, session->getState());
+ }
+
+ void testStartTLS() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithStartTLS();
+ getMockServer()->expectStartTLS();
+ // FIXME: Test 'encrypting' state
+ getMockServer()->sendTLSProceed();
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::Encrypting, session->getState());
+ CPPUNIT_ASSERT(session->getTLSLayer());
+ CPPUNIT_ASSERT(session->getTLSLayer()->isConnecting());
+
+ getMockServer()->resetParser();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ session->getTLSLayer()->setConnected();
+ // FIXME: Test 'WatingForStreamStart' state
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::Negotiating, session->getState());
+ }
+
+ void testStartTLS_ServerError() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithStartTLS();
+ getMockServer()->expectStartTLS();
+ getMockServer()->sendTLSFailure();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::TLSError, session->getError());
+ }
+
+ void testStartTLS_ConnectError() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithStartTLS();
+ getMockServer()->expectStartTLS();
+ getMockServer()->sendTLSProceed();
+ processEvents();
+ session->getTLSLayer()->setError();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::TLSError, session->getError());
+ }
+
+ void testStartTLS_ErrorAfterConnect() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithStartTLS();
+ getMockServer()->expectStartTLS();
+ getMockServer()->sendTLSProceed();
+ processEvents();
+ getMockServer()->resetParser();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ session->getTLSLayer()->setConnected();
+ processEvents();
+
+ session->getTLSLayer()->setError();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::TLSError, session->getError());
+ }
+
+ void testAuthenticate() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onNeedCredentials.connect(boost::bind(&SessionTest::setNeedCredentials, this));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithAuthentication();
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::WaitingForCredentials, session->getState());
+ CPPUNIT_ASSERT(needCredentials_);
+
+ getMockServer()->expectAuth("me", "mypass");
+ getMockServer()->sendAuthSuccess();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ session->sendCredentials("mypass");
+ CPPUNIT_ASSERT_EQUAL(Session::Authenticating, session->getState());
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(Session::Negotiating, session->getState());
+ }
+
+ void testAuthenticate_Unauthorized() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithAuthentication();
+ processEvents();
+
+ getMockServer()->expectAuth("me", "mypass");
+ getMockServer()->sendAuthFailure();
+ session->sendCredentials("mypass");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::AuthenticationFailedError, session->getError());
+ }
+
+ void testAuthenticate_NoValidAuthMechanisms() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithUnsupportedAuthentication();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::NoSupportedAuthMechanismsError, session->getError());
+ }
+
+ void testResourceBind() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("Bar", "session-bind");
+ // FIXME: Check CPPUNIT_ASSERT_EQUAL(Session::BindingResource, session->getState());
+ getMockServer()->sendResourceBindResponse("me@foo.com/Bar", "session-bind");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStarted, session->getState());
+ CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/Bar"), session->getJID());
+ }
+
+ void testResourceBind_ChangeResource() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("Bar", "session-bind");
+ getMockServer()->sendResourceBindResponse("me@foo.com/Bar123", "session-bind");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStarted, session->getState());
+ CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/Bar123"), session->getJID());
+ }
+
+ void testResourceBind_EmptyResource() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("", "session-bind");
+ getMockServer()->sendResourceBindResponse("me@foo.com/NewResource", "session-bind");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStarted, session->getState());
+ CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/NewResource"), session->getJID());
+ }
+
+ void testResourceBind_Error() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("", "session-bind");
+ getMockServer()->sendError("session-bind");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::ResourceBindError, session->getError());
+ }
+
+ void testSessionStart() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onSessionStarted.connect(boost::bind(&SessionTest::setSessionStarted, this));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithSession();
+ getMockServer()->expectSessionStart("session-start");
+ // FIXME: Check CPPUNIT_ASSERT_EQUAL(Session::StartingSession, session->getState());
+ getMockServer()->sendSessionStartResponse("session-start");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStarted, session->getState());
+ CPPUNIT_ASSERT(sessionStarted_);
+ }
+
+ void testSessionStart_Error() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithSession();
+ getMockServer()->expectSessionStart("session-start");
+ getMockServer()->sendError("session-start");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStartError, session->getError());
+ }
+
+ void testSessionStart_AfterResourceBind() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onSessionStarted.connect(boost::bind(&SessionTest::setSessionStarted, this));
+ session->start();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBindAndSession();
+ getMockServer()->expectResourceBind("Bar", "session-bind");
+ getMockServer()->sendResourceBindResponse("me@foo.com/Bar", "session-bind");
+ getMockServer()->expectSessionStart("session-start");
+ getMockServer()->sendSessionStartResponse("session-start");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(Session::SessionStarted, session->getState());
+ CPPUNIT_ASSERT(sessionStarted_);
+ }
+
+ void testWhitespacePing() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeatures();
+ processEvents();
+ CPPUNIT_ASSERT(session->getWhitespacePingLayer());
+ }
+
+ void testReceiveElementAfterSessionStarted() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->start();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeatures();
+ processEvents();
+
+ getMockServer()->expectMessage();
+ session->sendElement(boost::shared_ptr<Message>(new Message()));
+ }
+
+ void testSendElement() {
+ std::auto_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onElementReceived.connect(boost::bind(&SessionTest::addReceivedElement, this, _1));
+ session->start();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeatures();
+ getMockServer()->sendMessage();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(receivedElements_.size()));
+ CPPUNIT_ASSERT(boost::dynamic_pointer_cast<Message>(receivedElements_[0]));
+ }
+
+ private:
+ struct MockConnection;
+
+ MockConnection* getMockServer() const {
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connectionFactory_->connections_.size()));
+ return connectionFactory_->connections_[0];
+ }
+
+ void processEvents() {
+ eventLoop_->processEvents();
+ getMockServer()->assertNoMoreExpectations();
+ }
+
+ void setSessionStarted() {
+ sessionStarted_ = true;
+ }
+
+ void setNeedCredentials() {
+ needCredentials_ = true;
+ }
+
+ void addReceivedElement(boost::shared_ptr<Element> element) {
+ receivedElements_.push_back(element);
+ }
+
+ private:
+ struct MockConnection : public Connection, public XMPPParserClient {
+ struct Event {
+ enum Direction { In, Out };
+ enum Type { StreamStartEvent, StreamEndEvent, ElementEvent };
+
+ Event(
+ Direction direction,
+ Type type,
+ boost::shared_ptr<Element> element = boost::shared_ptr<Element>()) :
+ direction(direction), type(type), element(element) {}
+
+ Direction direction;
+ Type type;
+ boost::shared_ptr<Element> element;
+ };
+
+ MockConnection(const String& domain, bool fail) :
+ Connection(domain),
+ fail_(fail),
+ resetParser_(false),
+ parser_(0),
+ serializer_(&payloadSerializers_) {
+ parser_ = new XMPPParser(this, &payloadParserFactories_);
+ }
+
+ ~MockConnection() {
+ delete parser_;
+ }
+
+ void disconnect() {
+ }
+
+ void connect() {
+ if (fail_) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError), Connection::ConnectionError));
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onConnected)));
+ }
+ }
+
+ void setError() {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError), Connection::ConnectionError));
+ }
+
+ void write(const ByteArray& data) {
+ CPPUNIT_ASSERT(parser_->parse(data.toString()));
+ if (resetParser_) {
+ resetParser();
+ resetParser_ = false;
+ }
+ }
+
+ void resetParser() {
+ delete parser_;
+ parser_ = new XMPPParser(this, &payloadParserFactories_);
+ }
+
+ void handleStreamStart() {
+ handleEvent(Event::StreamStartEvent);
+ }
+
+ void handleElement(boost::shared_ptr<Swift::Element> element) {
+ handleEvent(Event::ElementEvent, element);
+ }
+
+ void handleStreamEnd() {
+ handleEvent(Event::StreamEndEvent);
+ }
+
+ void handleEvent(Event::Type type, boost::shared_ptr<Element> element = boost::shared_ptr<Element>()) {
+ CPPUNIT_ASSERT(!events_.empty());
+ CPPUNIT_ASSERT_EQUAL(events_[0].direction, Event::In);
+ CPPUNIT_ASSERT_EQUAL(events_[0].type, type);
+ if (type == Event::ElementEvent) {
+ CPPUNIT_ASSERT_EQUAL(serializer_.serializeElement(events_[0].element), serializer_.serializeElement(element));
+ }
+ events_.pop_front();
+
+ while (!events_.empty() && events_[0].direction == Event::Out) {
+ sendData(serializeEvent(events_[0]));
+ events_.pop_front();
+ }
+
+ if (!events_.empty() && events_[0].type == Event::StreamStartEvent) {
+ resetParser_ = true;
+ }
+ }
+
+ String serializeEvent(const Event& event) {
+ switch (event.type) {
+ case Event::StreamStartEvent:
+ return serializer_.serializeHeader(getDomain());
+ case Event::ElementEvent:
+ return serializer_.serializeElement(event.element);
+ case Event::StreamEndEvent:
+ return serializer_.serializeFooter();
+ }
+ assert(false);
+ }
+
+ void assertNoMoreExpectations() {
+ CPPUNIT_ASSERT(events_.empty());
+ }
+
+ void sendData(const ByteArray& data) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDataRead), data));
+ }
+
+ void expectStreamStart() {
+ events_.push_back(Event(Event::In, Event::StreamStartEvent));
+ }
+
+ void expectStartTLS() {
+ events_.push_back(Event(Event::In, Event::ElementEvent, boost::shared_ptr<StartTLSRequest>(new StartTLSRequest())));
+ }
+
+ void expectAuth(const String& user, const String& password) {
+ String s = String("") + '\0' + user + '\0' + password;
+ events_.push_back(Event(Event::In, Event::ElementEvent, boost::shared_ptr<AuthRequest>(new AuthRequest("PLAIN", ByteArray(s.getUTF8Data(), s.getUTF8Size())))));
+ }
+
+ void expectResourceBind(const String& resource, const String& id) {
+ boost::shared_ptr<ResourceBind> sessionStart(new ResourceBind());
+ sessionStart->setResource(resource);
+ events_.push_back(Event(Event::In, Event::ElementEvent, IQ::createRequest(IQ::Set, JID(), id, sessionStart)));
+ }
+
+ void expectSessionStart(const String& id) {
+ events_.push_back(Event(Event::In, Event::ElementEvent, IQ::createRequest(IQ::Set, JID(), id, boost::shared_ptr<StartSession>(new StartSession()))));
+ }
+
+ void expectMessage() {
+ events_.push_back(Event(Event::In, Event::ElementEvent, boost::shared_ptr<Message>(new Message())));
+ }
+
+ void sendInvalidXML() {
+ sendData("<invalid xml/>");
+ }
+
+ void sendStreamStart() {
+ events_.push_back(Event(Event::Out, Event::StreamStartEvent));
+ }
+
+ void sendStreamFeatures() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendStreamFeaturesWithStartTLS() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->setHasStartTLS();
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendStreamFeaturesWithAuthentication() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->addAuthenticationMechanism("PLAIN");
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendStreamFeaturesWithUnsupportedAuthentication() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->addAuthenticationMechanism("MY-UNSUPPORTED-MECH");
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendStreamFeaturesWithResourceBind() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->setHasResourceBind();
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendStreamFeaturesWithSession() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->setHasSession();
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendStreamFeaturesWithResourceBindAndSession() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->setHasResourceBind();
+ streamFeatures->setHasSession();
+ events_.push_back(Event(Event::Out, Event::ElementEvent, streamFeatures));
+ }
+
+ void sendMessage() {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, boost::shared_ptr<Message>(new Message())));
+ }
+
+ void sendTLSProceed() {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, boost::shared_ptr<TLSProceed>(new TLSProceed())));
+ }
+
+ void sendTLSFailure() {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, boost::shared_ptr<StartTLSFailure>(new StartTLSFailure())));
+ }
+
+ void sendAuthSuccess() {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, boost::shared_ptr<AuthSuccess>(new AuthSuccess())));
+ }
+
+ void sendAuthFailure() {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, boost::shared_ptr<AuthFailure>(new AuthFailure())));
+ }
+
+ void sendResourceBindResponse(const String& jid, const String& id) {
+ boost::shared_ptr<ResourceBind> sessionStart(new ResourceBind());
+ sessionStart->setJID(JID(jid));
+ events_.push_back(Event(Event::Out, Event::ElementEvent, IQ::createResult(JID(), id, sessionStart)));
+ }
+
+ void sendError(const String& id) {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, IQ::createError(JID(), id, Swift::Error::NotAllowed, Swift::Error::Cancel)));
+ }
+
+ void sendSessionStartResponse(const String& id) {
+ events_.push_back(Event(Event::Out, Event::ElementEvent, IQ::createResult(JID(), id, boost::shared_ptr<StartSession>(new StartSession()))));
+ }
+
+ bool fail_;
+ bool resetParser_;
+ FullPayloadParserFactoryCollection payloadParserFactories_;
+ FullPayloadSerializerCollection payloadSerializers_;
+ XMPPParser* parser_;
+ XMPPSerializer serializer_;
+ std::deque<Event> events_;
+ };
+
+ struct MockConnectionFactory : public ConnectionFactory {
+ MockConnectionFactory() : fail_(false) {}
+ MockConnection* createConnection(const String& domain) {
+ MockConnection* result = new MockConnection(domain, fail_);
+ connections_.push_back(result);
+ return result;
+ }
+ void setCreateFailingConnections() {
+ fail_ = true;
+ }
+ std::vector<MockConnection*> connections_;
+ bool fail_;
+ };
+
+ struct MockTLSLayer : public TLSLayer {
+ MockTLSLayer() : connecting_(false) {}
+ bool setClientCertificate(const PKCS12Certificate&) { return true; }
+ void writeData(const ByteArray& data) { onWriteData(data); }
+ void handleDataRead(const ByteArray& data) { onDataRead(data); }
+ void setConnected() { onConnected(); }
+ void setError() { onError(); }
+ void connect() { connecting_ = true; }
+ bool isConnecting() { return connecting_; }
+
+ bool connecting_;
+ };
+
+ struct MockTLSLayerFactory : public TLSLayerFactory {
+ MockTLSLayerFactory() : haveTLS_(true) {}
+ void setTLSSupported(bool b) { haveTLS_ = b; }
+ virtual bool canCreate() const { return haveTLS_; }
+ virtual TLSLayer* createTLSLayer() {
+ assert(haveTLS_);
+ MockTLSLayer* result = new MockTLSLayer();
+ layers_.push_back(result);
+ return result;
+ }
+ std::vector<MockTLSLayer*> layers_;
+ bool haveTLS_;
+ };
+
+ struct MockSession : public Session {
+ MockSession(const JID& jid, ConnectionFactory* connectionFactory, TLSLayerFactory* tlsLayerFactory, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers) : Session(jid, connectionFactory, tlsLayerFactory, payloadParserFactories, payloadSerializers) {}
+
+ MockTLSLayer* getTLSLayer() const {
+ return getStreamStack()->getLayer<MockTLSLayer>();
+ }
+ WhitespacePingLayer* getWhitespacePingLayer() const {
+ return getStreamStack()->getLayer<WhitespacePingLayer>();
+ }
+ };
+
+ MockSession* createSession(const String& jid) {
+ return new MockSession(JID(jid), connectionFactory_, tlsLayerFactory_, &payloadParserFactories_, &payloadSerializers_);
+ }
+
+
+ DummyEventLoop* eventLoop_;
+ MockConnectionFactory* connectionFactory_;
+ MockTLSLayerFactory* tlsLayerFactory_;
+ FullPayloadParserFactoryCollection payloadParserFactories_;
+ FullPayloadSerializerCollection payloadSerializers_;
+ bool sessionStarted_;
+ bool needCredentials_;
+ std::vector< boost::shared_ptr<Element> > receivedElements_;
+ typedef std::vector< boost::function<void ()> > EventQueue;
+ EventQueue events_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SessionTest);
diff --git a/Swiften/Compress/Makefile.inc b/Swiften/Compress/Makefile.inc
new file mode 100644
index 0000000..bf502fb
--- /dev/null
+++ b/Swiften/Compress/Makefile.inc
@@ -0,0 +1,4 @@
+SWIFTEN_SOURCES += \
+ Swiften/Compress/ZLibCodecompressor.cpp
+
+include Swiften/Compress/UnitTest/Makefile.inc
diff --git a/Swiften/Compress/UnitTest/Makefile.inc b/Swiften/Compress/UnitTest/Makefile.inc
new file mode 100644
index 0000000..fcc1a2c
--- /dev/null
+++ b/Swiften/Compress/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/Compress/UnitTest/ZLibDecompressorTest.cpp \
+ Swiften/Compress/UnitTest/ZLibCompressorTest.cpp
diff --git a/Swiften/Compress/UnitTest/ZLibCompressorTest.cpp b/Swiften/Compress/UnitTest/ZLibCompressorTest.cpp
new file mode 100644
index 0000000..7235f8e
--- /dev/null
+++ b/Swiften/Compress/UnitTest/ZLibCompressorTest.cpp
@@ -0,0 +1,35 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Compress/ZLibCompressor.h"
+
+using namespace Swift;
+
+
+class ZLibCompressorTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ZLibCompressorTest);
+ CPPUNIT_TEST(testProcess);
+ CPPUNIT_TEST(testProcess_Twice);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ZLibCompressorTest() {}
+
+ void testProcess() {
+ ZLibCompressor testling;
+ ByteArray result = testling.process("foo");
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x78\xda\x4a\xcb\xcf\x07\x00\x00\x00\xff\xff", 11), result);
+ }
+
+ void testProcess_Twice() {
+ ZLibCompressor testling;
+ testling.process("foo");
+ ByteArray result = testling.process("bar");
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x4a\x4a\x2c\x02\x00\x00\x00\xff\xff",9), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ZLibCompressorTest);
diff --git a/Swiften/Compress/UnitTest/ZLibDecompressorTest.cpp b/Swiften/Compress/UnitTest/ZLibDecompressorTest.cpp
new file mode 100644
index 0000000..871a630
--- /dev/null
+++ b/Swiften/Compress/UnitTest/ZLibDecompressorTest.cpp
@@ -0,0 +1,71 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Compress/ZLibDecompressor.h"
+#include "Swiften/Compress/ZLibCompressor.h"
+#include "Swiften/Compress/ZLibException.h"
+
+using namespace Swift;
+
+
+class ZLibDecompressorTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ZLibDecompressorTest);
+ CPPUNIT_TEST(testProcess);
+ CPPUNIT_TEST(testProcess_Twice);
+ CPPUNIT_TEST(testProcess_Invalid);
+ CPPUNIT_TEST(testProcess_Huge);
+ CPPUNIT_TEST(testProcess_ChunkSize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ZLibDecompressorTest() {}
+
+ void testProcess() {
+ ZLibDecompressor testling;
+ ByteArray result = testling.process(ByteArray("\x78\xda\x4a\xcb\xcf\x07\x00\x00\x00\xff\xff", 11));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("foo"), result);
+ }
+
+ void testProcess_Twice() {
+ ZLibDecompressor testling;
+ testling.process(ByteArray("\x78\xda\x4a\xcb\xcf\x07\x00\x00\x00\xff\xff", 11));
+ ByteArray result = testling.process(ByteArray("\x4a\x4a\x2c\x02\x00\x00\x00\xff\xff", 9));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("bar"), result);
+ }
+
+ void testProcess_Invalid() {
+ ZLibDecompressor testling;
+ CPPUNIT_ASSERT_THROW(testling.process(ByteArray("invalid")), ZLibException);
+ }
+
+ void testProcess_Huge() {
+ std::vector<char> data;
+ data.reserve(2048);
+ for (unsigned int i = 0; i < 2048; ++i) {
+ data.push_back(static_cast<char>(i));
+ }
+ ByteArray original(&data[0], data.size());
+ ByteArray compressed = ZLibCompressor().process(original);
+ ByteArray decompressed = ZLibDecompressor().process(compressed);
+
+ CPPUNIT_ASSERT_EQUAL(original, decompressed);
+ }
+
+ void testProcess_ChunkSize() {
+ std::vector<char> data;
+ data.reserve(1024);
+ for (unsigned int i = 0; i < 1024; ++i) {
+ data.push_back(static_cast<char>(i));
+ }
+ ByteArray original(&data[0], data.size());
+ ByteArray compressed = ZLibCompressor().process(original);
+ ByteArray decompressed = ZLibDecompressor().process(compressed);
+
+ CPPUNIT_ASSERT_EQUAL(original, decompressed);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ZLibDecompressorTest);
diff --git a/Swiften/Compress/ZLibCodecompressor.cpp b/Swiften/Compress/ZLibCodecompressor.cpp
new file mode 100644
index 0000000..a14f09d
--- /dev/null
+++ b/Swiften/Compress/ZLibCodecompressor.cpp
@@ -0,0 +1,43 @@
+#include "Swiften/Compress/ZLibCodecompressor.h"
+
+#include <cassert>
+
+#include "Swiften/Compress/ZLibException.h"
+
+namespace Swift {
+
+static const int CHUNK_SIZE = 1024; // If you change this, also change the unittest
+
+ZLibCodecompressor::ZLibCodecompressor() {
+ stream_.zalloc = Z_NULL;
+ stream_.zfree = Z_NULL;
+ stream_.opaque = Z_NULL;
+}
+
+ZLibCodecompressor::~ZLibCodecompressor() {
+}
+
+ByteArray ZLibCodecompressor::process(const ByteArray& input) {
+ ByteArray output;
+ stream_.avail_in = input.getSize();
+ stream_.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(input.getData()));
+ int outputPosition = 0;
+ do {
+ output.resize(outputPosition + CHUNK_SIZE);
+ stream_.avail_out = CHUNK_SIZE;
+ stream_.next_out = reinterpret_cast<Bytef*>(output.getData() + outputPosition);
+ int result = processZStream();
+ if (result != Z_OK && result != Z_BUF_ERROR) {
+ throw ZLibException(/* stream_.msg */);
+ }
+ outputPosition += CHUNK_SIZE;
+ }
+ while (stream_.avail_out == 0);
+ if (stream_.avail_in != 0) {
+ throw ZLibException();
+ }
+ output.resize(outputPosition - stream_.avail_out);
+ return output;
+}
+
+}
diff --git a/Swiften/Compress/ZLibCodecompressor.h b/Swiften/Compress/ZLibCodecompressor.h
new file mode 100644
index 0000000..dd032fa
--- /dev/null
+++ b/Swiften/Compress/ZLibCodecompressor.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_ZLibCodecompressor_H
+#define SWIFTEN_ZLibCodecompressor_H
+
+#include <zlib.h>
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class ZLibCodecompressor {
+ public:
+ ZLibCodecompressor();
+ virtual ~ZLibCodecompressor();
+
+ ByteArray process(const ByteArray& data);
+ virtual int processZStream() = 0;
+
+ protected:
+ z_stream stream_;
+ };
+}
+
+#endif
diff --git a/Swiften/Compress/ZLibCompressor.h b/Swiften/Compress/ZLibCompressor.h
new file mode 100644
index 0000000..b5bace6
--- /dev/null
+++ b/Swiften/Compress/ZLibCompressor.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_ZLibCompressor_H
+#define SWIFTEN_ZLibCompressor_H
+
+#include <cassert>
+
+#include "Swiften/Compress/ZLibCodecompressor.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class ZLibCompressor : public ZLibCodecompressor {
+ public:
+ ZLibCompressor() {
+ int result = deflateInit(&stream_, COMPRESSION_LEVEL);
+ assert(result == Z_OK);
+ (void) result;
+ }
+
+ ~ZLibCompressor() {
+ deflateEnd(&stream_);
+ }
+
+ virtual int processZStream() {
+ return deflate(&stream_, Z_SYNC_FLUSH);
+ }
+
+ private:
+ static const int COMPRESSION_LEVEL = 9;
+ };
+}
+
+#endif
diff --git a/Swiften/Compress/ZLibDecompressor.h b/Swiften/Compress/ZLibDecompressor.h
new file mode 100644
index 0000000..808feb2
--- /dev/null
+++ b/Swiften/Compress/ZLibDecompressor.h
@@ -0,0 +1,28 @@
+#ifndef SWIFTEN_ZLibDecompressor_H
+#define SWIFTEN_ZLibDecompressor_H
+
+#include <cassert>
+
+#include "Swiften/Compress/ZLibCodecompressor.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class ZLibDecompressor : public ZLibCodecompressor {
+ public:
+ ZLibDecompressor() {
+ int result = inflateInit(&stream_);
+ assert(result == Z_OK);
+ (void) result;
+ }
+
+ ~ZLibDecompressor() {
+ inflateEnd(&stream_);
+ }
+
+ virtual int processZStream() {
+ return inflate(&stream_, Z_SYNC_FLUSH);
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Compress/ZLibException.h b/Swiften/Compress/ZLibException.h
new file mode 100644
index 0000000..f16b4ed
--- /dev/null
+++ b/Swiften/Compress/ZLibException.h
@@ -0,0 +1,11 @@
+#ifndef SWIFTEN_ZLIBEXCEPTION_H
+#define SWIFTEN_ZLIBEXCEPTION_H
+
+namespace Swift {
+ class ZLibException {
+ public:
+ ZLibException() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Controllers/ChatController.cpp b/Swiften/Controllers/ChatController.cpp
new file mode 100644
index 0000000..bac73d4
--- /dev/null
+++ b/Swiften/Controllers/ChatController.cpp
@@ -0,0 +1,38 @@
+#include "Swiften/Controllers/ChatController.h"
+
+#include "Swiften/Controllers/ChatWindow.h"
+#include "Swiften/Controllers/ChatWindowFactory.h"
+#include "Swiften/Controllers/NickResolver.h"
+
+namespace Swift {
+
+/**
+ * The controller does not gain ownership of the stanzaChannel, nor the factory.
+ */
+ChatController::ChatController(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle)
+ : ChatControllerBase(stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle) {
+ nickResolver_ = nickResolver;
+}
+
+bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
+ return false;
+}
+
+void ChatController::preHandleIncomingMessage(boost::shared_ptr<Message> message) {
+ JID from = message->getFrom();
+ if (!from.equals(toJID_, JID::WithResource)) {
+ if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){
+ toJID_ = from;
+ }
+ }
+}
+
+void ChatController::postSendMessage(const String& body) {
+ chatWindow_->addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>());
+}
+
+String ChatController::senderDisplayNameFromMessage(JID from) {
+ return nickResolver_->jidToNick(from);
+}
+
+}
diff --git a/Swiften/Controllers/ChatController.h b/Swiften/Controllers/ChatController.h
new file mode 100644
index 0000000..314bd70
--- /dev/null
+++ b/Swiften/Controllers/ChatController.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_ChatController_H
+#define SWIFTEN_ChatController_H
+
+#include "Swiften/Controllers/ChatControllerBase.h"
+
+namespace Swift {
+ class NickResolver;
+ class ChatController : public ChatControllerBase {
+ public:
+ ChatController(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle);
+ ~ChatController() {};
+ //boost::signal<void (const JID&, const JID&)> onJIDChanged;
+ protected:
+ bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);
+ void postSendMessage(const String &body);
+ void preHandleIncomingMessage(boost::shared_ptr<Message> message);
+ String senderDisplayNameFromMessage(JID from);
+ private:
+ NickResolver* nickResolver_;
+ JID contact_;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/ChatControllerBase.cpp b/Swiften/Controllers/ChatControllerBase.cpp
new file mode 100644
index 0000000..5f8535e
--- /dev/null
+++ b/Swiften/Controllers/ChatControllerBase.cpp
@@ -0,0 +1,171 @@
+#include "Swiften/Controllers/ChatControllerBase.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Controllers/ChatWindow.h"
+#include "Swiften/Controllers/ChatWindowFactory.h"
+#include "Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h"
+
+namespace Swift {
+
+ChatControllerBase::ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle) {
+ chatWindow_ = chatWindowFactory_->createChatWindow(toJID);
+ chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this));
+ chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1));
+ presenceOracle_->onPresenceChange.connect(boost::bind(&ChatControllerBase::handlePresenceChange, this, _1, _2));
+}
+
+ChatControllerBase::~ChatControllerBase() {
+ delete chatWindow_;
+}
+
+void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) {
+ if (info->hasFeature(DiscoInfo::SecurityLabels)) {
+ chatWindow_->setSecurityLabelsEnabled(true);
+ chatWindow_->setSecurityLabelsError();
+ GetSecurityLabelsCatalogRequest* request = new GetSecurityLabelsCatalogRequest(JID(toJID_.toBare()), iqRouter_, Request::AutoDeleteAfterResponse);
+ request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2));
+ request->send();
+ labelsEnabled_ = true;
+ } else {
+ chatWindow_->setSecurityLabelsEnabled(false);
+ labelsEnabled_ = false;
+ }
+}
+
+String ChatControllerBase::getStatusChangeString(boost::shared_ptr<Presence> presence) {
+ String nick = senderDisplayNameFromMessage(presence->getFrom());
+ if (presence->getType() == Presence::Unavailable) {
+ return nick + " has gone offline.";
+ } else if (presence->getType() == Presence::Available) {
+ StatusShow::Type show = presence->getShow();
+ if (show == StatusShow::Online || show == StatusShow::FFC) {
+ return nick + " has become available.";
+ } else if (show == StatusShow::Away || show == StatusShow::XA) {
+ return nick + " has gone away.";
+ } else if (show == StatusShow::DND) {
+ return nick + " is now busy.";
+ }
+ }
+
+ return "";
+}
+
+void ChatControllerBase::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence) {
+ if (!(toJID_.isBare() && newPresence->getFrom().equals(toJID_, JID::WithoutResource)) && newPresence->getFrom() != toJID_) {
+ return;
+ }
+ String newStatusChangeString = getStatusChangeString(newPresence);
+ if (previousPresence.get() == NULL || newStatusChangeString != getStatusChangeString(previousPresence)) {
+ chatWindow_->addSystemMessage(newStatusChangeString);
+ }
+}
+
+void ChatControllerBase::handleAllMessagesRead() {
+ foreach (boost::shared_ptr<MessageEvent> messageEvent, unreadMessages_) {
+ messageEvent->read();
+ }
+ unreadMessages_.clear();
+ chatWindow_->setUnreadMessageCount(0);
+}
+
+void ChatControllerBase::handleSendMessageRequest(const String &body) {
+ if (body.isEmpty()) {
+ return;
+ }
+ boost::shared_ptr<Message> message(new Message());
+ message->setTo(toJID_);
+ message->setType(Swift::Message::Chat);
+ message->setBody(body);
+ boost::optional<SecurityLabel> label;
+ if (labelsEnabled_) {
+ message->addPayload(boost::shared_ptr<SecurityLabel>(new SecurityLabel(chatWindow_->getSelectedSecurityLabel())));
+ label = boost::optional<SecurityLabel>(chatWindow_->getSelectedSecurityLabel());
+ }
+ preSendMessageRequest(message);
+ stanzaChannel_->sendMessage(message);
+ postSendMessage(message->getBody());
+}
+
+void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, const boost::optional<Error>& error) {
+ if (!error) {
+ if (catalog->getLabels().size() == 0) {
+ chatWindow_->setSecurityLabelsEnabled(false);
+ labelsEnabled_ = false;
+ } else {
+ chatWindow_->setAvailableSecurityLabels(catalog->getLabels());
+ chatWindow_->setSecurityLabelsEnabled(true);
+ }
+ } else {
+ chatWindow_->setSecurityLabelsError();
+ }
+}
+
+void ChatControllerBase::showChatWindow() {
+ chatWindow_->show();
+}
+
+String ChatControllerBase::senderDisplayNameFromMessage(JID from) {
+ return from;
+}
+
+
+
+void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
+ unreadMessages_.push_back(messageEvent);
+ chatWindow_->setUnreadMessageCount(unreadMessages_.size());
+
+ boost::shared_ptr<Message> message = messageEvent->getStanza();
+ preHandleIncomingMessage(message);
+ String body = message->getBody();
+ if (message->isError()) {
+ String errorMessage = getErrorMessage(message->getPayload<Error>());
+ chatWindow_->addErrorMessage(errorMessage);
+ }
+ else {
+ showChatWindow();
+ boost::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>();
+ boost::optional<SecurityLabel> maybeLabel = label ? boost::optional<SecurityLabel>(*label) : boost::optional<SecurityLabel>();
+ JID from = message->getFrom();
+ chatWindow_->addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), maybeLabel);
+ }
+}
+
+String ChatControllerBase::getErrorMessage(boost::shared_ptr<Error> error) {
+ String defaultMessage = "Error sending message";
+ if (!error->getText().isEmpty()) {
+ return error->getText();
+ }
+ else {
+ switch (error->getCondition()) {
+ case Error::BadRequest: return defaultMessage; break;
+ case Error::Conflict: return defaultMessage; break;
+ case Error::FeatureNotImplemented: return defaultMessage; break;
+ case Error::Forbidden: return defaultMessage; break;
+ case Error::Gone: return "Recipient can no longer be contacted"; break;
+ case Error::InternalServerError: return "Internal server error"; break;
+ case Error::ItemNotFound: return defaultMessage; break;
+ case Error::JIDMalformed: return defaultMessage; break;
+ case Error::NotAcceptable: return "Message was rejected"; break;
+ case Error::NotAllowed: return defaultMessage; break;
+ case Error::NotAuthorized: return defaultMessage; break;
+ case Error::PaymentRequired: return defaultMessage; break;
+ case Error::RecipientUnavailable: return "Recipient is unavailable."; break;
+ case Error::Redirect: return defaultMessage; break;
+ case Error::RegistrationRequired: return defaultMessage; break;
+ case Error::RemoteServerNotFound: return "Recipient's server not found."; break;
+ case Error::RemoteServerTimeout: return defaultMessage; break;
+ case Error::ResourceConstraint: return defaultMessage; break;
+ case Error::ServiceUnavailable: return defaultMessage; break;
+ case Error::SubscriptionRequired: return defaultMessage; break;
+ case Error::UndefinedCondition: return defaultMessage; break;
+ case Error::UnexpectedRequest: return defaultMessage; break;
+ }
+ }
+ return defaultMessage;
+}
+
+}
diff --git a/Swiften/Controllers/ChatControllerBase.h b/Swiften/Controllers/ChatControllerBase.h
new file mode 100644
index 0000000..1967977
--- /dev/null
+++ b/Swiften/Controllers/ChatControllerBase.h
@@ -0,0 +1,57 @@
+#ifndef SWIFTEN_ChatControllerBase_H
+#define SWIFTEN_ChatControllerBase_H
+
+#include <map>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/signals.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/Events/MessageEvent.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/SecurityLabelsCatalog.h"
+#include "Swiften/Elements/Error.h"
+#include "Swiften/Presence/PresenceOracle.h"
+
+namespace Swift {
+ class IQRouter;
+ class StanzaChannel;
+ class ChatWindow;
+ class ChatWindowFactory;
+
+ class ChatControllerBase {
+ public:
+ virtual ~ChatControllerBase();
+ void showChatWindow();
+ void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);
+ void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);
+
+ protected:
+ ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle);
+ virtual void postSendMessage(const String&) {};
+ virtual String senderDisplayNameFromMessage(JID from);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);
+ virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0;
+ virtual void preHandleIncomingMessage(boost::shared_ptr<Message>) {};
+ virtual void preSendMessageRequest(boost::shared_ptr<Message>) {};
+
+ std::vector<boost::shared_ptr<MessageEvent> > unreadMessages_;
+ StanzaChannel* stanzaChannel_;
+ IQRouter* iqRouter_;
+ ChatWindowFactory* chatWindowFactory_;
+ ChatWindow* chatWindow_;
+ JID toJID_;
+ bool labelsEnabled_;
+ PresenceOracle* presenceOracle_;
+
+ private:
+ void handleSendMessageRequest(const String &body);
+ String getStatusChangeString(boost::shared_ptr<Presence> presence);
+ void handleAllMessagesRead();
+ void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, const boost::optional<Error>& error);
+ String getErrorMessage(boost::shared_ptr<Error>);
+ };
+}
+
+#endif
diff --git a/Swiften/Controllers/ChatWindow.h b/Swiften/Controllers/ChatWindow.h
new file mode 100644
index 0000000..04d0007
--- /dev/null
+++ b/Swiften/Controllers/ChatWindow.h
@@ -0,0 +1,37 @@
+#ifndef SWIFTEN_CHATWINDOW_H
+#define SWIFTEN_CHATWINDOW_H
+
+#include <boost/optional.hpp>
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/SecurityLabel.h"
+
+namespace Swift {
+ class TreeWidget;
+ class ChatWindow {
+ public:
+ virtual ~ChatWindow() {};
+
+ virtual void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label) = 0;
+ virtual void addSystemMessage(const String& message) = 0;
+ virtual void addErrorMessage(const String& message) = 0;
+
+ virtual void show() = 0;
+ virtual void setAvailableSecurityLabels(const std::vector<SecurityLabel>& labels) = 0;
+ virtual void setSecurityLabelsEnabled(bool enabled) = 0;
+ virtual void setUnreadMessageCount(int count) = 0;
+ virtual void convertToMUC() = 0;
+ virtual TreeWidget *getTreeWidget() = 0;
+ virtual void setSecurityLabelsError() = 0;
+ virtual SecurityLabel getSelectedSecurityLabel() = 0;
+
+ boost::signal<void ()> onClosed;
+ boost::signal<void ()> onAllMessagesRead;
+ boost::signal<void (const String&)> onSendMessageRequest;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/ChatWindowFactory.h b/Swiften/Controllers/ChatWindowFactory.h
new file mode 100644
index 0000000..c55ddba
--- /dev/null
+++ b/Swiften/Controllers/ChatWindowFactory.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_CHATWINDOWFACTORY_H
+#define SWIFTEN_CHATWINDOWFACTORY_H
+
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class ChatWindow;
+
+ class ChatWindowFactory {
+ public:
+ virtual ~ChatWindowFactory() {};
+ /**
+ * Transfers ownership of result.
+ */
+ virtual ChatWindow* createChatWindow(const JID &contact) = 0;
+
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/EventController.cpp b/Swiften/Controllers/EventController.cpp
new file mode 100644
index 0000000..a87f79a
--- /dev/null
+++ b/Swiften/Controllers/EventController.cpp
@@ -0,0 +1,21 @@
+#include "Swiften/Controllers/EventController.h"
+
+#include <boost/bind.hpp>
+#include <algorithm>
+
+namespace Swift {
+
+void EventController::handleIncomingEvent(boost::shared_ptr<MessageEvent> event) {
+ if (event->isReadable()) {
+ events_.push_back(event);
+ event->onRead.connect(boost::bind(&EventController::handleEventRead, this, event));
+ onEventQueueLengthChange(events_.size());
+ }
+}
+
+void EventController::handleEventRead(boost::shared_ptr<MessageEvent> event) {
+ events_.erase(std::remove(events_.begin(), events_.end(), event), events_.end());
+ onEventQueueLengthChange(events_.size());
+}
+
+}
diff --git a/Swiften/Controllers/EventController.h b/Swiften/Controllers/EventController.h
new file mode 100644
index 0000000..ab161af
--- /dev/null
+++ b/Swiften/Controllers/EventController.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_EventController_H
+#define SWIFTEN_EventController_H
+
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include "Swiften/Events/MessageEvent.h"
+
+namespace Swift {
+ class EventController {
+ public:
+ void handleIncomingEvent(boost::shared_ptr<MessageEvent> event);
+ boost::signal<void (int)> onEventQueueLengthChange;
+
+ private:
+ void handleEventRead(boost::shared_ptr<MessageEvent> event);
+ std::vector<boost::shared_ptr<MessageEvent> > events_;
+ };
+}
+#endif
+
+
diff --git a/Swiften/Controllers/LoginWindow.h b/Swiften/Controllers/LoginWindow.h
new file mode 100644
index 0000000..44855c0
--- /dev/null
+++ b/Swiften/Controllers/LoginWindow.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_LoginWindow_H
+#define SWIFTEN_LoginWindow_H
+
+#include "Swiften/Base/String.h"
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class MainWindow;
+ class LoginWindow {
+ public:
+ virtual ~LoginWindow() {};
+ virtual void morphInto(MainWindow *mainWindow) = 0;
+ virtual void loggedOut() = 0;
+ virtual void setMessage(const String&) = 0;
+
+ boost::signal<void (const String&, const String&, const String& /* certificateFile */, bool)> onLoginRequest;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/LoginWindowFactory.h b/Swiften/Controllers/LoginWindowFactory.h
new file mode 100644
index 0000000..d52325e
--- /dev/null
+++ b/Swiften/Controllers/LoginWindowFactory.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_LoginWindowFactory_H
+#define SWIFTEN_LoginWindowFactory_H
+
+
+
+namespace Swift {
+ class LoginWindow;
+ class String;
+
+ class LoginWindowFactory {
+ public:
+ virtual ~LoginWindowFactory() {};
+
+ /**
+ * Transfers ownership of result.
+ */
+ virtual LoginWindow* createLoginWindow(const String& defaultJID, const String& defaultPassword, const String& defaultCertificate) = 0;
+
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/MUCController.cpp b/Swiften/Controllers/MUCController.cpp
new file mode 100644
index 0000000..8137fe1
--- /dev/null
+++ b/Swiften/Controllers/MUCController.cpp
@@ -0,0 +1,76 @@
+#include "Swiften/Controllers/MUCController.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Controllers/ChatWindow.h"
+#include "Swiften/Controllers/ChatWindowFactory.h"
+#include "Swiften/MUC/MUC.h"
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Roster/Roster.h"
+#include "Swiften/Roster/SetPresence.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+
+namespace Swift {
+
+/**
+ * The controller does not gain ownership of the stanzaChannel, nor the factory.
+ */
+MUCController::MUCController (
+ const JID &muc,
+ const String &nick,
+ StanzaChannel* stanzaChannel,
+ IQRouter* iqRouter,
+ ChatWindowFactory* chatWindowFactory,
+ TreeWidgetFactory *treeWidgetFactory,
+ PresenceOracle* presenceOracle) :
+ ChatControllerBase(stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle),
+ muc_(new MUC(stanzaChannel, muc)),
+ nick_(nick),
+ treeWidgetFactory_(treeWidgetFactory) {
+ roster_ = new Roster(chatWindow_->getTreeWidget(), treeWidgetFactory_);
+ chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
+ muc_->joinAs(nick);
+ muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
+ muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
+ muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
+ chatWindow_->convertToMUC();
+ chatWindow_->show();
+}
+
+MUCController::~MUCController() {
+ delete muc_;
+ //don't crash on exit by masking this. FIXME.
+ //delete roster_;
+}
+
+void MUCController::handleWindowClosed() {
+ muc_->part();
+}
+
+void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
+ roster_->addContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()), occupant.getNick(), "Occupants");
+}
+
+void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& /*reason*/) {
+ roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
+}
+
+void MUCController::handleOccupantPresenceChange(boost::shared_ptr<Presence> presence) {
+ roster_->applyOnItems(SetPresence(presence, JID::WithResource));
+}
+
+bool MUCController::isIncomingMessageFromMe(boost::shared_ptr<Message> message) {
+ JID from = message->getFrom();
+ return nick_ == from.getResource();
+}
+
+String MUCController::senderDisplayNameFromMessage(JID from) {
+ return from.getResource();
+}
+
+void MUCController::preSendMessageRequest(boost::shared_ptr<Message> message) {
+ message->setType(Swift::Message::Groupchat);
+}
+
+}
diff --git a/Swiften/Controllers/MUCController.h b/Swiften/Controllers/MUCController.h
new file mode 100644
index 0000000..a7859cf
--- /dev/null
+++ b/Swiften/Controllers/MUCController.h
@@ -0,0 +1,43 @@
+#ifndef SWIFTEN_MUCController_H
+#define SWIFTEN_MUCController_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Controllers/ChatControllerBase.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/MUC/MUC.h"
+#include "Swiften/MUC/MUCOccupant.h"
+
+namespace Swift {
+ class StanzaChannel;
+ class IQRouter;
+ class ChatWindow;
+ class ChatWindowFactory;
+ class Roster;
+ class TreeWidgetFactory;
+
+ class MUCController : public ChatControllerBase {
+ public:
+ MUCController(const JID &muc, const String &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle);
+ ~MUCController();
+
+ protected:
+ void preSendMessageRequest(boost::shared_ptr<Message> message);
+ bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);
+ String senderDisplayNameFromMessage(JID from);
+ private:
+ void handleWindowClosed();
+ void handleOccupantJoined(const MUCOccupant& occupant);
+ void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason);
+ void handleOccupantPresenceChange(boost::shared_ptr<Presence> presence);
+ MUC *muc_;
+ String nick_;
+ TreeWidgetFactory *treeWidgetFactory_;
+ Roster *roster_;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/MainController.cpp b/Swiften/Controllers/MainController.cpp
new file mode 100644
index 0000000..e0d0fe7
--- /dev/null
+++ b/Swiften/Controllers/MainController.cpp
@@ -0,0 +1,259 @@
+#include "Swiften/Controllers/MainController.h"
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+#include <stdlib.h>
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/ApplicationMessageDisplay.h"
+#include "Swiften/Controllers/ChatController.h"
+#include "Swiften/Controllers/ChatWindowFactory.h"
+#include "Swiften/Controllers/EventController.h"
+#include "Swiften/Controllers/LoginWindow.h"
+#include "Swiften/Controllers/LoginWindowFactory.h"
+#include "Swiften/Controllers/MainWindow.h"
+#include "Swiften/Controllers/MainWindowFactory.h"
+#include "Swiften/Controllers/MUCController.h"
+#include "Swiften/Controllers/NickResolver.h"
+#include "Swiften/Controllers/RosterController.h"
+#include "Swiften/Controllers/XMPPRosterController.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Client/Client.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Settings/SettingsProvider.h"
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
+#include "Swiften/Disco/CapsInfoGenerator.h"
+#include "Swiften/Queries/Requests/GetDiscoInfoRequest.h"
+
+namespace Swift {
+
+static const String CLIENT_NAME = "Swift";
+static const String CLIENT_VERSION = "0.3";
+static const String CLIENT_NODE = "http://swift.im";
+
+typedef std::pair<JID, ChatController*> JIDChatControllerPair;
+typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
+
+MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory *treeWidgetFactory, SettingsProvider *settings, Application* application)
+ : client_(NULL), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), treeWidgetFactory_(treeWidgetFactory), settings_(settings),
+ xmppRosterController_(NULL), rosterController_(NULL), loginWindow_(NULL), clientVersionResponder_(NULL), nickResolver_(NULL), discoResponder_(NULL),
+ serverDiscoInfo_(new DiscoInfo()), presenceOracle_(NULL) {
+ application_ = application;
+ eventController_ = new EventController();
+ eventController_->onEventQueueLengthChange.connect(boost::bind(&MainController::handleEventQueueLengthChange, this, _1));
+ loginWindow_ = loginWindowFactory_->createLoginWindow(settings->getStringSetting("jid"), settings->getStringSetting("pass"), settings->getStringSetting("certificate"));
+ loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4));
+}
+
+MainController::~MainController() {
+ delete discoResponder_;
+ delete clientVersionResponder_;
+ delete xmppRosterController_;
+ delete rosterController_;
+ foreach (JIDChatControllerPair controllerPair, chatControllers_) {
+ delete controllerPair.second;
+ }
+ foreach (JIDMUCControllerPair controllerPair, mucControllers_) {
+ delete controllerPair.second;
+ }
+ delete presenceOracle_;
+ delete nickResolver_;
+ delete client_;
+}
+
+void MainController::handleConnected() {
+ delete presenceOracle_;
+ presenceOracle_ = new PresenceOracle(client_);
+
+ client_->onPresenceReceived.connect(boost::bind(&MainController::handleIncomingPresence, this, _1));
+
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+
+
+ delete nickResolver_;
+ nickResolver_ = new NickResolver(xmppRoster);
+
+ delete rosterController_;
+ rosterController_ = new RosterController(xmppRoster, mainWindowFactory_, treeWidgetFactory_);
+ rosterController_->onStartChatRequest.connect(boost::bind(&MainController::handleChatRequest, this, _1));
+ rosterController_->onJoinMUCRequest.connect(boost::bind(&MainController::handleJoinMUCRequest, this, _1, _2));
+ rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
+
+ delete xmppRosterController_;
+ xmppRosterController_ = new XMPPRosterController(client_, xmppRoster);
+
+ delete clientVersionResponder_;
+ clientVersionResponder_ = new SoftwareVersionResponder(CLIENT_NAME, CLIENT_VERSION, client_);
+ loginWindow_->morphInto(rosterController_->getWindow());
+
+ DiscoInfo discoInfo;
+ discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
+ capsInfo_ = boost::shared_ptr<CapsInfo>(new CapsInfo(CapsInfoGenerator(CLIENT_NODE).generateCapsInfo(discoInfo)));
+ discoResponder_ = new DiscoInfoResponder(client_);
+ discoResponder_->setDiscoInfo(discoInfo);
+ discoResponder_->setDiscoInfo(capsInfo_->getNode() + "#" + capsInfo_->getVersion(), discoInfo);
+
+ serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo());
+ GetDiscoInfoRequest* discoInfoRequest = new GetDiscoInfoRequest(JID(), client_, Request::AutoDeleteAfterResponse);
+ discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2));
+ discoInfoRequest->send();
+
+ //Send presence last to catch all the incoming presences.
+ boost::shared_ptr<Presence> initialPresence(new Presence());
+ initialPresence->addPayload(capsInfo_);
+ client_->sendPresence(initialPresence);
+}
+
+void MainController::handleEventQueueLengthChange(int count) {
+ application_->getApplicationMessageDisplay()->setMessage(count == 0 ? "" : boost::lexical_cast<std::string>(count).c_str());
+}
+
+void MainController::handleChangeStatusRequest(StatusShow::Type show, const String &statusText) {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->addPayload(capsInfo_);
+ presence->setShow(show);
+ presence->setStatus(statusText);
+ // FIXME: This is wrong. None doesn't mean unavailable
+ if (show == StatusShow::None) {
+ presence->setType(Presence::Unavailable);
+ }
+ client_->sendPresence(presence);
+ if (presence->getType() == Presence::Unavailable) {
+ logout();
+ }
+}
+
+void MainController::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ rosterController_->handleIncomingPresence(presence);
+}
+
+void MainController::handleLoginRequest(const String &username, const String &password, const String& certificateFile, bool remember) {
+ loginWindow_->setMessage("");
+
+ settings_->storeString("jid", username);
+ settings_->storeString("certificate", certificateFile);
+ settings_->storeString("pass", remember ? password : "");
+
+ delete client_;
+ client_ = new Swift::Client(JID(username), password);
+ if (!certificateFile.isEmpty()) {
+ client_->setCertificate(certificateFile);
+ }
+ client_->onError.connect(boost::bind(&MainController::handleError, this, _1));
+ client_->onConnected.connect(boost::bind(&MainController::handleConnected, this));
+ client_->onMessageReceived.connect(boost::bind(&MainController::handleIncomingMessage, this, _1));
+ client_->connect();
+}
+
+void MainController::handleError(const ClientError& error) {
+ String message;
+ switch(error.getType()) {
+ case ClientError::NoError: assert(false); break;
+ case ClientError::DomainNameResolveError: message = "Unable to find server"; break;
+ case ClientError::ConnectionError: message = "Error connecting to server"; break;
+ case ClientError::ConnectionReadError: message = "Error while receiving server data"; break;
+ case ClientError::XMLError: message = "Error parsing server data"; break;
+ case ClientError::AuthenticationFailedError: message = "Login/password invalid"; break;
+ case ClientError::NoSupportedAuthMechanismsError: message = "Authentication mechanisms not supported"; break;
+ case ClientError::UnexpectedElementError: message = "Unexpected response"; break;
+ case ClientError::ResourceBindError: message = "Error binding resource"; break;
+ case ClientError::SessionStartError: message = "Error starting session"; break;
+ case ClientError::TLSError: message = "Encryption error"; break;
+ case ClientError::ClientCertificateLoadError: message = "Error loading certificate (Invalid password?)"; break;
+ case ClientError::ClientCertificateError: message = "Certificate not authorized"; break;
+ }
+ loginWindow_->setMessage(message);
+ logout();
+}
+
+void MainController::logout() {
+ loginWindow_->loggedOut();
+
+ delete discoResponder_;
+ discoResponder_ = NULL;
+ delete clientVersionResponder_;
+ clientVersionResponder_ = NULL;
+ foreach (JIDChatControllerPair controllerPair, chatControllers_) {
+ delete controllerPair.second;
+ }
+ client_->disconnect();
+ chatControllers_.clear();
+ foreach (JIDMUCControllerPair controllerPair, mucControllers_) {
+ delete controllerPair.second;
+ }
+ mucControllers_.clear();
+}
+
+
+void MainController::handleChatRequest(const String &contact) {
+ getChatController(JID(contact))->showChatWindow();
+}
+
+ChatController* MainController::getChatController(const JID &contact) {
+ JID lookupContact(contact);
+ if (chatControllers_.find(lookupContact) == chatControllers_.end()) {
+ lookupContact = JID(contact.toBare());
+ }
+ if (chatControllers_.find(lookupContact) == chatControllers_.end()) {
+ chatControllers_[contact] = new ChatController(client_, client_, chatWindowFactory_, contact, nickResolver_, presenceOracle_);
+ chatControllers_[contact]->setAvailableServerFeatures(serverDiscoInfo_);
+ lookupContact = contact;
+ }
+ return chatControllers_[lookupContact];
+}
+
+void MainController::handleChatControllerJIDChanged(const JID& from, const JID& to) {
+ chatControllers_[to] = chatControllers_[from];
+ chatControllers_.erase(from);
+}
+
+void MainController::handleJoinMUCRequest(const JID &muc, const String &nick) {
+ mucControllers_[muc] = new MUCController(muc, nick, client_, client_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_);
+ mucControllers_[muc]->setAvailableServerFeatures(serverDiscoInfo_);
+}
+
+void MainController::handleIncomingMessage(boost::shared_ptr<Message> message) {
+ JID jid = message->getFrom();
+ boost::shared_ptr<MessageEvent> event(new MessageEvent(message));
+
+ // Try to deliver it to a MUC
+ if (message->getType() == Message::Groupchat || message->getType() == Message::Error) {
+ std::map<JID, MUCController*>::iterator i = mucControllers_.find(jid.toBare());
+ if (i != mucControllers_.end()) {
+ i->second->handleIncomingMessage(event);
+ return;
+ }
+ else if (message->getType() == Message::Groupchat) {
+ //FIXME: Error handling - groupchat messages from an unknown muc.
+ return;
+ }
+ }
+
+ //if not a mucroom
+ eventController_->handleIncomingEvent(event);
+
+ // FIXME: This logic should go into a chat manager
+ if (event->isReadable()) {
+ getChatController(jid)->handleIncomingMessage(event);
+ }
+}
+
+void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, const boost::optional<Error>& error) {
+ if (!error) {
+ serverDiscoInfo_ = info;
+ foreach (JIDChatControllerPair pair, chatControllers_) {
+ pair.second->setAvailableServerFeatures(info);
+ }
+ foreach (JIDMUCControllerPair pair, mucControllers_) {
+ pair.second->setAvailableServerFeatures(info);
+ }
+ }
+}
+
+}
diff --git a/Swiften/Controllers/MainController.h b/Swiften/Controllers/MainController.h
new file mode 100644
index 0000000..e09d4fa
--- /dev/null
+++ b/Swiften/Controllers/MainController.h
@@ -0,0 +1,85 @@
+#ifndef SWIFTEN_MainController_H
+#define SWIFTEN_MainController_H
+
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Client/ClientError.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/Elements/Error.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Settings/SettingsProvider.h"
+#include "Swiften/Elements/CapsInfo.h"
+
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+
+namespace Swift {
+ class Application;
+ class Client;
+ class ChatWindowFactory;
+ class ChatController;
+ class EventController;
+ class MainWindowFactory;
+ class MainWindow;
+ class NickResolver;
+ class RosterController;
+ class XMPPRosterController;
+ class DiscoInfoResponder;
+ class LoginWindow;
+ class EventLoop;
+ class SoftwareVersionResponder;
+ class LoginWindowFactory;
+ class TreeWidgetFactory;
+ class MUCController;
+ class PresenceOracle;
+
+ class MainController {
+ public:
+ MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory* treeWidgetFactory, SettingsProvider *settings, Application* application);
+ ~MainController();
+
+
+ private:
+ void handleConnected();
+ void handleLoginRequest(const String& username, const String& password, const String& certificateFile, bool remember);
+ void handleChatRequest(const String& contact);
+ void handleJoinMUCRequest(const JID& muc, const String& nick);
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+ void handleChatControllerJIDChanged(const JID& from, const JID& to);
+ void handleIncomingMessage(boost::shared_ptr<Message> message);
+ void handleChangeStatusRequest(StatusShow::Type show, const String &statusText);
+ void handleError(const ClientError& error);
+ void handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>, const boost::optional<Error>&);
+ void handleEventQueueLengthChange(int count);
+ ChatController* getChatController(const JID &contact);
+ void logout();
+
+ Client* client_;
+ ChatWindowFactory* chatWindowFactory_;
+ MainWindowFactory* mainWindowFactory_;
+ LoginWindowFactory* loginWindowFactory_;
+ TreeWidgetFactory* treeWidgetFactory_;
+ SettingsProvider *settings_;
+ Application* application_;
+ ChatController* chatController_;
+ XMPPRosterController* xmppRosterController_;
+ RosterController* rosterController_;
+ EventController* eventController_;
+ LoginWindow* loginWindow_;
+ SoftwareVersionResponder* clientVersionResponder_;
+ NickResolver* nickResolver_;
+ DiscoInfoResponder* discoResponder_;
+ boost::shared_ptr<CapsInfo> capsInfo_;
+ std::map<JID, MUCController*> mucControllers_;
+ std::map<JID, ChatController*> chatControllers_;
+ boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
+ PresenceOracle* presenceOracle_;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/MainWindow.h b/Swiften/Controllers/MainWindow.h
new file mode 100644
index 0000000..081fe6e
--- /dev/null
+++ b/Swiften/Controllers/MainWindow.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_MainWindow_H
+#define SWIFTEN_MainWindow_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/StatusShow.h"
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class TreeWidget;
+
+ class MainWindow {
+ public:
+ virtual ~MainWindow() {};
+ virtual TreeWidget* getTreeWidget() = 0;
+
+ boost::signal<void (const JID&)> onStartChatRequest;
+ boost::signal<void (const JID&, const String&)> onJoinMUCRequest;
+ boost::signal<void (StatusShow::Type, const String&)> onChangeStatusRequest;
+ boost::signal<void (bool)> onShowOfflineToggled;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/MainWindowFactory.h b/Swiften/Controllers/MainWindowFactory.h
new file mode 100644
index 0000000..cf5a061
--- /dev/null
+++ b/Swiften/Controllers/MainWindowFactory.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_MainWindowFactory_H
+#define SWIFTEN_MainWindowFactory_H
+
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class MainWindow;
+
+ class MainWindowFactory {
+ public:
+ virtual ~MainWindowFactory() {};
+ /**
+ * Transfers ownership of result.
+ */
+ virtual MainWindow* createMainWindow() = 0;
+
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/Makefile.inc b/Swiften/Controllers/Makefile.inc
new file mode 100644
index 0000000..c51b0f3
--- /dev/null
+++ b/Swiften/Controllers/Makefile.inc
@@ -0,0 +1,11 @@
+SWIFTEN_SOURCES += \
+ Swiften/Controllers/ChatController.cpp \
+ Swiften/Controllers/ChatControllerBase.cpp \
+ Swiften/Controllers/MainController.cpp \
+ Swiften/Controllers/NickResolver.cpp \
+ Swiften/Controllers/RosterController.cpp \
+ Swiften/Controllers/XMPPRosterController.cpp \
+ Swiften/Controllers/MUCController.cpp \
+ Swiften/Controllers/EventController.cpp
+
+include Swiften/Controllers/UnitTest/Makefile.inc
diff --git a/Swiften/Controllers/NickResolver.cpp b/Swiften/Controllers/NickResolver.cpp
new file mode 100644
index 0000000..1c51cb7
--- /dev/null
+++ b/Swiften/Controllers/NickResolver.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/Controllers/NickResolver.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Roster/XMPPRoster.h"
+
+namespace Swift {
+
+NickResolver::NickResolver(boost::shared_ptr<XMPPRoster> xmppRoster) {
+ xmppRoster_ = xmppRoster;
+}
+
+String NickResolver::jidToNick(const JID& jid) {
+ if (xmppRoster_->containsJID(jid)) {
+ return xmppRoster_->getNameForJID(jid);
+ }
+ std::map<JID, String>::iterator it = map_.find(jid);
+ return (it == map_.end()) ? jid.toBare() : it->second;
+}
+
+}
+
diff --git a/Swiften/Controllers/NickResolver.h b/Swiften/Controllers/NickResolver.h
new file mode 100644
index 0000000..b7dc005
--- /dev/null
+++ b/Swiften/Controllers/NickResolver.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_NickResolver_H
+#define SWIFTEN_NickResolver_H
+
+#include <map>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class XMPPRoster;
+ class NickResolver {
+ public:
+ NickResolver(boost::shared_ptr<XMPPRoster> xmppRoster);
+ String jidToNick(const JID& jid);
+
+ private:
+ std::map<JID, String> map_;
+ boost::shared_ptr<XMPPRoster> xmppRoster_;
+ };
+}
+#endif
+
+
diff --git a/Swiften/Controllers/RosterController.cpp b/Swiften/Controllers/RosterController.cpp
new file mode 100644
index 0000000..1efbeea
--- /dev/null
+++ b/Swiften/Controllers/RosterController.cpp
@@ -0,0 +1,83 @@
+#include "Swiften/Controllers/RosterController.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Controllers/MainWindow.h"
+#include "Swiften/Controllers/MainWindowFactory.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Roster/Roster.h"
+#include "Swiften/Roster/SetPresence.h"
+#include "Swiften/Roster/OfflineRosterFilter.h"
+#include "Swiften/Roster/OpenChatRosterAction.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/XMPPRoster.h"
+
+namespace Swift {
+
+/**
+ * The controller does not gain ownership of these parameters.
+ */
+RosterController::RosterController(boost::shared_ptr<XMPPRoster> xmppRoster, MainWindowFactory* mainWindowFactory, TreeWidgetFactory* treeWidgetFactory)
+ : xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), treeWidgetFactory_(treeWidgetFactory), mainWindow_(mainWindowFactory_->createMainWindow()), roster_(new Roster(mainWindow_->getTreeWidget(), treeWidgetFactory_)), offlineFilter_(new OfflineRosterFilter()) {
+ roster_->addFilter(offlineFilter_);
+ mainWindow_->onStartChatRequest.connect(boost::bind(&RosterController::handleStartChatRequest, this, _1));
+ mainWindow_->onJoinMUCRequest.connect(boost::bind(&RosterController::handleJoinMUCRequest, this, _1, _2));
+ mainWindow_->onChangeStatusRequest.connect(boost::bind(&RosterController::handleChangeStatusRequest, this, _1, _2));
+ mainWindow_->onShowOfflineToggled.connect(boost::bind(&RosterController::handleShowOfflineToggled, this, _1));
+ roster_->onUserAction.connect(boost::bind(&RosterController::handleUserAction, this, _1));
+ xmppRoster_->onJIDAdded.connect(boost::bind(&RosterController::handleOnJIDAdded, this, _1));
+}
+
+RosterController::~RosterController() {
+ delete offlineFilter_;
+
+}
+
+void RosterController::handleShowOfflineToggled(bool state) {
+ if (state) {
+ roster_->removeFilter(offlineFilter_);
+ } else {
+ roster_->addFilter(offlineFilter_);
+ }
+}
+
+void RosterController::handleChangeStatusRequest(StatusShow::Type show, const String &statusText) {
+ onChangeStatusRequest(show, statusText);
+}
+
+void RosterController::handleUserAction(boost::shared_ptr<UserRosterAction> action) {
+ boost::shared_ptr<OpenChatRosterAction> chatAction = boost::dynamic_pointer_cast<OpenChatRosterAction>(action);
+ if (chatAction.get() != NULL) {
+ ContactRosterItem *contactItem = dynamic_cast<ContactRosterItem*>(chatAction->getRosterItem());
+ assert(contactItem);
+ onStartChatRequest(contactItem->getJID().toBare());
+ }
+}
+
+void RosterController::handleOnJIDAdded(const JID& jid) {
+ std::vector<String> groups = xmppRoster_->getGroupsForJID(jid);
+ String name = xmppRoster_->getNameForJID(jid);
+ if (!groups.empty()) {
+ foreach(const String& group, groups) {
+ roster_->addContact(jid, name, group);
+ }
+ } else {
+ roster_->addContact(jid, name, "Contacts");
+ }
+}
+
+void RosterController::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ roster_->applyOnItems(SetPresence(presence));
+}
+
+void RosterController::handleStartChatRequest(const JID& contact) {
+ onStartChatRequest(contact);
+}
+
+void RosterController::handleJoinMUCRequest(const JID &muc, const String &nick) {
+ onJoinMUCRequest(JID(muc), nick);
+}
+
+}
diff --git a/Swiften/Controllers/RosterController.h b/Swiften/Controllers/RosterController.h
new file mode 100644
index 0000000..945b068
--- /dev/null
+++ b/Swiften/Controllers/RosterController.h
@@ -0,0 +1,48 @@
+#ifndef SWIFTEN_RosterController_H
+#define SWIFTEN_RosterController_H
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Roster/UserRosterAction.h"
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class IQRouter;
+ class Roster;
+ class XMPPRoster;
+ class MainWindow;
+ class MainWindowFactory;
+ class TreeWidgetFactory;
+ class OfflineRosterFilter;
+
+ class RosterController {
+ public:
+ RosterController(boost::shared_ptr<XMPPRoster> xmppRoster, MainWindowFactory *mainWindowFactory, TreeWidgetFactory *treeWidgetFactory);
+ ~RosterController();
+ void showRosterWindow();
+ MainWindow* getWindow() {return mainWindow_;};
+ boost::signal<void (const JID&)> onStartChatRequest;
+ boost::signal<void (const JID&, const String&)> onJoinMUCRequest;
+ boost::signal<void (StatusShow::Type, const String&)> onChangeStatusRequest;
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+
+ private:
+ void handleOnJIDAdded(const JID &jid);
+ void handleStartChatRequest(const JID& contact);
+ void handleJoinMUCRequest(const JID &muc, const String &nick);
+ void handleUserAction(boost::shared_ptr<UserRosterAction> action);
+ void handleChangeStatusRequest(StatusShow::Type show, const String &statusText);
+ void handleShowOfflineToggled(bool state);
+ boost::shared_ptr<XMPPRoster> xmppRoster_;
+ MainWindowFactory* mainWindowFactory_;
+ TreeWidgetFactory* treeWidgetFactory_;
+ MainWindow* mainWindow_;
+ Roster* roster_;
+ OfflineRosterFilter* offlineFilter_;
+ };
+}
+#endif
+
diff --git a/Swiften/Controllers/UnitTest/Makefile.inc b/Swiften/Controllers/UnitTest/Makefile.inc
new file mode 100644
index 0000000..163da01
--- /dev/null
+++ b/Swiften/Controllers/UnitTest/Makefile.inc
@@ -0,0 +1,4 @@
+UNITTEST_SOURCES += \
+ Swiften/Controllers/UnitTest/NickResolverTest.cpp \
+ Swiften/Controllers/UnitTest/XMPPRosterControllerTest.cpp
+
diff --git a/Swiften/Controllers/UnitTest/NickResolverTest.cpp b/Swiften/Controllers/UnitTest/NickResolverTest.cpp
new file mode 100644
index 0000000..9c89d4d
--- /dev/null
+++ b/Swiften/Controllers/UnitTest/NickResolverTest.cpp
@@ -0,0 +1,62 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Controllers/NickResolver.h"
+#include "Swiften/Roster/XMPPRoster.h"
+
+using namespace Swift;
+
+class NickResolverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(NickResolverTest);
+ CPPUNIT_TEST(testNoMatch);
+ CPPUNIT_TEST(testMatch);
+ CPPUNIT_TEST(testOverwrittenMatch);
+ CPPUNIT_TEST(testRemovedMatch);
+ CPPUNIT_TEST_SUITE_END();
+
+ std::vector<String> groups_;
+
+ public:
+ NickResolverTest() {}
+
+ void testNoMatch() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ NickResolver resolver(xmppRoster);
+ JID testling("foo@bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo@bar"), resolver.jidToNick(testling));
+ }
+
+ void testMatch() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ NickResolver resolver(xmppRoster);
+ JID testling("foo@bar/baz");
+ xmppRoster->addContact(testling, "Test", groups_);
+
+ CPPUNIT_ASSERT_EQUAL(String("Test"), resolver.jidToNick(testling));
+ }
+
+ void testOverwrittenMatch() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ NickResolver resolver(xmppRoster);
+ JID testling("foo@bar/baz");
+ xmppRoster->addContact(testling, "FailTest", groups_);
+ xmppRoster->addContact(testling, "Test", groups_);
+
+ CPPUNIT_ASSERT_EQUAL(String("Test"), resolver.jidToNick(testling));
+ }
+
+ void testRemovedMatch() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ NickResolver resolver(xmppRoster);
+ JID testling("foo@bar/baz");
+ xmppRoster->addContact(testling, "FailTest", groups_);
+ xmppRoster->removeContact(testling);
+ CPPUNIT_ASSERT_EQUAL(String("foo@bar"), resolver.jidToNick(testling));
+ }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(NickResolverTest);
+
diff --git a/Swiften/Controllers/UnitTest/XMPPRosterControllerTest.cpp b/Swiften/Controllers/UnitTest/XMPPRosterControllerTest.cpp
new file mode 100644
index 0000000..3bff8f3
--- /dev/null
+++ b/Swiften/Controllers/UnitTest/XMPPRosterControllerTest.cpp
@@ -0,0 +1,83 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Controllers/XMPPRosterController.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/RosterItemPayload.h"
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Roster/XMPPRoster.h"
+
+using namespace Swift;
+
+class XMPPRosterControllerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMPPRosterControllerTest);
+ CPPUNIT_TEST(testAdd);
+ CPPUNIT_TEST(testModify);
+ CPPUNIT_TEST(testRemove);
+ CPPUNIT_TEST_SUITE_END();
+
+ DummyIQChannel* channel_;
+ IQRouter* router_;
+ public:
+ XMPPRosterControllerTest() : channel_(new DummyIQChannel()), router_(new IQRouter(channel_)) {}
+
+ ~XMPPRosterControllerTest() {
+ delete channel_;
+ delete router_;
+ }
+
+ void testAdd() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ XMPPRosterController controller(router_, xmppRoster);
+ JID testling("foo@bar");
+ CPPUNIT_ASSERT(!xmppRoster->containsJID(testling));
+ boost::shared_ptr<Payload> payload(new RosterPayload());
+ RosterItemPayload item(testling, "Bob", RosterItemPayload::Both);
+ dynamic_cast<RosterPayload*>(payload.get())->addItem(item);
+ controller.handleIQ(IQ::createRequest(IQ::Set, JID(), "eou", payload));
+ CPPUNIT_ASSERT(xmppRoster->containsJID(testling));
+ }
+
+ void testModify() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ XMPPRosterController controller(router_, xmppRoster);
+ JID testling("foo@bar");
+ CPPUNIT_ASSERT(!xmppRoster->containsJID(testling));
+ boost::shared_ptr<Payload> payload1(new RosterPayload());
+ RosterItemPayload item1(testling, "Bob", RosterItemPayload::Both);
+ dynamic_cast<RosterPayload*>(payload1.get())->addItem(item1);
+ controller.handleIQ(IQ::createRequest(IQ::Set, JID(), "eou", payload1));
+ CPPUNIT_ASSERT(xmppRoster->containsJID(testling));
+ CPPUNIT_ASSERT_EQUAL(String("Bob"), xmppRoster->getNameForJID(testling));
+ boost::shared_ptr<Payload> payload2(new RosterPayload());
+ RosterItemPayload item2(testling, "Bob2", RosterItemPayload::Both);
+ dynamic_cast<RosterPayload*>(payload2.get())->addItem(item2);
+ controller.handleIQ(IQ::createRequest(IQ::Set, JID(), "eou", payload2));
+ CPPUNIT_ASSERT_EQUAL(String("Bob2"), xmppRoster->getNameForJID(testling));
+ }
+
+ void testRemove() {
+ boost::shared_ptr<XMPPRoster> xmppRoster(new XMPPRoster());
+ XMPPRosterController controller(router_, xmppRoster);
+ JID testling("foo@bar");
+ CPPUNIT_ASSERT(!xmppRoster->containsJID(testling));
+ boost::shared_ptr<Payload> payload1(new RosterPayload());
+ RosterItemPayload item1(testling, "Bob", RosterItemPayload::Both);
+ dynamic_cast<RosterPayload*>(payload1.get())->addItem(item1);
+ controller.handleIQ(IQ::createRequest(IQ::Set, JID(), "eou", payload1));
+ CPPUNIT_ASSERT(xmppRoster->containsJID(testling));
+ boost::shared_ptr<Payload> payload2(new RosterPayload());
+ RosterItemPayload item2(testling, "Bob", RosterItemPayload::Remove);
+ dynamic_cast<RosterPayload*>(payload2.get())->addItem(item2);
+ controller.handleIQ(IQ::createRequest(IQ::Set, JID(), "eou", payload2));
+ CPPUNIT_ASSERT(!xmppRoster->containsJID(testling));
+ }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterControllerTest);
+
+
diff --git a/Swiften/Controllers/XMPPRosterController.cpp b/Swiften/Controllers/XMPPRosterController.cpp
new file mode 100644
index 0000000..b6c36d7
--- /dev/null
+++ b/Swiften/Controllers/XMPPRosterController.cpp
@@ -0,0 +1,54 @@
+#include "Swiften/Controllers/XMPPRosterController.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Controllers/MainWindow.h"
+#include "Swiften/Controllers/MainWindowFactory.h"
+#include "Swiften/Elements/RosterItemPayload.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Roster/Roster.h"
+#include "Swiften/Roster/SetPresence.h"
+#include "Swiften/Roster/OfflineRosterFilter.h"
+#include "Swiften/Roster/OpenChatRosterAction.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/XMPPRoster.h"
+
+namespace Swift {
+
+/**
+ * The controller does not gain ownership of these parameters.
+ */
+XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, boost::shared_ptr<XMPPRoster> xmppRoster)
+ : IQHandler(iqRouter), xmppRoster_(xmppRoster) {
+ GetRosterRequest* rosterRequest = new GetRosterRequest(iqRouter, Request::AutoDeleteAfterResponse);
+ rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1));
+ rosterRequest->send();
+}
+
+XMPPRosterController::~XMPPRosterController() {
+
+}
+
+void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload) {
+ foreach(const RosterItemPayload& item, rosterPayload->getItems()) {
+ if (item.getSubscription() == RosterItemPayload::Remove) {
+ xmppRoster_->removeContact(item.getJID());
+ } else {
+ xmppRoster_->addContact(item.getJID(), item.getName(), item.getGroups());
+ }
+ }
+}
+
+bool XMPPRosterController::handleIQ(boost::shared_ptr<IQ> iq) {
+ if (iq->getType() != IQ::Set || iq->getPayload<RosterPayload>().get() == NULL || iq->getFrom().isValid()) {
+ return false;
+ }
+ handleRosterReceived(iq->getPayload<RosterPayload>());
+ return true;
+}
+
+}
+
diff --git a/Swiften/Controllers/XMPPRosterController.h b/Swiften/Controllers/XMPPRosterController.h
new file mode 100644
index 0000000..2eaa6f5
--- /dev/null
+++ b/Swiften/Controllers/XMPPRosterController.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Queries/IQHandler.h"
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class IQRouter;
+ class XMPPRoster;
+
+ class XMPPRosterController : public IQHandler {
+ public:
+ XMPPRosterController(IQRouter *iqRouter, boost::shared_ptr<XMPPRoster> xmppRoster);
+ ~XMPPRosterController();
+ boost::shared_ptr<XMPPRoster> getXMPPRoster() {return xmppRoster_;};
+ bool handleIQ(boost::shared_ptr<IQ>);
+
+ private:
+ void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload);
+ boost::shared_ptr<XMPPRoster> xmppRoster_;
+ };
+}
+
diff --git a/Swiften/Disco/CapsInfoGenerator.cpp b/Swiften/Disco/CapsInfoGenerator.cpp
new file mode 100644
index 0000000..339d76e
--- /dev/null
+++ b/Swiften/Disco/CapsInfoGenerator.cpp
@@ -0,0 +1,34 @@
+#include "Swiften/Disco/CapsInfoGenerator.h"
+
+#include <algorithm>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/StringCodecs/SHA1.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+CapsInfoGenerator::CapsInfoGenerator(const String& node) : node_(node) {
+}
+
+CapsInfo CapsInfoGenerator::generateCapsInfo(const DiscoInfo& discoInfo) const {
+ String serializedCaps;
+
+ std::vector<DiscoInfo::Identity> identities(discoInfo.getIdentities());
+ std::sort(identities.begin(), identities.end());
+ foreach (const DiscoInfo::Identity& identity, identities) {
+ serializedCaps += identity.getCategory() + "/" + identity.getType() + "/" + identity.getLanguage() + "/" + identity.getName() + "<";
+ }
+
+ std::vector<String> features(discoInfo.getFeatures());
+ std::sort(features.begin(), features.end());
+ foreach (const String& feature, features) {
+ serializedCaps += feature + "<";
+ }
+
+ String version(Base64::encode(SHA1::getBinaryHash(serializedCaps)));
+ return CapsInfo(node_, version, "sha-1");
+}
+
+}
diff --git a/Swiften/Disco/CapsInfoGenerator.h b/Swiften/Disco/CapsInfoGenerator.h
new file mode 100644
index 0000000..66949ab
--- /dev/null
+++ b/Swiften/Disco/CapsInfoGenerator.h
@@ -0,0 +1,21 @@
+#ifndef SWIFTEN_CapsInfoGenerator_H
+#define SWIFTEN_CapsInfoGenerator_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/CapsInfo.h"
+
+namespace Swift {
+ class DiscoInfo;
+
+ class CapsInfoGenerator {
+ public:
+ CapsInfoGenerator(const String& node);
+
+ CapsInfo generateCapsInfo(const DiscoInfo& discoInfo) const;
+
+ private:
+ String node_;
+ };
+}
+
+#endif
diff --git a/Swiften/Disco/Makefile.inc b/Swiften/Disco/Makefile.inc
new file mode 100644
index 0000000..5aeba07
--- /dev/null
+++ b/Swiften/Disco/Makefile.inc
@@ -0,0 +1,4 @@
+SWIFTEN_SOURCES += \
+ Swiften/Disco/CapsInfoGenerator.cpp
+
+include Swiften/Disco/UnitTest/Makefile.inc
diff --git a/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp b/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp
new file mode 100644
index 0000000..94b9913
--- /dev/null
+++ b/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp
@@ -0,0 +1,35 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/Disco/CapsInfoGenerator.h"
+
+using namespace Swift;
+
+class CapsInfoGeneratorTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(CapsInfoGeneratorTest);
+ CPPUNIT_TEST(testGenerate_XEP0115SimpleExample);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ CapsInfoGeneratorTest() {}
+
+ void testGenerate_XEP0115SimpleExample() {
+ DiscoInfo discoInfo;
+ discoInfo.addIdentity(DiscoInfo::Identity("Exodus 0.9.1", "client", "pc"));
+ discoInfo.addFeature("http://jabber.org/protocol/disco#items");
+ discoInfo.addFeature("http://jabber.org/protocol/caps");
+ discoInfo.addFeature("http://jabber.org/protocol/disco#info");
+ discoInfo.addFeature("http://jabber.org/protocol/muc");
+
+ CapsInfoGenerator testling("http://code.google.com/p/exodus");
+ CapsInfo result = testling.generateCapsInfo(discoInfo);
+
+ CPPUNIT_ASSERT_EQUAL(String("http://code.google.com/p/exodus"), result.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("sha-1"), result.getHash());
+ CPPUNIT_ASSERT_EQUAL(String("QgayPKawpkPSDYmwT/WM94uAlu0="), result.getVersion());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CapsInfoGeneratorTest);
diff --git a/Swiften/Disco/UnitTest/Makefile.inc b/Swiften/Disco/UnitTest/Makefile.inc
new file mode 100644
index 0000000..928664c
--- /dev/null
+++ b/Swiften/Disco/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+ Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp
diff --git a/Swiften/Elements/AuthFailure.h b/Swiften/Elements/AuthFailure.h
new file mode 100644
index 0000000..b1857b3
--- /dev/null
+++ b/Swiften/Elements/AuthFailure.h
@@ -0,0 +1,13 @@
+#ifndef SWIFTEN_AuthFailure_H
+#define SWIFTEN_AuthFailure_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class AuthFailure : public Element {
+ public:
+ AuthFailure() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/AuthRequest.h b/Swiften/Elements/AuthRequest.h
new file mode 100644
index 0000000..1f49175
--- /dev/null
+++ b/Swiften/Elements/AuthRequest.h
@@ -0,0 +1,36 @@
+#ifndef SWIFTEN_AuthRequest_H
+#define SWIFTEN_AuthRequest_H
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class AuthRequest : public Element {
+ public:
+ AuthRequest(const String& mechanism = "", const ByteArray& message = "") :
+ mechanism_(mechanism), message_(message) {
+ }
+
+ const ByteArray& getMessage() const {
+ return message_;
+ }
+
+ void setMessage(const ByteArray& message) {
+ message_ = message;
+ }
+
+ const String& getMechanism() const {
+ return mechanism_;
+ }
+
+ void setMechanism(const String& mechanism) {
+ mechanism_ = mechanism;
+ }
+
+ private:
+ String mechanism_;
+ ByteArray message_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/AuthSuccess.h b/Swiften/Elements/AuthSuccess.h
new file mode 100644
index 0000000..f63d0a8
--- /dev/null
+++ b/Swiften/Elements/AuthSuccess.h
@@ -0,0 +1,13 @@
+#ifndef SWIFTEN_AuthSuccess_H
+#define SWIFTEN_AuthSuccess_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class AuthSuccess : public Element {
+ public:
+ AuthSuccess() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Body.h b/Swiften/Elements/Body.h
new file mode 100644
index 0000000..d78cecf
--- /dev/null
+++ b/Swiften/Elements/Body.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_Body_H
+#define SWIFTEN_Body_H
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class Body : public Payload {
+ public:
+ Body(const String& text = "") : text_(text) {
+ }
+
+ void setText(const String& text) {
+ text_ = text;
+ }
+
+ const String& getText() const {
+ return text_;
+ }
+
+ private:
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/CapsInfo.h b/Swiften/Elements/CapsInfo.h
new file mode 100644
index 0000000..4b478aa
--- /dev/null
+++ b/Swiften/Elements/CapsInfo.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_CapsInfo_H
+#define SWIFTEN_CapsInfo_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class CapsInfo : public Payload {
+ public:
+ CapsInfo(const String& node, const String& version, const String& hash = "sha-1") : node_(node), version_(version), hash_(hash) {}
+
+ const String& getNode() const { return node_; }
+ const String& getVersion() const { return version_; }
+ const String& getHash() const { return hash_; }
+
+ private:
+ String node_;
+ String version_;
+ String hash_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/CompressFailure.h b/Swiften/Elements/CompressFailure.h
new file mode 100644
index 0000000..880f71a
--- /dev/null
+++ b/Swiften/Elements/CompressFailure.h
@@ -0,0 +1,13 @@
+#ifndef SWIFTEN_CompressFailure_H
+#define SWIFTEN_CompressFailure_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class CompressFailure : public Element {
+ public:
+ CompressFailure() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/CompressRequest.h b/Swiften/Elements/CompressRequest.h
new file mode 100644
index 0000000..53c0805
--- /dev/null
+++ b/Swiften/Elements/CompressRequest.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_CompressRequest_H
+#define SWIFTEN_CompressRequest_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class CompressRequest : public Element
+ {
+ public:
+ CompressRequest(const String& method = "") : method_(method) {}
+
+ const String& getMethod() const {
+ return method_;
+ }
+
+ void setMethod(const String& method) {
+ method_ = method;
+ }
+
+ private:
+ String method_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Compressed.h b/Swiften/Elements/Compressed.h
new file mode 100644
index 0000000..37113d8
--- /dev/null
+++ b/Swiften/Elements/Compressed.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_COMPRESSED_H
+#define SWIFTEN_COMPRESSED_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class Compressed : public Element
+ {
+ public:
+ Compressed() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp
new file mode 100644
index 0000000..63ee051
--- /dev/null
+++ b/Swiften/Elements/DiscoInfo.cpp
@@ -0,0 +1,25 @@
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+
+bool DiscoInfo::Identity::operator<(const Identity& other) const {
+ if (category_ == other.category_) {
+ if (type_ == other.type_) {
+ if (lang_ == other.lang_) {
+ return name_ < other.name_;
+ }
+ else {
+ return lang_ < other.lang_;
+ }
+ }
+ else {
+ return type_ < other.type_;
+ }
+ }
+ else {
+ return category_ < other.category_;
+ }
+}
+
+const std::string DiscoInfo::SecurityLabels = "urn:xmpp:sec-label:0";
+}
diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h
new file mode 100644
index 0000000..8caeaf9
--- /dev/null
+++ b/Swiften/Elements/DiscoInfo.h
@@ -0,0 +1,83 @@
+#ifndef SWIFTEN_DiscoInfo_H
+#define SWIFTEN_DiscoInfo_H
+
+#include <vector>
+#include <algorithm>
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class DiscoInfo : public Payload {
+ public:
+ const static std::string SecurityLabels;
+ class Identity {
+ public:
+ Identity(const String& name, const String& category = "client", const String& type = "pc", const String& lang = "") : name_(name), category_(category), type_(type), lang_(lang) {
+ }
+
+ const String& getCategory() const {
+ return category_;
+ }
+
+ const String& getType() const {
+ return type_;
+ }
+
+ const String& getLanguage() const {
+ return lang_;
+ }
+
+ const String& getName() const {
+ return name_;
+ }
+
+ // Sorted according to XEP-115 rules
+ bool operator<(const Identity& other) const;
+
+ private:
+ String name_;
+ String category_;
+ String type_;
+ String lang_;
+ };
+
+ DiscoInfo() {
+ }
+
+ const String& getNode() const {
+ return node_;
+ }
+
+ void setNode(const String& node) {
+ node_ = node;
+ }
+
+ const std::vector<Identity> getIdentities() const {
+ return identities_;
+ }
+
+ void addIdentity(const Identity& identity) {
+ identities_.push_back(identity);
+ }
+
+ const std::vector<String>& getFeatures() const {
+ return features_;
+ }
+
+ void addFeature(const String& feature) {
+ features_.push_back(feature);
+ }
+
+ bool hasFeature(const String& feature) const {
+ return std::find(features_.begin(), features_.end(), feature) != features_.end();
+ }
+
+ private:
+ String node_;
+ std::vector<Identity> identities_;
+ std::vector<String> features_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Element.cpp b/Swiften/Elements/Element.cpp
new file mode 100644
index 0000000..a62aad9
--- /dev/null
+++ b/Swiften/Elements/Element.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+
+Element::~Element() {
+}
+
+}
diff --git a/Swiften/Elements/Element.h b/Swiften/Elements/Element.h
new file mode 100644
index 0000000..d1e9c6a
--- /dev/null
+++ b/Swiften/Elements/Element.h
@@ -0,0 +1,11 @@
+#ifndef SWIFTEN_ELEMENT_H
+#define SWIFTEN_ELEMENT_H
+
+namespace Swift {
+ class Element {
+ public:
+ virtual ~Element();
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Error.h b/Swiften/Elements/Error.h
new file mode 100644
index 0000000..8793f35
--- /dev/null
+++ b/Swiften/Elements/Error.h
@@ -0,0 +1,70 @@
+#ifndef SWIFTEN_Error_H
+#define SWIFTEN_Error_H
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class Error : public Payload {
+ public:
+ enum Type { Cancel, Continue, Modify, Auth, Wait };
+
+ enum Condition {
+ BadRequest,
+ Conflict,
+ FeatureNotImplemented,
+ Forbidden,
+ Gone,
+ InternalServerError,
+ ItemNotFound,
+ JIDMalformed,
+ NotAcceptable,
+ NotAllowed,
+ NotAuthorized,
+ PaymentRequired,
+ RecipientUnavailable,
+ Redirect,
+ RegistrationRequired,
+ RemoteServerNotFound,
+ RemoteServerTimeout,
+ ResourceConstraint,
+ ServiceUnavailable,
+ SubscriptionRequired,
+ UndefinedCondition,
+ UnexpectedRequest
+ };
+
+ Error(Condition condition = UndefinedCondition, Type type = Cancel, const String& text = String()) : type_(type), condition_(condition), text_(text) { }
+
+ Type getType() const {
+ return type_;
+ }
+
+ void setType(Type type) {
+ type_ = type;
+ }
+
+ Condition getCondition() const {
+ return condition_;
+ }
+
+ void setCondition(Condition condition) {
+ condition_ = condition;
+ }
+
+ void setText(const String& text) {
+ text_ = text;
+ }
+
+ const String& getText() const {
+ return text_;
+ }
+
+ private:
+ Type type_;
+ Condition condition_;
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/IQ.cpp b/Swiften/Elements/IQ.cpp
new file mode 100644
index 0000000..51f4745
--- /dev/null
+++ b/Swiften/Elements/IQ.cpp
@@ -0,0 +1,37 @@
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+
+boost::shared_ptr<IQ> IQ::createRequest(
+ Type type, const JID& to, const String& id, boost::shared_ptr<Payload> payload) {
+ boost::shared_ptr<IQ> iq(new IQ(type));
+ if (to.isValid()) {
+ iq->setTo(to);
+ }
+ iq->setID(id);
+ if (payload) {
+ iq->addPayload(payload);
+ }
+ return iq;
+}
+
+boost::shared_ptr<IQ> IQ::createResult(
+ const JID& to, const String& id, boost::shared_ptr<Payload> payload) {
+ boost::shared_ptr<IQ> iq(new IQ(Result));
+ iq->setTo(to);
+ iq->setID(id);
+ if (payload) {
+ iq->addPayload(payload);
+ }
+ return iq;
+}
+
+boost::shared_ptr<IQ> IQ::createError(const JID& to, const String& id, Error::Condition condition, Error::Type type) {
+ boost::shared_ptr<IQ> iq(new IQ(IQ::Error));
+ iq->setTo(to);
+ iq->setID(id);
+ iq->addPayload(boost::shared_ptr<Swift::Error>(new Swift::Error(condition, type)));
+ return iq;
+}
+
+}
diff --git a/Swiften/Elements/IQ.h b/Swiften/Elements/IQ.h
new file mode 100644
index 0000000..231439f
--- /dev/null
+++ b/Swiften/Elements/IQ.h
@@ -0,0 +1,39 @@
+#ifndef SWIFTEN_IQ_H
+#define SWIFTEN_IQ_H
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Elements/Error.h"
+
+namespace Swift
+{
+ class IQ : public Stanza
+ {
+ public:
+ enum Type { Get, Set, Result, Error };
+
+ IQ(Type type = Get) : type_(type) { }
+
+ Type getType() const { return type_; }
+ void setType(Type type) { type_ = type; }
+
+ static boost::shared_ptr<IQ> createRequest(
+ Type type,
+ const JID& to,
+ const String& id,
+ boost::shared_ptr<Payload> payload);
+ static boost::shared_ptr<IQ> createResult(
+ const JID& to,
+ const String& id,
+ boost::shared_ptr<Payload> payload = boost::shared_ptr<Payload>());
+ static boost::shared_ptr<IQ> createError(
+ const JID& to,
+ const String& id,
+ Error::Condition condition,
+ Error::Type type);
+
+ private:
+ Type type_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/MUCPayload.cpp b/Swiften/Elements/MUCPayload.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swiften/Elements/MUCPayload.cpp
diff --git a/Swiften/Elements/MUCPayload.h b/Swiften/Elements/MUCPayload.h
new file mode 100644
index 0000000..205ae46
--- /dev/null
+++ b/Swiften/Elements/MUCPayload.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_MUCPayload_H
+#define SWIFTEN_MUCPayload_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class MUCPayload : public Payload
+ {
+ public:
+ MUCPayload() { }
+
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Makefile.inc b/Swiften/Elements/Makefile.inc
new file mode 100644
index 0000000..aceaac0
--- /dev/null
+++ b/Swiften/Elements/Makefile.inc
@@ -0,0 +1,9 @@
+SWIFTEN_SOURCES += \
+ Swiften/Elements/RosterPayload.cpp \
+ Swiften/Elements/Payload.cpp \
+ Swiften/Elements/Stanza.cpp \
+ Swiften/Elements/Element.cpp \
+ Swiften/Elements/DiscoInfo.cpp \
+ Swiften/Elements/IQ.cpp
+
+include Swiften/Elements/UnitTest/Makefile.inc
diff --git a/Swiften/Elements/Message.h b/Swiften/Elements/Message.h
new file mode 100644
index 0000000..a49f496
--- /dev/null
+++ b/Swiften/Elements/Message.h
@@ -0,0 +1,46 @@
+#ifndef SWIFTEN_STANZAS_MESSAGE_H
+#define SWIFTEN_STANZAS_MESSAGE_H
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Body.h"
+#include "Swiften/Elements/Error.h"
+#include "Swiften/Elements/Stanza.h"
+
+namespace Swift
+{
+ class Message : public Stanza
+ {
+ public:
+ enum Type { Normal, Chat, Error, Groupchat, Headline };
+
+ Message() : type_(Chat) { }
+
+ String getBody() const {
+ boost::shared_ptr<Body> body(getPayload<Body>());
+ if (body) {
+ return body->getText();
+ }
+ return "";
+ }
+
+ void setBody(const String& body) {
+ updatePayload(boost::shared_ptr<Body>(new Body(body)));
+ }
+
+ bool isError() {
+ boost::shared_ptr<Swift::Error> error(getPayload<Swift::Error>());
+ return getType() == Message::Error || error.get() != NULL;
+ }
+
+ Type getType() const { return type_; }
+ void setType(Type type) { type_ = type; }
+
+ private:
+ String body_;
+ Type type_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Payload.cpp b/Swiften/Elements/Payload.cpp
new file mode 100644
index 0000000..d929fad
--- /dev/null
+++ b/Swiften/Elements/Payload.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+
+Payload::~Payload() {
+}
+
+}
diff --git a/Swiften/Elements/Payload.h b/Swiften/Elements/Payload.h
new file mode 100644
index 0000000..829dc24
--- /dev/null
+++ b/Swiften/Elements/Payload.h
@@ -0,0 +1,11 @@
+#ifndef SWIFTEN_PAYLOAD_H
+#define SWIFTEN_PAYLOAD_H
+
+namespace Swift {
+ class Payload {
+ public:
+ virtual ~Payload();
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Presence.h b/Swiften/Elements/Presence.h
new file mode 100644
index 0000000..1243bca
--- /dev/null
+++ b/Swiften/Elements/Presence.h
@@ -0,0 +1,58 @@
+#ifndef SWIFTEN_Presence
+#define SWIFTEN_Presence
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Elements/Status.h"
+#include "Swiften/Elements/StatusShow.h"
+#include "Swiften/Elements/Priority.h"
+
+namespace Swift {
+ class Presence : public Stanza
+ {
+ public:
+ enum Type { Available, Error, Probe, Subscribe, Subscribed, Unavailable, Unsubscribe, Unsubscribed };
+
+ Presence() : type_(Available) /*, showType_(Online)*/ {}
+
+ Type getType() const { return type_; }
+ void setType(Type type) { type_ = type; }
+
+ StatusShow::Type getShow() const {
+ boost::shared_ptr<StatusShow> show(getPayload<StatusShow>());
+ if (show) {
+ return show->getType();
+ }
+ return type_ == Available ? StatusShow::Online : StatusShow::None;
+ }
+
+ void setShow(const StatusShow::Type &show) {
+ updatePayload(boost::shared_ptr<StatusShow>(new StatusShow(show)));
+ }
+
+ String getStatus() const {
+ boost::shared_ptr<Status> status(getPayload<Status>());
+ if (status) {
+ return status->getText();
+ }
+ return "";
+ }
+
+ void setStatus(const String& status) {
+ updatePayload(boost::shared_ptr<Status>(new Status(status)));
+ }
+
+ int getPriority() const {
+ boost::shared_ptr<Priority> priority(getPayload<Priority>());
+ return (priority ? priority->getPriority() : 0);
+ }
+
+ void setPriority(int priority) {
+ updatePayload(boost::shared_ptr<Priority>(new Priority(priority)));
+ }
+
+ private:
+ Presence::Type type_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Priority.h b/Swiften/Elements/Priority.h
new file mode 100644
index 0000000..fd6080e
--- /dev/null
+++ b/Swiften/Elements/Priority.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_Priority_H
+#define SWIFTEN_Priority_H
+
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class Priority : public Payload {
+ public:
+ Priority(int priority = 0) : priority_(priority) {
+ }
+
+ void setPriority(int priority) {
+ priority_ = priority;
+ }
+
+ int getPriority() const {
+ return priority_;
+ }
+
+ private:
+ int priority_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/ResourceBind.h b/Swiften/Elements/ResourceBind.h
new file mode 100644
index 0000000..3b6c632
--- /dev/null
+++ b/Swiften/Elements/ResourceBind.h
@@ -0,0 +1,36 @@
+#ifndef SWIFTEN_ResourceBind_H
+#define SWIFTEN_ResourceBind_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class ResourceBind : public Payload
+ {
+ public:
+ ResourceBind() {}
+
+ void setJID(const JID& jid) {
+ jid_ = jid;
+ }
+
+ const JID& getJID() const {
+ return jid_;
+ }
+
+ void setResource(const String& resource) {
+ resource_ = resource;
+ }
+
+ const String& getResource() const {
+ return resource_;
+ }
+
+ private:
+ JID jid_;
+ String resource_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/RosterItemPayload.h b/Swiften/Elements/RosterItemPayload.h
new file mode 100644
index 0000000..3925117
--- /dev/null
+++ b/Swiften/Elements/RosterItemPayload.h
@@ -0,0 +1,43 @@
+#ifndef SWIFTEN_RosterItemPayloadPayload_H
+#define SWIFTEN_RosterItemPayloadPayload_H
+
+#include <vector>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class RosterItemPayload
+ {
+ public:
+ enum Subscription { None, To, From, Both, Remove };
+
+ RosterItemPayload() : subscription_(None), ask_(false) {}
+ RosterItemPayload(const JID& jid, const String& name, Subscription subscription) : jid_(jid), name_(name), subscription_(subscription), ask_(false) { }
+
+ void setJID(const JID& jid) { jid_ = jid; }
+ const JID& getJID() const { return jid_; }
+
+ void setName(const String& name) { name_ = name; }
+ const String& getName() const { return name_; }
+
+ void setSubscription(Subscription subscription) { subscription_ = subscription; }
+ const Subscription& getSubscription() const { return subscription_; }
+
+ void addGroup(const String& group) { groups_.push_back(group); }
+ void setGroups(const std::vector<String>& groups) { groups_ = groups; }
+ const std::vector<String>& getGroups() const { return groups_; }
+
+ void setSubscriptionRequested() { ask_ = true; }
+ bool getSubscriptionRequested() const { return ask_; }
+
+ private:
+ JID jid_;
+ String name_;
+ Subscription subscription_;
+ std::vector<String> groups_;
+ bool ask_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/RosterPayload.cpp b/Swiften/Elements/RosterPayload.cpp
new file mode 100644
index 0000000..6d39264
--- /dev/null
+++ b/Swiften/Elements/RosterPayload.cpp
@@ -0,0 +1,17 @@
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+boost::optional<RosterItemPayload> RosterPayload::getItem(const JID& jid) const {
+ foreach(const RosterItemPayload& item, items_) {
+ // FIXME: MSVC rejects this. Find out why.
+ //if (item.getJID() == jid) {
+ if (item.getJID().compare(jid, JID::WithResource)) {
+ return boost::optional<RosterItemPayload>(item);
+ }
+ }
+ return boost::optional<RosterItemPayload>();
+}
+
+}
diff --git a/Swiften/Elements/RosterPayload.h b/Swiften/Elements/RosterPayload.h
new file mode 100644
index 0000000..afb68c2
--- /dev/null
+++ b/Swiften/Elements/RosterPayload.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_RosterPayload_H
+#define SWIFTEN_RosterPayload_H
+
+#include <vector>
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/RosterItemPayload.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class RosterPayload : public Payload {
+ public:
+ typedef std::vector<RosterItemPayload> RosterItemPayloads;
+
+ public:
+ RosterPayload() {}
+
+ boost::optional<RosterItemPayload> getItem(const JID& jid) const;
+
+ void addItem(const RosterItemPayload& item) {
+ items_.push_back(item);
+ }
+
+ const RosterItemPayloads& getItems() const {
+ return items_;
+ }
+
+ private:
+ RosterItemPayloads items_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/SecurityLabel.h b/Swiften/Elements/SecurityLabel.h
new file mode 100644
index 0000000..65bdb4f
--- /dev/null
+++ b/Swiften/Elements/SecurityLabel.h
@@ -0,0 +1,57 @@
+#ifndef SWIFTEN_SecurityLabel_H
+#define SWIFTEN_SecurityLabel_H
+
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class SecurityLabel : public Payload {
+ public:
+ SecurityLabel() {}
+
+ const String& getDisplayMarking() const { return displayMarking_; }
+
+ void setDisplayMarking(const String& displayMarking) {
+ displayMarking_ = displayMarking;
+ }
+
+ const String& getForegroundColor() const {
+ return foregroundColor_;
+ }
+
+ void setForegroundColor(const String& foregroundColor) {
+ foregroundColor_ = foregroundColor;
+ }
+
+ const String& getBackgroundColor() const {
+ return backgroundColor_;
+ }
+
+ void setBackgroundColor(const String& backgroundColor) {
+ backgroundColor_ = backgroundColor;
+ }
+
+ const String& getLabel() const { return label_; }
+
+ void setLabel(const String& label) {
+ label_ = label;
+ }
+
+ const std::vector<String>& getEquivalentLabels() const { return equivalentLabels_; }
+
+ void addEquivalentLabel(const String& label) {
+ equivalentLabels_.push_back(label);
+ }
+
+ private:
+ String displayMarking_;
+ String foregroundColor_;
+ String backgroundColor_;
+ String label_;
+ std::vector<String> equivalentLabels_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/SecurityLabelsCatalog.h b/Swiften/Elements/SecurityLabelsCatalog.h
new file mode 100644
index 0000000..611c26b
--- /dev/null
+++ b/Swiften/Elements/SecurityLabelsCatalog.h
@@ -0,0 +1,56 @@
+#ifndef SWIFTEN_SecurityLabelsCatalog_H
+#define SWIFTEN_SecurityLabelsCatalog_H
+
+#include <vector>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/SecurityLabel.h"
+
+namespace Swift {
+ class SecurityLabelsCatalog : public Payload {
+ public:
+ SecurityLabelsCatalog(const JID& to = JID()) : to_(to) {}
+
+ const std::vector<SecurityLabel>& getLabels() const {
+ return labels_;
+ }
+
+ void addLabel(const SecurityLabel& label) {
+ labels_.push_back(label);
+ }
+
+ const JID& getTo() const {
+ return to_;
+ }
+
+ void setTo(const JID& to) {
+ to_ = to;
+ }
+
+ const String& getName() const {
+ return name_;
+ }
+
+ void setName(const String& name) {
+ name_ = name;
+ }
+
+ const String& getDescription() const {
+ return description_;
+ }
+
+ void setDescription(const String& description) {
+ description_ = description;
+ }
+
+ private:
+ JID to_;
+ String name_;
+ String description_;
+ std::vector<SecurityLabel> labels_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/SoftwareVersion.h b/Swiften/Elements/SoftwareVersion.h
new file mode 100644
index 0000000..d064414
--- /dev/null
+++ b/Swiften/Elements/SoftwareVersion.h
@@ -0,0 +1,47 @@
+#ifndef SWIFTEN_SoftwareVersion_H
+#define SWIFTEN_SoftwareVersion_H
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class SoftwareVersion : public Payload {
+ public:
+ SoftwareVersion(
+ const String& name = "",
+ const String& version = "",
+ const String& os = "") :
+ name_(name), version_(version), os_(os) {}
+
+ const String& getName() const {
+ return name_;
+ }
+
+ void setName(const String& name) {
+ name_ = name;
+ }
+
+ const String& getVersion() const {
+ return version_;
+ }
+
+ void setVersion(const String& version) {
+ version_ = version;
+ }
+
+ const String& getOS() const {
+ return os_;
+ }
+
+ void setOS(const String& os) {
+ os_ = os;
+ }
+
+ private:
+ String name_;
+ String version_;
+ String os_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Stanza.cpp b/Swiften/Elements/Stanza.cpp
new file mode 100644
index 0000000..e644665
--- /dev/null
+++ b/Swiften/Elements/Stanza.cpp
@@ -0,0 +1,31 @@
+#include "Swiften/Elements/Stanza.h"
+
+#include <typeinfo>
+
+namespace Swift {
+
+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);
+}
+
+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>();
+}
+
+
+}
diff --git a/Swiften/Elements/Stanza.h b/Swiften/Elements/Stanza.h
new file mode 100644
index 0000000..e60ab21
--- /dev/null
+++ b/Swiften/Elements/Stanza.h
@@ -0,0 +1,60 @@
+#ifndef SWIFTEN_STANZAS_STANZA_H
+#define SWIFTEN_STANZAS_STANZA_H
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class Stanza : public Element {
+ public:
+ virtual ~Stanza();
+
+ template<typename T>
+ boost::shared_ptr<T> getPayload() const {
+ foreach (const boost::shared_ptr<Payload>& i, payloads_) {
+ boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(i));
+ if (result) {
+ return result;
+ }
+ }
+ return boost::shared_ptr<T>();
+ }
+
+ const std::vector< boost::shared_ptr<Payload> >& getPayloads() const {
+ return payloads_;
+ }
+
+ void addPayload(boost::shared_ptr<Payload> payload) {
+ payloads_.push_back(payload);
+ }
+
+ void updatePayload(boost::shared_ptr<Payload> 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 String& getID() const { return id_; }
+ void setID(const String& id) { id_ = id; }
+
+ private:
+ String id_;
+ JID from_;
+ JID to_;
+
+ typedef std::vector< boost::shared_ptr<Payload> > Payloads;
+ Payloads payloads_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/StartSession.h b/Swiften/Elements/StartSession.h
new file mode 100644
index 0000000..2b46d05
--- /dev/null
+++ b/Swiften/Elements/StartSession.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StartSession_H
+#define SWIFTEN_StartSession_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class StartSession : public Payload {
+ public:
+ StartSession() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/StartTLSFailure.h b/Swiften/Elements/StartTLSFailure.h
new file mode 100644
index 0000000..17a1750
--- /dev/null
+++ b/Swiften/Elements/StartTLSFailure.h
@@ -0,0 +1,13 @@
+#ifndef SWIFTEN_StartTLSFailure_H
+#define SWIFTEN_StartTLSFailure_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class StartTLSFailure : public Element {
+ public:
+ StartTLSFailure() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/StartTLSRequest.h b/Swiften/Elements/StartTLSRequest.h
new file mode 100644
index 0000000..c40499a
--- /dev/null
+++ b/Swiften/Elements/StartTLSRequest.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StartTLSRequest_H
+#define SWIFTEN_StartTLSRequest_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class StartTLSRequest : public Element
+ {
+ public:
+ StartTLSRequest() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Status.h b/Swiften/Elements/Status.h
new file mode 100644
index 0000000..0b80682
--- /dev/null
+++ b/Swiften/Elements/Status.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_Status_H
+#define SWIFTEN_Status_H
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class Status : public Payload {
+ public:
+ Status(const String& text = "") : text_(text) {
+ }
+
+ void setText(const String& text) {
+ text_ = text;
+ }
+
+ const String& getText() const {
+ return text_;
+ }
+
+ private:
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/StatusShow.h b/Swiften/Elements/StatusShow.h
new file mode 100644
index 0000000..a001657
--- /dev/null
+++ b/Swiften/Elements/StatusShow.h
@@ -0,0 +1,27 @@
+#ifndef SWIFTEN_StatusShow_H
+#define SWIFTEN_StatusShow_H
+
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class StatusShow : public Payload {
+ public:
+ enum Type { Online, Away, FFC, XA, DND, None };
+
+ StatusShow(const Type& type = Online) : type_(type) {
+ }
+
+ void setType(const Type& type) {
+ type_ = type;
+ }
+
+ const Type& getType() const {
+ return type_;
+ }
+
+ private:
+ Type type_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/StreamFeatures.h b/Swiften/Elements/StreamFeatures.h
new file mode 100644
index 0000000..2d5f4d6
--- /dev/null
+++ b/Swiften/Elements/StreamFeatures.h
@@ -0,0 +1,77 @@
+#ifndef SWIFTEN_StreamFeatures_H
+#define SWIFTEN_StreamFeatures_H
+
+#include <vector>
+#include <algorithm>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class StreamFeatures : public Element
+ {
+ public:
+ StreamFeatures() : hasStartTLS_(false), hasResourceBind_(false), hasSession_(false) {}
+
+ void setHasStartTLS() {
+ hasStartTLS_ = true;
+ }
+
+ bool hasStartTLS() const {
+ return hasStartTLS_;
+ }
+
+ void setHasSession() {
+ hasSession_ = true;
+ }
+
+ bool hasSession() const {
+ return hasSession_;
+ }
+
+ void setHasResourceBind() {
+ hasResourceBind_ = true;
+ }
+
+ bool hasResourceBind() const {
+ return hasResourceBind_;
+ }
+
+ const std::vector<String>& getCompressionMethods() const {
+ return compressionMethods_;
+ }
+
+ void addCompressionMethod(const String& mechanism) {
+ compressionMethods_.push_back(mechanism);
+ }
+
+ bool hasCompressionMethod(const String& mechanism) const {
+ return std::find(compressionMethods_.begin(), compressionMethods_.end(), mechanism) != compressionMethods_.end();
+ }
+
+ const std::vector<String>& getAuthenticationMechanisms() const {
+ return authenticationMechanisms_;
+ }
+
+ void addAuthenticationMechanism(const String& mechanism) {
+ authenticationMechanisms_.push_back(mechanism);
+ }
+
+ bool hasAuthenticationMechanism(const String& mechanism) const {
+ return std::find(authenticationMechanisms_.begin(), authenticationMechanisms_.end(), mechanism) != authenticationMechanisms_.end();
+ }
+
+ bool hasAuthenticationMechanisms() const {
+ return !authenticationMechanisms_.empty();
+ }
+
+ private:
+ bool hasStartTLS_;
+ std::vector<String> compressionMethods_;
+ std::vector<String> authenticationMechanisms_;
+ bool hasResourceBind_;
+ bool hasSession_;
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/TLSProceed.h b/Swiften/Elements/TLSProceed.h
new file mode 100644
index 0000000..41f0341
--- /dev/null
+++ b/Swiften/Elements/TLSProceed.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_TLSProceed_H
+#define SWIFTEN_TLSProceed_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class TLSProceed : public Element
+ {
+ public:
+ TLSProceed() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/UnitTest/IQTest.cpp b/Swiften/Elements/UnitTest/IQTest.cpp
new file mode 100644
index 0000000..bc22c81
--- /dev/null
+++ b/Swiften/Elements/UnitTest/IQTest.cpp
@@ -0,0 +1,52 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/SoftwareVersion.h"
+
+using namespace Swift;
+
+class IQTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(IQTest);
+ CPPUNIT_TEST(testCreateResult);
+ CPPUNIT_TEST(testCreateResult_WithoutPayload);
+ CPPUNIT_TEST(testCreateError);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ IQTest() {}
+
+ void testCreateResult() {
+ boost::shared_ptr<Payload> payload(new SoftwareVersion("myclient"));
+ boost::shared_ptr<IQ> iq(IQ::createResult(JID("foo@bar/fum"), "myid", payload));
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar/fum"), iq->getTo());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), iq->getID());
+ CPPUNIT_ASSERT(iq->getPayload<SoftwareVersion>());
+ CPPUNIT_ASSERT(payload == iq->getPayload<SoftwareVersion>());
+ }
+
+ void testCreateResult_WithoutPayload() {
+ boost::shared_ptr<IQ> iq(IQ::createResult(JID("foo@bar/fum"), "myid"));
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar/fum"), iq->getTo());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), iq->getID());
+ CPPUNIT_ASSERT(!iq->getPayload<SoftwareVersion>());
+ }
+
+ void testCreateError() {
+ boost::shared_ptr<IQ> iq(IQ::createError(JID("foo@bar/fum"), "myid", Error::BadRequest, Error::Modify));
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar/fum"), iq->getTo());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), iq->getID());
+ boost::shared_ptr<Error> error(iq->getPayload<Error>());
+ CPPUNIT_ASSERT(error);
+ CPPUNIT_ASSERT_EQUAL(Error::BadRequest, error->getCondition());
+ CPPUNIT_ASSERT_EQUAL(Error::Modify, error->getType());
+ }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IQTest);
diff --git a/Swiften/Elements/UnitTest/Makefile.inc b/Swiften/Elements/UnitTest/Makefile.inc
new file mode 100644
index 0000000..848aa19
--- /dev/null
+++ b/Swiften/Elements/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/Elements/UnitTest/StanzaTest.cpp \
+ Swiften/Elements/UnitTest/IQTest.cpp
diff --git a/Swiften/Elements/UnitTest/StanzaTest.cpp b/Swiften/Elements/UnitTest/StanzaTest.cpp
new file mode 100644
index 0000000..b905957
--- /dev/null
+++ b/Swiften/Elements/UnitTest/StanzaTest.cpp
@@ -0,0 +1,155 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/Message.h"
+
+using namespace Swift;
+
+class StanzaTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StanzaTest);
+ CPPUNIT_TEST(testConstructor_Copy);
+ CPPUNIT_TEST(testGetPayload);
+ CPPUNIT_TEST(testGetPayload_NoSuchPayload);
+ CPPUNIT_TEST(testDestructor);
+ CPPUNIT_TEST(testDestructor_Copy);
+ CPPUNIT_TEST(testUpdatePayload_ExistingPayload);
+ CPPUNIT_TEST(testUpdatePayload_NewPayload);
+ CPPUNIT_TEST(testGetPayloadOfSameType);
+ CPPUNIT_TEST(testGetPayloadOfSameType_NoSuchPayload);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ class MyPayload1 : public Payload {
+ public:
+ MyPayload1() {}
+ };
+
+ class MyPayload2 : public Payload {
+ public:
+ MyPayload2(const String& s = "") : text_(s) {}
+
+ String text_;
+ };
+
+ class MyPayload3 : public Payload {
+ public:
+ MyPayload3() {}
+ };
+
+ class DestroyingPayload : public Payload {
+ public:
+ DestroyingPayload(bool* alive) : alive_(alive) {
+ }
+
+ ~DestroyingPayload() {
+ (*alive_) = false;
+ }
+
+ private:
+ bool* alive_;
+ };
+
+ StanzaTest() {}
+
+ void testConstructor_Copy() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload2>(new MyPayload2()));
+ Message copy(m);
+
+ CPPUNIT_ASSERT(copy.getPayload<MyPayload1>());
+ CPPUNIT_ASSERT(copy.getPayload<MyPayload2>());
+ }
+
+ void testDestructor() {
+ bool payloadAlive = true;
+ {
+ Message m;
+ m.addPayload(boost::shared_ptr<DestroyingPayload>(new DestroyingPayload(&payloadAlive)));
+ }
+
+ CPPUNIT_ASSERT(!payloadAlive);
+ }
+
+ void testDestructor_Copy() {
+ bool payloadAlive = true;
+ Message* m1 = new Message();
+ m1->addPayload(boost::shared_ptr<DestroyingPayload>(new DestroyingPayload(&payloadAlive)));
+ Message* m2 = new Message(*m1);
+
+ delete m1;
+ CPPUNIT_ASSERT(payloadAlive);
+
+ delete m2;
+ CPPUNIT_ASSERT(!payloadAlive);
+ }
+
+ void testGetPayload() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload2>(new MyPayload2()));
+ m.addPayload(boost::shared_ptr<MyPayload3>(new MyPayload3()));
+
+ boost::shared_ptr<MyPayload2> p(m.getPayload<MyPayload2>());
+ CPPUNIT_ASSERT(p);
+ }
+
+ void testGetPayload_NoSuchPayload() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload3>(new MyPayload3()));
+
+ boost::shared_ptr<MyPayload2> p(m.getPayload<MyPayload2>());
+ CPPUNIT_ASSERT(!p);
+ }
+
+ void testUpdatePayload_ExistingPayload() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload2>(new MyPayload2("foo")));
+ m.addPayload(boost::shared_ptr<MyPayload3>(new MyPayload3()));
+
+ m.updatePayload(boost::shared_ptr<MyPayload2>(new MyPayload2("bar")));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), m.getPayloads().size());
+ boost::shared_ptr<MyPayload2> p(m.getPayload<MyPayload2>());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), p->text_);
+ }
+
+ void testUpdatePayload_NewPayload() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload3>(new MyPayload3()));
+
+ m.updatePayload(boost::shared_ptr<MyPayload2>(new MyPayload2("bar")));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), m.getPayloads().size());
+ boost::shared_ptr<MyPayload2> p(m.getPayload<MyPayload2>());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), p->text_);
+ }
+
+ void testGetPayloadOfSameType() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload2>(new MyPayload2("foo")));
+ m.addPayload(boost::shared_ptr<MyPayload3>(new MyPayload3()));
+
+ boost::shared_ptr<MyPayload2> payload(boost::dynamic_pointer_cast<MyPayload2>(m.getPayloadOfSameType(boost::shared_ptr<MyPayload2>(new MyPayload2("bar")))));
+ CPPUNIT_ASSERT(payload);
+ CPPUNIT_ASSERT_EQUAL(String("foo"), payload->text_);
+ }
+
+ void testGetPayloadOfSameType_NoSuchPayload() {
+ Message m;
+ m.addPayload(boost::shared_ptr<MyPayload1>(new MyPayload1()));
+ m.addPayload(boost::shared_ptr<MyPayload3>(new MyPayload3()));
+
+ CPPUNIT_ASSERT(!m.getPayloadOfSameType(boost::shared_ptr<MyPayload2>(new MyPayload2("bar"))));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StanzaTest);
diff --git a/Swiften/Elements/UnitTest/StanzasTest.cpp b/Swiften/Elements/UnitTest/StanzasTest.cpp
new file mode 100644
index 0000000..35b84e7
--- /dev/null
+++ b/Swiften/Elements/UnitTest/StanzasTest.cpp
@@ -0,0 +1,3 @@
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Presence.h"
diff --git a/Swiften/Elements/UnknownElement.h b/Swiften/Elements/UnknownElement.h
new file mode 100644
index 0000000..a2ae406
--- /dev/null
+++ b/Swiften/Elements/UnknownElement.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_UnknownElement_H
+#define SWIFTEN_UnknownElement_H
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class UnknownElement : public Element
+ {
+ public:
+ UnknownElement() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Elements/Version.h b/Swiften/Elements/Version.h
new file mode 100644
index 0000000..327178e
--- /dev/null
+++ b/Swiften/Elements/Version.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_STANZAS_VERSION_H
+#define SWIFTEN_STANZAS_VERSION_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class Version : public Payload
+ {
+ public:
+ Version(const String& name = "", const String& version = "", const String& os = "") : name_(name), version_(version), os_(os) { }
+
+ const String& getName() const { return name_; }
+ const String& getVersion() const { return version_; }
+ const String& getOS() const { return os_; }
+
+ private:
+ String name_;
+ String version_;
+ String os_;
+ };
+}
+
+#endif
diff --git a/Swiften/EventLoop/Deleter.h b/Swiften/EventLoop/Deleter.h
new file mode 100644
index 0000000..217a17f
--- /dev/null
+++ b/Swiften/EventLoop/Deleter.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_Deleter_H
+#define SWIFTEN_Deleter_H
+
+#include <cassert>
+
+namespace Swift {
+ template<typename T>
+ class Deleter {
+ public:
+ Deleter(T* object) : object_(object) {
+ }
+
+ void operator()() {
+ assert(object_);
+ delete object_;
+ object_ = 0;
+ }
+
+ private:
+ T* object_;
+ };
+}
+#endif
diff --git a/Swiften/EventLoop/DummyEventLoop.h b/Swiften/EventLoop/DummyEventLoop.h
new file mode 100644
index 0000000..234ecfa
--- /dev/null
+++ b/Swiften/EventLoop/DummyEventLoop.h
@@ -0,0 +1,37 @@
+#ifndef SWIFTEN_DummyEventLoop_H
+#define SWIFTEN_DummyEventLoop_H
+
+#include <deque>
+#include <boost/function.hpp>
+
+#include "Swiften/EventLoop/EventLoop.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+ class DummyEventLoop : public EventLoop {
+ public:
+ DummyEventLoop() {
+ }
+
+ void processEvents() {
+ while (!events_.empty()) {
+ handleEvent(events_[0]);
+ events_.pop_front();
+ }
+ }
+
+ bool hasEvents() {
+ return events_.size() > 0;
+ }
+
+ virtual void post(const Event& event) {
+ events_.push_back(event);
+ }
+
+ private:
+ std::deque<Event> events_;
+ };
+}
+
+#endif
+
diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp
new file mode 100644
index 0000000..cec149c
--- /dev/null
+++ b/Swiften/EventLoop/EventLoop.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/EventLoop/EventLoop.h"
+
+#include <algorithm>
+#include <boost/bind.hpp>
+
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+EventLoop::EventLoop() : nextEventID_(0) {
+ MainEventLoop::setInstance(this);
+}
+
+EventLoop::~EventLoop() {
+ MainEventLoop::resetInstance();
+}
+
+void EventLoop::handleEvent(const Event& event) {
+ bool doCallback = false;
+ {
+ boost::lock_guard<boost::mutex> lock(eventsMutex_);
+ std::list<Event>::iterator i = std::find(events_.begin(), events_.end(), event);
+ if (i != events_.end()) {
+ doCallback = true;
+ events_.erase(i);
+ }
+ }
+ if (doCallback) {
+ event.callback();
+ }
+}
+
+void EventLoop::postEvent(boost::function<void ()> callback, void* owner) {
+ Event event(owner, callback);
+ {
+ boost::lock_guard<boost::mutex> lock(eventsMutex_);
+ event.id = nextEventID_;
+ nextEventID_++;
+ events_.push_back(event);
+ }
+ post(event);
+}
+
+void EventLoop::removeEventsFromOwner(void* owner) {
+ boost::lock_guard<boost::mutex> lock(eventsMutex_);
+ events_.remove_if(HasOwner(owner));
+}
+
+}
diff --git a/Swiften/EventLoop/EventLoop.h b/Swiften/EventLoop/EventLoop.h
new file mode 100644
index 0000000..2f04f32
--- /dev/null
+++ b/Swiften/EventLoop/EventLoop.h
@@ -0,0 +1,52 @@
+#ifndef SWIFTEN_EventLoop_H
+#define SWIFTEN_EventLoop_H
+
+#include <boost/function.hpp>
+#include <boost/thread/mutex.hpp>
+#include <list>
+
+namespace Swift {
+ class EventLoop {
+ public:
+ EventLoop();
+ virtual ~EventLoop();
+
+ void postEvent(boost::function<void ()> event, void* owner);
+ void removeEventsFromOwner(void* owner);
+
+ protected:
+ struct Event {
+ Event(void* owner, const boost::function<void()>& callback) :
+ owner(owner), callback(callback) {
+ }
+
+ bool operator==(const Event& o) const {
+ return o.id == id;
+ }
+
+ unsigned int id;
+ void* owner;
+ boost::function<void()> callback;
+ };
+
+ /**
+ * Reimplement this to call handleEvent(event) from the thread in which
+ * the event loop is residing.
+ */
+ virtual void post(const Event& event) = 0;
+
+ void handleEvent(const Event& event);
+
+ private:
+ struct HasOwner {
+ HasOwner(void* owner) : owner(owner) {}
+ bool operator()(const Event& event) { return event.owner == owner; }
+ void* owner;
+ };
+ boost::mutex eventsMutex_;
+ unsigned int nextEventID_;
+ std::list<Event> events_;
+ };
+}
+
+#endif
diff --git a/Swiften/EventLoop/MainEventLoop.cpp b/Swiften/EventLoop/MainEventLoop.cpp
new file mode 100644
index 0000000..afaab42
--- /dev/null
+++ b/Swiften/EventLoop/MainEventLoop.cpp
@@ -0,0 +1,35 @@
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+#include <iostream>
+
+namespace Swift {
+
+EventLoop* MainEventLoop::getInstance() {
+ if (!instance_) {
+ std::cerr << "No main event loop instantiated. Please instantiate the appropriate subclass of EventLoop (e.g. SimpleEventLoop, QtEventLoop) at the start of your application." << std::endl;
+ exit(-1);
+ }
+ return instance_;
+}
+
+void MainEventLoop::setInstance(EventLoop* loop) {
+ assert(!instance_);
+ instance_ = loop;
+}
+
+void MainEventLoop::resetInstance() {
+ assert(instance_);
+ instance_ = 0;
+}
+
+void MainEventLoop::postEvent(boost::function<void ()> event, void* owner) {
+ getInstance()->postEvent(event, owner);
+}
+
+void MainEventLoop::removeEventsFromOwner(void* owner) {
+ getInstance()->removeEventsFromOwner(owner);
+}
+
+EventLoop* MainEventLoop::instance_ = 0;
+
+}
diff --git a/Swiften/EventLoop/MainEventLoop.h b/Swiften/EventLoop/MainEventLoop.h
new file mode 100644
index 0000000..f29dbd4
--- /dev/null
+++ b/Swiften/EventLoop/MainEventLoop.h
@@ -0,0 +1,40 @@
+#ifndef SWIFTEN_MainEventLoop_H
+#define SWIFTEN_MainEventLoop_H
+
+#include <boost/function.hpp>
+
+#include "Swiften/EventLoop/Deleter.h"
+#include "Swiften/EventLoop/EventLoop.h"
+
+namespace Swift {
+ class EventLoop;
+
+ class MainEventLoop {
+ friend class EventLoop;
+
+ public:
+ /**
+ * Post an event from the given owner to the event loop.
+ * If the owner is destroyed, all events should be removed from the
+ * loop using removeEventsFromOwner().
+ */
+ static void postEvent(boost::function<void ()> event, void* owner = 0);
+
+ static void removeEventsFromOwner(void* owner);
+
+ template<typename T>
+ static void deleteLater(T* t) {
+ getInstance()->postEvent(Deleter<T>(t), 0);
+ }
+
+ private:
+ static void setInstance(EventLoop*);
+ static void resetInstance();
+ static EventLoop* getInstance();
+
+ private:
+ static EventLoop* instance_;
+ };
+}
+
+#endif
diff --git a/Swiften/EventLoop/Makefile.inc b/Swiften/EventLoop/Makefile.inc
new file mode 100644
index 0000000..894b18d
--- /dev/null
+++ b/Swiften/EventLoop/Makefile.inc
@@ -0,0 +1,6 @@
+SWIFTEN_SOURCES += \
+ Swiften/EventLoop/EventLoop.cpp \
+ Swiften/EventLoop/SimpleEventLoop.cpp \
+ Swiften/EventLoop/MainEventLoop.cpp
+
+include Swiften/EventLoop/UnitTest/Makefile.inc
diff --git a/Swiften/EventLoop/SimpleEventLoop.cpp b/Swiften/EventLoop/SimpleEventLoop.cpp
new file mode 100644
index 0000000..96ad774
--- /dev/null
+++ b/Swiften/EventLoop/SimpleEventLoop.cpp
@@ -0,0 +1,48 @@
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+
+
+namespace Swift {
+
+void nop() {}
+
+SimpleEventLoop::SimpleEventLoop() : isRunning_(true) {
+}
+
+void SimpleEventLoop::run() {
+ while (isRunning_) {
+ std::vector<Event> events;
+ {
+ boost::unique_lock<boost::mutex> lock(eventsMutex_);
+ while (events_.size() == 0) {
+ eventsAvailable_.wait(lock);
+ }
+ events.swap(events_);
+ }
+ foreach(const Event& event, events) {
+ handleEvent(event);
+ }
+ }
+}
+
+void SimpleEventLoop::stop() {
+ postEvent(boost::bind(&SimpleEventLoop::doStop, this), 0);
+}
+
+void SimpleEventLoop::doStop() {
+ isRunning_ = false;
+}
+
+void SimpleEventLoop::post(const Event& event) {
+ {
+ boost::lock_guard<boost::mutex> lock(eventsMutex_);
+ events_.push_back(event);
+ }
+ eventsAvailable_.notify_one();
+}
+
+
+}
diff --git a/Swiften/EventLoop/SimpleEventLoop.h b/Swiften/EventLoop/SimpleEventLoop.h
new file mode 100644
index 0000000..45eaae1
--- /dev/null
+++ b/Swiften/EventLoop/SimpleEventLoop.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_SimpleEventLoop_H
+#define SWIFTEN_SimpleEventLoop_H
+
+#include <vector>
+#include <boost/function.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include "Swiften/EventLoop/EventLoop.h"
+
+namespace Swift {
+ class SimpleEventLoop : public EventLoop {
+ public:
+ SimpleEventLoop();
+
+ void run();
+ void stop();
+
+ virtual void post(const Event& event);
+
+ private:
+ void doStop();
+
+ private:
+ bool isRunning_;
+ std::vector<Event> events_;
+ boost::mutex eventsMutex_;
+ boost::condition_variable eventsAvailable_;
+ };
+}
+#endif
diff --git a/Swiften/EventLoop/UnitTest/EventLoopTest.cpp b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp
new file mode 100644
index 0000000..c64d1ad
--- /dev/null
+++ b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp
@@ -0,0 +1,63 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Base/sleep.h"
+
+using namespace Swift;
+
+class EventLoopTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(EventLoopTest);
+ CPPUNIT_TEST(testPost);
+ CPPUNIT_TEST(testRemove);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ EventLoopTest() {}
+
+ void setUp() {
+ events_.clear();
+ }
+
+ void testPost() {
+ SimpleEventLoop testling;
+
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), 0);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), 0);
+ testling.stop();
+ testling.run();
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size()));
+ CPPUNIT_ASSERT_EQUAL(1, events_[0]);
+ CPPUNIT_ASSERT_EQUAL(2, events_[1]);
+ }
+
+ void testRemove() {
+ SimpleEventLoop testling;
+
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), &testling);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), this);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 3), &testling);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 4), this);
+ testling.removeEventsFromOwner(this);
+ testling.stop();
+ testling.run();
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size()));
+ CPPUNIT_ASSERT_EQUAL(1, events_[0]);
+ CPPUNIT_ASSERT_EQUAL(3, events_[1]);
+ }
+
+ private:
+ void logEvent(int i) {
+ events_.push_back(i);
+ }
+
+ private:
+ std::vector<int> events_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(EventLoopTest);
diff --git a/Swiften/EventLoop/UnitTest/Makefile.inc b/Swiften/EventLoop/UnitTest/Makefile.inc
new file mode 100644
index 0000000..5eec2da
--- /dev/null
+++ b/Swiften/EventLoop/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp \
+ Swiften/EventLoop/UnitTest/EventLoopTest.cpp
diff --git a/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp
new file mode 100644
index 0000000..a39aa9a
--- /dev/null
+++ b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp
@@ -0,0 +1,62 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Base/sleep.h"
+
+using namespace Swift;
+
+class SimpleEventLoopTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SimpleEventLoopTest);
+ CPPUNIT_TEST(testRun);
+ CPPUNIT_TEST(testPostFromMainThread);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SimpleEventLoopTest() {}
+
+ void setUp() {
+ counter_ = 0;
+ }
+
+ void testRun() {
+ SimpleEventLoop testling;
+ boost::thread thread(boost::bind(&SimpleEventLoopTest::runIncrementingThread, this, &testling));
+ testling.run();
+
+ CPPUNIT_ASSERT_EQUAL(10, counter_);
+ }
+
+ void testPostFromMainThread() {
+ SimpleEventLoop testling;
+ testling.postEvent(boost::bind(&SimpleEventLoopTest::incrementCounterAndStop, this, &testling), 0);
+ testling.run();
+
+ CPPUNIT_ASSERT_EQUAL(1, counter_);
+ }
+
+ private:
+ void runIncrementingThread(SimpleEventLoop* loop) {
+ for (unsigned int i = 0; i < 10; ++i) {
+ Swift::sleep(1);
+ loop->postEvent(boost::bind(&SimpleEventLoopTest::incrementCounter, this), 0);
+ }
+ loop->stop();
+ }
+
+ void incrementCounter() {
+ counter_++;
+ }
+
+ void incrementCounterAndStop(SimpleEventLoop* loop) {
+ counter_++;
+ loop->stop();
+ }
+
+ int counter_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SimpleEventLoopTest);
diff --git a/Swiften/Events/Makefile.inc b/Swiften/Events/Makefile.inc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swiften/Events/Makefile.inc
diff --git a/Swiften/Events/MessageEvent.h b/Swiften/Events/MessageEvent.h
new file mode 100644
index 0000000..27eecaf
--- /dev/null
+++ b/Swiften/Events/MessageEvent.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_MessageEvent_H
+#define SWIFTEN_MessageEvent_H
+
+#include <cassert>
+
+#include "Swiften/Elements/Message.h"
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class MessageEvent {
+ public:
+ MessageEvent(boost::shared_ptr<Message> stanza) : stanza_(stanza){}
+ boost::shared_ptr<Message> getStanza() {return stanza_;}
+ boost::signal<void()> onRead;
+
+ bool isReadable() {
+ return getStanza()->isError() || !getStanza()->getBody().isEmpty();
+ }
+
+ void read() {
+ assert (isReadable());
+ onRead();
+ }
+
+ private:
+ boost::shared_ptr<Message> stanza_;
+ };
+}
+
+#endif
diff --git a/Swiften/Examples/Makefile.inc b/Swiften/Examples/Makefile.inc
new file mode 100644
index 0000000..4f61f2c
--- /dev/null
+++ b/Swiften/Examples/Makefile.inc
@@ -0,0 +1,4 @@
+include Swiften/Examples/TuneBot/Makefile.inc
+
+.PHONY: examples
+examples: $(EXAMPLES_TARGETS)
diff --git a/Swiften/Examples/TuneBot/Makefile.inc b/Swiften/Examples/TuneBot/Makefile.inc
new file mode 100644
index 0000000..c7c00c7
--- /dev/null
+++ b/Swiften/Examples/TuneBot/Makefile.inc
@@ -0,0 +1,11 @@
+TUNEBOT_TARGET = Swiften/Examples/TuneBot/TuneBot
+TUNEBOT_SOURCES += \
+ Swiften/Examples/TuneBot/TuneBot.cpp
+TUNEBOT_OBJECTS = \
+ $(TUNEBOT_SOURCES:.cpp=.o)
+
+CLEANFILES += $(TUNEBOT_OBJECTS) $(TUNEBOT_TARGET)
+EXAMPLES_TARGETS += $(TUNEBOT_TARGET)
+
+$(TUNEBOT_TARGET): $(SWIFTEN_TARGET) $(TUNEBOT_OBJECTS)
+ $(QUIET_LINK)$(CXX) -o $(TUNEBOT_TARGET) $(TUNEBOT_OBJECTS) $(LDFLAGS) $(CPPCLIENT_LDFLAGS) $(SWIFTEN_TARGET) $(LIBS)
diff --git a/Swiften/Examples/TuneBot/TuneBot.cpp b/Swiften/Examples/TuneBot/TuneBot.cpp
new file mode 100644
index 0000000..d2bb100
--- /dev/null
+++ b/Swiften/Examples/TuneBot/TuneBot.cpp
@@ -0,0 +1,72 @@
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/Elements/CapsInfo.h"
+#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
+#include "Swiften/Disco/CapsInfoGenerator.h"
+
+using namespace Swift;
+using namespace boost;
+
+class TuneBot {
+ public:
+ TuneBot(const JID& jid, const String& password) {
+ client_ = new Client(jid, password);
+ router_ = new IQRouter(client_);
+
+ DiscoInfo discoInfo;
+ discoInfo.addIdentity(DiscoInfo::Identity("TuneBot", "client", "bot"));
+ discoInfo.addFeature("http://jabber.org/protocol/tune+notify");
+ capsInfo_ = boost::shared_ptr<CapsInfo>(new CapsInfo(CapsInfoGenerator("http://el-tramo.be/tunebot").generateCapsInfo(discoInfo)));
+ discoResponder_ = new DiscoInfoResponder(router_);
+ discoResponder_->setDiscoInfo(discoInfo);
+ discoResponder_->setDiscoInfo(capsInfo_->getNode() + "#" + capsInfo_->getVersion(), discoInfo);
+
+ client_->onSessionStarted.connect(bind(&TuneBot::handleSessionStarted, this));
+ client_->onMessageReceived.connect(bind(&TuneBot::handleMessage, this, _1));
+ client_->connect();
+ }
+
+ void handleSessionStarted() {
+ GetRosterRequest* rosterRequest = new GetRosterRequest(router_, Request::AutoDeleteAfterResponse);
+ rosterRequest->onResponse.connect(bind(&TuneBot::handleRosterReceived, this, _1));
+ rosterRequest->send();
+ }
+
+ void handleRosterReceived(shared_ptr<Payload>) {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->addPayload(capsInfo_);
+ presence->setPriority(-1);
+ client_->send(presence);
+ }
+
+ void handleMessage(shared_ptr<Message> message) {
+ // TODO
+ }
+
+ private:
+ Client* client_;
+ IQRouter* router_;
+ DiscoInfoResponder* discoResponder_;
+ boost::shared_ptr<CapsInfo> capsInfo_;
+};
+
+
+int main(int argc, char* argv[])
+{
+ if (argc != 3) {
+ std::cerr << "Usage: " << argv[0] << " <jid> <password>" << std::endl;
+ return -1;
+ }
+
+ SimpleEventLoop eventLoop;
+
+ TuneBot bot(argv[1], argv[2]);
+ eventLoop.run();
+}
diff --git a/Swiften/JID/JID.cpp b/Swiften/JID/JID.cpp
new file mode 100644
index 0000000..dcd6dd1
--- /dev/null
+++ b/Swiften/JID/JID.cpp
@@ -0,0 +1,116 @@
+#include <stringprep.h>
+#include <vector>
+#include <iostream>
+
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+
+
+class StringPrepper {
+ private:
+ static const int MAX_STRINGPREP_SIZE = 1024;
+
+ public:
+ static String getNamePrepped(const String& name) {
+ return getStringPrepped(name, stringprep_nameprep);
+ }
+
+ static String getNodePrepped(const String& node) {
+ return getStringPrepped(node, stringprep_xmpp_nodeprep);
+ }
+
+ static String getResourcePrepped(const String& resource) {
+ return getStringPrepped(resource, stringprep_xmpp_resourceprep);
+ }
+
+ static String getStringPrepped(const String& s, const Stringprep_profile profile[]) {
+ std::vector<char> input(s.getUTF8String().begin(), s.getUTF8String().end());
+ input.resize(MAX_STRINGPREP_SIZE);
+ if (stringprep(&input[0], MAX_STRINGPREP_SIZE, static_cast<Stringprep_profile_flags>(0), profile) == 0) {
+ return String(&input[0]);
+ }
+ else {
+ return "";
+ }
+ }
+};
+
+
+JID::JID(const char* jid) {
+ initializeFromString(String(jid));
+}
+
+JID::JID(const String& jid) {
+ initializeFromString(jid);
+}
+
+JID::JID(const String& node, const String& domain) : hasResource_(false) {
+ nameprepAndSetComponents(node, domain, "");
+}
+
+JID::JID(const String& node, const String& domain, const String& resource) : hasResource_(true) {
+ nameprepAndSetComponents(node, domain, resource);
+}
+
+void JID::initializeFromString(const String& jid) {
+ if (jid.beginsWith('@')) {
+ return;
+ }
+
+ String bare, resource;
+ size_t slashIndex = jid.find('/');
+ if (slashIndex != jid.npos()) {
+ hasResource_ = true;
+ bare = jid.getSubstring(0, slashIndex);
+ resource = jid.getSubstring(slashIndex + 1, jid.npos());
+ }
+ else {
+ hasResource_ = false;
+ bare = jid;
+ }
+ std::pair<String,String> nodeAndDomain = bare.getSplittedAtFirst('@');
+ if (nodeAndDomain.second.isEmpty()) {
+ nameprepAndSetComponents("", nodeAndDomain.first, resource);
+ }
+ else {
+ nameprepAndSetComponents(nodeAndDomain.first, nodeAndDomain.second, resource);
+ }
+}
+
+
+void JID::nameprepAndSetComponents(const String& node, const String& domain, const String& resource) {
+ node_ = StringPrepper::getNamePrepped(node);
+ domain_ = StringPrepper::getNodePrepped(domain);
+ resource_ = StringPrepper::getResourcePrepped(resource);
+}
+
+String JID::toString() const {
+ String string;
+ if (!node_.isEmpty()) {
+ string += node_ + "@";
+ }
+ string += domain_;
+ if (!isBare()) {
+ string += "/" + resource_;
+ }
+ return string;
+}
+
+int JID::compare(const Swift::JID& o, CompareType compareType) const {
+ if (node_ < o.node_) { return -1; }
+ if (node_ > o.node_) { return 1; }
+ if (domain_ < o.domain_) { return -1; }
+ if (domain_ > o.domain_) { return 1; }
+ if (compareType == WithResource) {
+ if (hasResource_ != o.hasResource_) {
+ return hasResource_ ? 1 : -1;
+ }
+ if (resource_ < o.resource_) { return -1; }
+ if (resource_ > o.resource_) { return 1; }
+ }
+ return 0;
+}
+
+} // namespace Swift
+
diff --git a/Swiften/JID/JID.h b/Swiften/JID/JID.h
new file mode 100644
index 0000000..ad89c85
--- /dev/null
+++ b/Swiften/JID/JID.h
@@ -0,0 +1,68 @@
+#ifndef SWIFTEN_JID_H
+#define SWIFTEN_JID_H
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class JID
+ {
+ public:
+ enum CompareType { WithResource, WithoutResource };
+
+ explicit JID(const String& = String());
+ explicit JID(const char*);
+ JID(const String& node, const String& domain);
+ JID(const String& node, const String& domain, const String& resource);
+
+ bool isValid() const { return !domain_.isEmpty(); /* FIXME */ }
+
+ const String& getNode() const { return node_; }
+ const String& getDomain() const { return domain_; }
+ const String& getResource() const { return resource_; }
+ bool isBare() const { return !hasResource_; }
+
+ JID toBare() const { return JID(getNode(), getDomain()); /* FIXME: Duplicate unnecessary nameprepping. Probably ok. */ }
+
+ String toString() const;
+
+ bool equals(const JID& o, CompareType compareType) const {
+ return compare(o, compareType) == 0;
+ }
+
+ int compare(const JID& o, CompareType compareType) const;
+
+ operator String () const { return toString(); }
+
+ bool operator<(const Swift::JID& b) const {
+ return compare(b, Swift::JID::WithResource) < 0;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const Swift::JID& j) {
+ os << j.toString();
+ return os;
+ }
+
+ friend bool operator==(const Swift::JID& a, const Swift::JID& b) {
+ return a.compare(b, Swift::JID::WithResource) == 0;
+ }
+
+ friend bool operator!=(const Swift::JID& a, const Swift::JID& b) {
+ return a.compare(b, Swift::JID::WithResource) != 0;
+ }
+
+ protected:
+ void nameprepAndSetComponents(const String& node, const String& domain, const String& resource);
+
+ private:
+ void initializeFromString(const String&);
+
+ private:
+ String node_;
+ String domain_;
+ bool hasResource_;
+ String resource_;
+ };
+}
+
+#endif
+
diff --git a/Swiften/JID/Makefile.inc b/Swiften/JID/Makefile.inc
new file mode 100644
index 0000000..f60cb3c
--- /dev/null
+++ b/Swiften/JID/Makefile.inc
@@ -0,0 +1,4 @@
+SWIFTEN_SOURCES += \
+ Swiften/JID/JID.cpp
+
+include Swiften/JID/UnitTest/Makefile.inc
diff --git a/Swiften/JID/UnitTest/JIDTest.cpp b/Swiften/JID/UnitTest/JIDTest.cpp
new file mode 100644
index 0000000..917f89f
--- /dev/null
+++ b/Swiften/JID/UnitTest/JIDTest.cpp
@@ -0,0 +1,310 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/JID/JID.h"
+
+using namespace Swift;
+
+class JIDTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(JIDTest);
+ CPPUNIT_TEST(testConstructorWithString);
+ CPPUNIT_TEST(testConstructorWithString_NoResource);
+ CPPUNIT_TEST(testConstructorWithString_NoNode);
+ CPPUNIT_TEST(testConstructorWithString_EmptyResource);
+ CPPUNIT_TEST(testConstructorWithString_OnlyDomain);
+ CPPUNIT_TEST(testConstructorWithString_UpperCaseNode);
+ CPPUNIT_TEST(testConstructorWithString_UpperCaseDomain);
+ CPPUNIT_TEST(testConstructorWithString_UpperCaseResource);
+ CPPUNIT_TEST(testConstructorWithString_EmptyNode);
+ CPPUNIT_TEST(testConstructorWithStrings);
+ CPPUNIT_TEST(testIsBare);
+ CPPUNIT_TEST(testIsBare_NotBare);
+ CPPUNIT_TEST(testToBare);
+ CPPUNIT_TEST(testToBare_EmptyNode);
+ CPPUNIT_TEST(testToBare_EmptyResource);
+ CPPUNIT_TEST(testToString);
+ CPPUNIT_TEST(testToString_EmptyNode);
+ CPPUNIT_TEST(testToString_EmptyResource);
+ CPPUNIT_TEST(testToString_NoResource);
+ CPPUNIT_TEST(testCompare_SmallerNode);
+ CPPUNIT_TEST(testCompare_LargerNode);
+ CPPUNIT_TEST(testCompare_SmallerDomain);
+ CPPUNIT_TEST(testCompare_LargerDomain);
+ CPPUNIT_TEST(testCompare_SmallerResource);
+ CPPUNIT_TEST(testCompare_LargerResource);
+ CPPUNIT_TEST(testCompare_Equal);
+ CPPUNIT_TEST(testCompare_EqualWithoutResource);
+ CPPUNIT_TEST(testCompare_NoResourceAndEmptyResource);
+ CPPUNIT_TEST(testCompare_EmptyResourceAndNoResource);
+ CPPUNIT_TEST(testEquals);
+ CPPUNIT_TEST(testEquals_NotEqual);
+ CPPUNIT_TEST(testEquals_WithoutResource);
+ CPPUNIT_TEST(testSmallerThan);
+ CPPUNIT_TEST(testSmallerThan_Equal);
+ CPPUNIT_TEST(testSmallerThan_Larger);
+ CPPUNIT_TEST(testHasResource);
+ CPPUNIT_TEST(testHasResource_NoResource);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ JIDTest() {}
+
+ void testConstructorWithString() {
+ JID testling("foo@bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo"), testling.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.getDomain());
+ CPPUNIT_ASSERT_EQUAL(String("baz"), testling.getResource());
+ CPPUNIT_ASSERT(!testling.isBare());
+ }
+
+ void testConstructorWithString_NoResource() {
+ JID testling("foo@bar");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo"), testling.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.getDomain());
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.getResource());
+ CPPUNIT_ASSERT(testling.isBare());
+ }
+
+ void testConstructorWithString_EmptyResource() {
+ JID testling("bar/");
+
+ CPPUNIT_ASSERT(testling.isValid());
+ CPPUNIT_ASSERT(!testling.isBare());
+ }
+
+ void testConstructorWithString_NoNode() {
+ JID testling("bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.getDomain());
+ CPPUNIT_ASSERT_EQUAL(String("baz"), testling.getResource());
+ CPPUNIT_ASSERT(!testling.isBare());
+ }
+
+ void testConstructorWithString_OnlyDomain() {
+ JID testling("bar");
+
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.getDomain());
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.getResource());
+ CPPUNIT_ASSERT(testling.isBare());
+ }
+
+ void testConstructorWithString_UpperCaseNode() {
+ JID testling("Fo\xCE\xA9@bar");
+
+ CPPUNIT_ASSERT_EQUAL(String("fo\xCF\x89"), testling.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.getDomain());
+ }
+
+ void testConstructorWithString_UpperCaseDomain() {
+ JID testling("Fo\xCE\xA9");
+
+ CPPUNIT_ASSERT_EQUAL(String("fo\xCF\x89"), testling.getDomain());
+ }
+
+ void testConstructorWithString_UpperCaseResource() {
+ JID testling("bar/Fo\xCE\xA9");
+
+ CPPUNIT_ASSERT_EQUAL(testling.getResource(), String("Fo\xCE\xA9"));
+ }
+
+ void testConstructorWithString_EmptyNode() {
+ JID testling("@bar");
+
+ CPPUNIT_ASSERT(!testling.isValid());
+ }
+
+ void testConstructorWithStrings() {
+ JID testling("foo", "bar", "baz");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo"), testling.getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.getDomain());
+ CPPUNIT_ASSERT_EQUAL(String("baz"), testling.getResource());
+ }
+
+ void testIsBare() {
+ CPPUNIT_ASSERT(JID("foo@bar").isBare());
+ }
+
+ void testIsBare_NotBare() {
+ CPPUNIT_ASSERT(!JID("foo@bar/baz").isBare());
+ }
+
+ void testToBare() {
+ JID testling("foo@bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo"), testling.toBare().getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.toBare().getDomain());
+ CPPUNIT_ASSERT(testling.toBare().isBare());
+ }
+
+ void testToBare_EmptyNode() {
+ JID testling("bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.toBare().getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.toBare().getDomain());
+ CPPUNIT_ASSERT(testling.toBare().isBare());
+ }
+
+ void testToBare_EmptyResource() {
+ JID testling("bar/");
+
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.toBare().getNode());
+ CPPUNIT_ASSERT_EQUAL(String("bar"), testling.toBare().getDomain());
+ CPPUNIT_ASSERT(testling.toBare().isBare());
+ }
+
+ void testToString() {
+ JID testling("foo@bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo@bar/baz"), testling.toString());
+ }
+
+ void testToString_EmptyNode() {
+ JID testling("bar/baz");
+
+ CPPUNIT_ASSERT_EQUAL(String("bar/baz"), testling.toString());
+ }
+
+ void testToString_NoResource() {
+ JID testling("foo@bar");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo@bar"), testling.toString());
+ }
+
+ void testToString_EmptyResource() {
+ JID testling("foo@bar/");
+
+ CPPUNIT_ASSERT_EQUAL(String("foo@bar/"), testling.toString());
+ }
+
+ void testCompare_SmallerNode() {
+ JID testling1("a@c");
+ JID testling2("b@b");
+
+ CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_LargerNode() {
+ JID testling1("c@a");
+ JID testling2("b@b");
+
+ CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_SmallerDomain() {
+ JID testling1("x@a/c");
+ JID testling2("x@b/b");
+
+ CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_LargerDomain() {
+ JID testling1("x@b/b");
+ JID testling2("x@a/c");
+
+ CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_SmallerResource() {
+ JID testling1("x@y/a");
+ JID testling2("x@y/b");
+
+ CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_LargerResource() {
+ JID testling1("x@y/b");
+ JID testling2("x@y/a");
+
+ CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_Equal() {
+ JID testling1("x@y/z");
+ JID testling2("x@y/z");
+
+ CPPUNIT_ASSERT_EQUAL(0, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_EqualWithoutResource() {
+ JID testling1("x@y/a");
+ JID testling2("x@y/b");
+
+ CPPUNIT_ASSERT_EQUAL(0, testling1.compare(testling2, JID::WithoutResource));
+ }
+
+ void testCompare_NoResourceAndEmptyResource() {
+ JID testling1("x@y/");
+ JID testling2("x@y");
+
+ CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testCompare_EmptyResourceAndNoResource() {
+ JID testling1("x@y");
+ JID testling2("x@y/");
+
+ CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource));
+ }
+
+ void testEquals() {
+ JID testling1("x@y/c");
+ JID testling2("x@y/c");
+
+ CPPUNIT_ASSERT(testling1.equals(testling2, JID::WithResource));
+ }
+
+ void testEquals_NotEqual() {
+ JID testling1("x@y/c");
+ JID testling2("x@y/d");
+
+ CPPUNIT_ASSERT(!testling1.equals(testling2, JID::WithResource));
+ }
+
+ void testEquals_WithoutResource() {
+ JID testling1("x@y/c");
+ JID testling2("x@y/d");
+
+ CPPUNIT_ASSERT(testling1.equals(testling2, JID::WithoutResource));
+ }
+
+ void testSmallerThan() {
+ JID testling1("x@y/c");
+ JID testling2("x@y/d");
+
+ CPPUNIT_ASSERT(testling1 < testling2);
+ }
+
+ void testSmallerThan_Equal() {
+ JID testling1("x@y/d");
+ JID testling2("x@y/d");
+
+ CPPUNIT_ASSERT(!(testling1 < testling2));
+ }
+
+ void testSmallerThan_Larger() {
+ JID testling1("x@y/d");
+ JID testling2("x@y/c");
+
+ CPPUNIT_ASSERT(!(testling1 < testling2));
+ }
+
+ void testHasResource() {
+ JID testling("x@y/d");
+
+ CPPUNIT_ASSERT(!testling.isBare());
+ }
+
+ void testHasResource_NoResource() {
+ JID testling("x@y");
+
+ CPPUNIT_ASSERT(testling.isBare());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(JIDTest);
diff --git a/Swiften/JID/UnitTest/Makefile.inc b/Swiften/JID/UnitTest/Makefile.inc
new file mode 100644
index 0000000..e896fd7
--- /dev/null
+++ b/Swiften/JID/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+ Swiften/JID/UnitTest/JIDTest.cpp
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
new file mode 100644
index 0000000..2b8054f
--- /dev/null
+++ b/Swiften/MUC/MUC.cpp
@@ -0,0 +1,69 @@
+#include "Swiften/MUC/MUC.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/MUCPayload.h"
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+
+typedef std::pair<String, MUCOccupant> StringMUCOccupantPair;
+
+MUC::MUC(StanzaChannel* stanzaChannel, const JID &muc) : muc_(muc), stanzaChannel_(stanzaChannel) {
+ stanzaChannel_->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
+}
+
+MUC::~MUC() {
+}
+
+void MUC::joinAs(const String &nick) {
+ boost::shared_ptr<Presence> joinPresence(new Presence());
+ joinPresence->setTo(JID(muc_.getNode(), muc_.getDomain(), nick));
+ joinPresence->addPayload(boost::shared_ptr<Payload>(new MUCPayload()));
+ stanzaChannel_->sendPresence(joinPresence);
+ myNick_ = nick;
+}
+
+void MUC::part() {
+ boost::shared_ptr<Presence> partPresence(new Presence());
+ partPresence->setType(Presence::Unavailable);
+ partPresence->setTo(JID(muc_.getNode(), muc_.getDomain(), myNick_));
+ stanzaChannel_->sendPresence(partPresence);
+}
+
+void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ if (presence->getFrom().toBare() != muc_ || presence->getFrom().getResource() == "") {
+ return;
+ }
+ String nick = presence->getFrom().getResource();
+ if (presence->getType() == Presence::Unavailable) {
+ foreach (StringMUCOccupantPair occupantPair, occupants_) {
+ if (occupantPair.first == nick) {
+ occupants_.erase(nick);
+ onOccupantLeft(occupantPair.second, Part, "");
+ break;
+ }
+ }
+ } else if (presence->getType() == Presence::Available) {
+ bool found = false;
+ foreach (StringMUCOccupantPair occupantPair, occupants_) {
+ if (occupantPair.first == nick) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ MUCOccupant occupant(nick);
+ occupants_.insert(occupants_.end(), std::pair<String, MUCOccupant>(nick, occupant));
+ onOccupantJoined(occupant);
+ }
+ onOccupantPresenceChange(presence);
+ }
+}
+
+
+}
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
new file mode 100644
index 0000000..45bcbd3
--- /dev/null
+++ b/Swiften/MUC/MUC.h
@@ -0,0 +1,49 @@
+#ifndef SWIFTEN_MUC_H
+#define SWIFTEN_MUC_H
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/MUC/MUCOccupant.h"
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+
+namespace Swift {
+ class StanzaChannel;
+
+ class MUC {
+ public:
+ enum JoinResult { JoinSucceeded, JoinFailed };
+ enum LeavingType { Part };
+
+ public:
+ MUC(StanzaChannel* stanzaChannel, const JID &muc);
+ ~MUC();
+
+ void joinAs(const String &nick);
+ String getCurrentNick();
+ void part();
+ void handleIncomingMessage(boost::shared_ptr<Message> message);
+
+ public:
+ boost::signal<void (JoinResult)> onJoinComplete;
+ boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;
+ boost::signal<void (boost::shared_ptr<Presence>)> onOccupantPresenceChange;
+ boost::signal<void (const MUCOccupant&)> onOccupantJoined;
+ /**Occupant, type, and reason. */
+ boost::signal<void (const MUCOccupant&, LeavingType, const String&)> onOccupantLeft;
+
+ private:
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+ JID muc_;
+ StanzaChannel *stanzaChannel_;
+ String myNick_;
+ std::map<String, MUCOccupant> occupants_;
+ };
+}
+
+#endif
diff --git a/Swiften/MUC/MUCOccupant.cpp b/Swiften/MUC/MUCOccupant.cpp
new file mode 100644
index 0000000..6ed8591
--- /dev/null
+++ b/Swiften/MUC/MUCOccupant.cpp
@@ -0,0 +1,15 @@
+#include "Swiften/MUC/MUCOccupant.h"
+
+namespace Swift {
+
+MUCOccupant::MUCOccupant(const String &nick) : nick_(nick) {
+}
+
+MUCOccupant::~MUCOccupant() {
+}
+
+String MUCOccupant::getNick() const {
+ return nick_;
+}
+
+}
diff --git a/Swiften/MUC/MUCOccupant.h b/Swiften/MUC/MUCOccupant.h
new file mode 100644
index 0000000..22e58ac
--- /dev/null
+++ b/Swiften/MUC/MUCOccupant.h
@@ -0,0 +1,21 @@
+#ifndef SWIFTEN_MUCOccupant_H
+#define SWIFTEN_MUCOccupant_H
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class Client;
+
+ class MUCOccupant {
+ public:
+ MUCOccupant(const String &nick);
+ ~MUCOccupant();
+
+ String getNick() const;
+
+ private:
+ String nick_;
+ };
+}
+
+#endif
diff --git a/Swiften/MUC/Makefile.inc b/Swiften/MUC/Makefile.inc
new file mode 100644
index 0000000..d97b9fa
--- /dev/null
+++ b/Swiften/MUC/Makefile.inc
@@ -0,0 +1,3 @@
+SWIFTEN_SOURCES += \
+ Swiften/MUC/MUC.cpp \
+ Swiften/MUC/MUCOccupant.cpp
diff --git a/Swiften/Makefile.inc b/Swiften/Makefile.inc
new file mode 100644
index 0000000..880887b
--- /dev/null
+++ b/Swiften/Makefile.inc
@@ -0,0 +1,42 @@
+include Swiften/Base/Makefile.inc
+include Swiften/Application/Makefile.inc
+include Swiften/EventLoop/Makefile.inc
+include Swiften/StringCodecs/Makefile.inc
+include Swiften/JID/Makefile.inc
+include Swiften/Elements/Makefile.inc
+include Swiften/Events/Makefile.inc
+include Swiften/StreamStack/Makefile.inc
+include Swiften/Serializer/Makefile.inc
+include Swiften/Parser/Makefile.inc
+include Swiften/MUC/Makefile.inc
+include Swiften/Network/Makefile.inc
+include Swiften/Client/Makefile.inc
+include Swiften/TLS/Makefile.inc
+include Swiften/SASL/Makefile.inc
+include Swiften/Compress/Makefile.inc
+include Swiften/Queries/Makefile.inc
+include Swiften/Controllers/Makefile.inc
+include Swiften/Roster/Makefile.inc
+include Swiften/Disco/Makefile.inc
+include Swiften/Presence/Makefile.inc
+include Swiften/Notifier/Makefile.inc
+
+SWIFTEN_TARGET = Swiften/Swiften.a
+SWIFTEN_OBJECTS = \
+ $(SWIFTEN_SOURCES:.cpp=.o) \
+ $(SWIFTEN_OBJECTIVE_SOURCES:.mm=.o) \
+ $(LIBIDN_OBJECTS) \
+ $(BOOST_OBJECTS) \
+ $(ZLIB_OBJECTS)
+
+TARGETS += $(SWIFTEN_TARGET)
+CLEANFILES += $(SWIFTEN_TARGET) $(SWIFTEN_OBJECTS)
+
+.PHONY: lib
+lib: $(SWIFTEN_TARGET)
+
+$(SWIFTEN_TARGET): $(SWIFTEN_OBJECTS)
+ $(QUIET_AR)$(AR) $(ARFLAGS) $@ $(SWIFTEN_OBJECTS)
+
+include Swiften/Examples/Makefile.inc
+include Swiften/QA/Makefile.inc
diff --git a/Swiften/Network/BoostConnection.cpp b/Swiften/Network/BoostConnection.cpp
new file mode 100644
index 0000000..f055f6a
--- /dev/null
+++ b/Swiften/Network/BoostConnection.cpp
@@ -0,0 +1,100 @@
+#include "Swiften/Network/BoostConnection.h"
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Network/DomainNameResolver.h"
+#include "Swiften/Network/DomainNameResolveException.h"
+
+namespace Swift {
+
+static const size_t BUFFER_SIZE = 4096;
+
+BoostConnection::BoostConnection(const String& domain) :
+ Connection(domain), ioService_(0), thread_(0), socket_(0), readBuffer_(BUFFER_SIZE) {
+ ioService_ = new boost::asio::io_service();
+}
+
+BoostConnection::~BoostConnection() {
+ MainEventLoop::removeEventsFromOwner(this);
+ ioService_->stop();
+ thread_->join();
+ delete socket_;
+ delete thread_;
+ delete ioService_;
+}
+
+void BoostConnection::connect() {
+ thread_ = new boost::thread(boost::bind(&BoostConnection::doConnect, this));
+}
+
+void BoostConnection::disconnect() {
+ if (ioService_) {
+ ioService_->post(boost::bind(&BoostConnection::doDisconnect, this));
+ }
+}
+
+void BoostConnection::write(const ByteArray& data) {
+ if (ioService_) {
+ ioService_->post(boost::bind(&BoostConnection::doWrite, this, data));
+ }
+}
+
+void BoostConnection::doConnect() {
+ DomainNameResolver resolver;
+ try {
+ HostAddressPort addressPort = resolver.resolve(getDomain().getUTF8String());
+ socket_ = new boost::asio::ip::tcp::socket(*ioService_);
+ boost::asio::ip::tcp::endpoint endpoint(
+ boost::asio::ip::address::from_string(addressPort.getAddress().toString()), addressPort.getPort());
+ socket_->async_connect(
+ endpoint,
+ boost::bind(&BoostConnection::handleConnectFinished, this, boost::asio::placeholders::error));
+ ioService_->run();
+ }
+ catch (const DomainNameResolveException& e) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError), DomainNameResolveError), this);
+ }
+}
+
+void BoostConnection::handleConnectFinished(const boost::system::error_code& error) {
+ if (!error) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onConnected)), this);
+ doRead();
+ }
+ else if (error != boost::asio::error::operation_aborted) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError), ConnectionError), this);
+ }
+}
+
+void BoostConnection::doRead() {
+ socket_->async_read_some(
+ boost::asio::buffer(readBuffer_),
+ boost::bind(&BoostConnection::handleSocketRead, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
+}
+
+void BoostConnection::doWrite(const ByteArray& data) {
+ boost::asio::write(*socket_, boost::asio::buffer(static_cast<const char*>(data.getData()), data.getSize()));
+}
+
+void BoostConnection::handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred) {
+ if (!error) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDataRead), ByteArray(&readBuffer_[0], bytesTransferred)), this);
+ doRead();
+ }
+ else if (error != boost::asio::error::operation_aborted) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError), ReadError), this);
+ }
+}
+
+void BoostConnection::doDisconnect() {
+ if (socket_) {
+ socket_->close();
+ }
+}
+
+}
diff --git a/Swiften/Network/BoostConnection.h b/Swiften/Network/BoostConnection.h
new file mode 100644
index 0000000..f8fa514
--- /dev/null
+++ b/Swiften/Network/BoostConnection.h
@@ -0,0 +1,43 @@
+#ifndef SWIFTEN_BoostConnection_H
+#define SWIFTEN_BoostConnection_H
+
+#include <boost/asio.hpp>
+
+#include "Swiften/Network/Connection.h"
+
+namespace boost {
+ class thread;
+ namespace system {
+ class error_code;
+ }
+}
+
+namespace Swift {
+ class BoostConnection : public Connection {
+ public:
+ BoostConnection(const String& domain);
+ ~BoostConnection();
+
+ virtual void connect();
+ virtual void disconnect();
+ virtual void write(const ByteArray& data);
+
+ private:
+ void doConnect();
+ void doDisconnect();
+
+ void handleConnectFinished(const boost::system::error_code& error);
+ void handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred);
+ void doRead();
+ void doWrite(const ByteArray&);
+
+ private:
+ boost::asio::io_service* ioService_;
+ boost::thread* thread_;
+ boost::asio::ip::tcp::socket* socket_;
+ std::vector<char> readBuffer_;
+ bool disconnecting_;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/BoostConnectionFactory.cpp b/Swiften/Network/BoostConnectionFactory.cpp
new file mode 100644
index 0000000..9c542ac
--- /dev/null
+++ b/Swiften/Network/BoostConnectionFactory.cpp
@@ -0,0 +1,12 @@
+#include "Swiften/Network/BoostConnectionFactory.h"
+
+namespace Swift {
+
+BoostConnectionFactory::BoostConnectionFactory() {
+}
+
+BoostConnection* BoostConnectionFactory::createConnection(const String& domain) {
+ return new BoostConnection(domain);
+}
+
+}
diff --git a/Swiften/Network/BoostConnectionFactory.h b/Swiften/Network/BoostConnectionFactory.h
new file mode 100644
index 0000000..b6a27b2
--- /dev/null
+++ b/Swiften/Network/BoostConnectionFactory.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_BoostConnectionFactory_H
+#define SWIFTEN_BoostConnectionFactory_H
+
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/Network/BoostConnection.h"
+
+namespace Swift {
+ class BoostConnection;
+
+ class BoostConnectionFactory : public ConnectionFactory {
+ public:
+ BoostConnectionFactory();
+
+ virtual BoostConnection* createConnection(const String& domain);
+ };
+}
+
+#endif
diff --git a/Swiften/Network/Connection.h b/Swiften/Network/Connection.h
new file mode 100644
index 0000000..6d05eee
--- /dev/null
+++ b/Swiften/Network/Connection.h
@@ -0,0 +1,40 @@
+#ifndef SWIFTEN_CONNECTION_H
+#define SWIFTEN_CONNECTION_H
+
+#include <boost/signals.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class Connection {
+ public:
+ enum Error {
+ DomainNameResolveError,
+ ConnectionError,
+ ReadError
+ };
+
+ Connection(const String& domain) : domain_(domain) {}
+ virtual ~Connection() {}
+
+ virtual void connect() = 0;
+ virtual void disconnect() = 0;
+ virtual void write(const ByteArray& data) = 0;
+
+ public:
+ boost::signal<void ()> onConnected;
+ boost::signal<void (Error)> onError;
+ boost::signal<void (const ByteArray&)> onDataRead;
+
+ protected:
+ const String& getDomain() const {
+ return domain_;
+ }
+
+ private:
+ String domain_;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/ConnectionFactory.cpp b/Swiften/Network/ConnectionFactory.cpp
new file mode 100644
index 0000000..686a165
--- /dev/null
+++ b/Swiften/Network/ConnectionFactory.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Network/ConnectionFactory.h"
+
+namespace Swift {
+
+ConnectionFactory::~ConnectionFactory() {
+}
+
+}
diff --git a/Swiften/Network/ConnectionFactory.h b/Swiften/Network/ConnectionFactory.h
new file mode 100644
index 0000000..2af9ebf
--- /dev/null
+++ b/Swiften/Network/ConnectionFactory.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_ConnectionFactory_H
+#define SWIFTEN_ConnectionFactory_H
+
+namespace Swift {
+ class String;
+ class Connection;
+
+ class ConnectionFactory {
+ public:
+ virtual ~ConnectionFactory();
+
+ virtual Connection* createConnection(const String& domain) = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/DomainNameResolveException.h b/Swiften/Network/DomainNameResolveException.h
new file mode 100644
index 0000000..a6cfbc6
--- /dev/null
+++ b/Swiften/Network/DomainNameResolveException.h
@@ -0,0 +1,11 @@
+#ifndef SWIFTEN_DOMAINNAMELOOKUPEXCEPTION_H
+#define SWIFTEN_DOMAINNAMELOOKUPEXCEPTION_H
+
+namespace Swift {
+ class DomainNameResolveException {
+ public:
+ DomainNameResolveException() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Network/DomainNameResolver.cpp b/Swiften/Network/DomainNameResolver.cpp
new file mode 100644
index 0000000..d2bbf0d
--- /dev/null
+++ b/Swiften/Network/DomainNameResolver.cpp
@@ -0,0 +1,176 @@
+#include "Swiften/Network/DomainNameResolver.h"
+#include "Swiften/Base/Platform.h"
+
+#include <stdlib.h>
+#include <boost/asio.hpp>
+#include <idna.h>
+#ifdef SWIFTEN_PLATFORM_WINDOWS
+#undef UNICODE
+#include <windows.h>
+#include <windns.h>
+#ifndef DNS_TYPE_SRV
+#define DNS_TYPE_SRV 33
+#endif
+#else
+#include <arpa/nameser.h>
+#include <arpa/nameser_compat.h>
+#include <resolv.h>
+#endif
+
+#include "Swiften/Network/DomainNameResolveException.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+
+DomainNameResolver::DomainNameResolver() {
+}
+
+DomainNameResolver::~DomainNameResolver() {
+}
+
+HostAddressPort DomainNameResolver::resolve(const String& domain) {
+ char* output;
+ if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) {
+ std::string outputString(output);
+ free(output);
+ return resolveDomain(outputString);
+ }
+ else {
+ return resolveDomain(domain.getUTF8String());
+ }
+}
+
+HostAddressPort DomainNameResolver::resolveDomain(const std::string& domain) {
+ try {
+ return resolveXMPPService(domain);
+ }
+ catch (const DomainNameResolveException&) {
+ }
+ return HostAddressPort(resolveHostName(domain), 5222);
+}
+
+HostAddressPort DomainNameResolver::resolveXMPPService(const std::string& domain) {
+ std::string srvQuery = "_xmpp-client._tcp." + domain;
+
+#if defined(SWIFTEN_PLATFORM_WINDOWS)
+ DNS_RECORD* responses;
+ // FIXME: This conversion doesn't work if unicode is deffed above
+ if (DnsQuery(srvQuery.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &responses, NULL) != ERROR_SUCCESS) {
+ throw DomainNameResolveException();
+ }
+
+ DNS_RECORD* currentEntry = responses;
+ while (currentEntry) {
+ if (currentEntry->wType == DNS_TYPE_SRV) {
+ int port = currentEntry->Data.SRV.wPort;
+ try {
+ // The pNameTarget is actually a PCWSTR, so I would have expected this
+ // conversion to not work at all, but it does.
+ // Actually, it doesn't. Fix this and remove explicit cast
+ // Remove unicode undef above as well
+ std::string hostname((const char*) currentEntry->Data.SRV.pNameTarget);
+ HostAddress address = resolveHostName(hostname);
+ DnsRecordListFree(responses, DnsFreeRecordList);
+ return HostAddressPort(address, port);
+ }
+ catch (const DomainNameResolveException&) {
+ }
+ }
+ currentEntry = currentEntry->pNext;
+ }
+ DnsRecordListFree(responses, DnsFreeRecordList);
+
+#else
+
+ ByteArray response;
+ response.resize(NS_PACKETSZ);
+ int responseLength = res_query(const_cast<char*>(srvQuery.c_str()), ns_c_in, ns_t_srv, reinterpret_cast<u_char*>(response.getData()), response.getSize());
+ if (responseLength == -1) {
+ throw DomainNameResolveException();
+ }
+
+ // Parse header
+ HEADER* header = reinterpret_cast<HEADER*>(response.getData());
+ unsigned char* messageStart = reinterpret_cast<unsigned char*>(response.getData());
+ unsigned char* messageEnd = messageStart + responseLength;
+ unsigned char* currentEntry = messageStart + NS_HFIXEDSZ;
+
+ // Skip over the queries
+ int queriesCount = ntohs(header->qdcount);
+ while (queriesCount > 0) {
+ int entryLength = dn_skipname(currentEntry, messageEnd);
+ if (entryLength < 0) {
+ throw DomainNameResolveException();
+ }
+ currentEntry += entryLength + NS_QFIXEDSZ;
+ queriesCount--;
+ }
+
+ // Process the SRV answers
+ int answersCount = ntohs(header->ancount);
+ while (answersCount > 0) {
+ int entryLength = dn_skipname(currentEntry, messageEnd);
+ currentEntry += entryLength;
+ currentEntry += NS_RRFIXEDSZ;
+
+ // Uninteresting information
+ currentEntry += 2; // PRIORITY
+ currentEntry += 2; // WEIGHT
+
+ // Port
+ if (currentEntry >= messageEnd) {
+ throw DomainNameResolveException();
+ }
+ int port = ns_get16(currentEntry);
+ currentEntry += 2;
+
+ // Hostname
+ if (currentEntry >= messageEnd) {
+ throw DomainNameResolveException();
+ }
+ ByteArray entry;
+ entry.resize(NS_MAXDNAME);
+ entryLength = dn_expand(messageStart, messageEnd, currentEntry, entry.getData(), entry.getSize());
+ if (entryLength < 0) {
+ throw DomainNameResolveException();
+ }
+ try {
+ // Resolve the hostname
+ std::string hostname(entry.getData(), entryLength);
+ HostAddress address = resolveHostName(hostname);
+ return HostAddressPort(address, port);
+ }
+ catch (const DomainNameResolveException&) {
+ }
+ currentEntry += entryLength;
+ answersCount--;
+ }
+#endif
+
+ throw DomainNameResolveException();
+}
+
+HostAddress DomainNameResolver::resolveHostName(const std::string& hostname) {
+ boost::asio::io_service ioService;
+ boost::asio::ip::tcp::resolver resolver(ioService);
+ boost::asio::ip::tcp::resolver::query query(hostname, "5222");
+ try {
+ boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(query);
+ if (endpointIterator == boost::asio::ip::tcp::resolver::iterator()) {
+ throw DomainNameResolveException();
+ }
+ boost::asio::ip::address address = (*endpointIterator).endpoint().address();
+ if (address.is_v4()) {
+ return HostAddress(&address.to_v4().to_bytes()[0], 4);
+ }
+ else {
+ return HostAddress(&address.to_v6().to_bytes()[0], 16);
+ }
+ }
+ catch (...) {
+ throw DomainNameResolveException();
+ }
+}
+
+}
diff --git a/Swiften/Network/DomainNameResolver.h b/Swiften/Network/DomainNameResolver.h
new file mode 100644
index 0000000..c7736b1
--- /dev/null
+++ b/Swiften/Network/DomainNameResolver.h
@@ -0,0 +1,27 @@
+#ifndef SWIFTEN_DOMAINNAMERESOLVER_H
+#define SWIFTEN_DOMAINNAMERESOLVER_H
+
+#include <string>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/HostAddress.h"
+#include "Swiften/Network/HostAddressPort.h"
+
+namespace Swift {
+ class String;
+
+ class DomainNameResolver {
+ public:
+ DomainNameResolver();
+ virtual ~DomainNameResolver();
+
+ HostAddressPort resolve(const String& domain);
+
+ private:
+ virtual HostAddressPort resolveDomain(const std::string& domain);
+ HostAddressPort resolveXMPPService(const std::string& domain);
+ HostAddress resolveHostName(const std::string& hostName);
+ };
+}
+
+#endif
diff --git a/Swiften/Network/HostAddress.cpp b/Swiften/Network/HostAddress.cpp
new file mode 100644
index 0000000..84a0012
--- /dev/null
+++ b/Swiften/Network/HostAddress.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Network/HostAddress.h"
+
+#include <boost/numeric/conversion/cast.hpp>
+#include <cassert>
+#include <sstream>
+#include <iomanip>
+
+namespace Swift {
+
+HostAddress::HostAddress() {
+ for (int i = 0; i < 4; ++i) {
+ address_.push_back(0);
+ }
+}
+
+HostAddress::HostAddress(const unsigned char* address, int length) {
+ assert(length == 4 || length == 16);
+ address_.reserve(length);
+ for (int i = 0; i < length; ++i) {
+ address_.push_back(address[i]);
+ }
+}
+
+std::string HostAddress::toString() const {
+ if (address_.size() == 4) {
+ std::ostringstream result;
+ for (size_t i = 0; i < address_.size() - 1; ++i) {
+ result << boost::numeric_cast<unsigned int>(address_[i]) << ".";
+ }
+ result << boost::numeric_cast<unsigned int>(address_[address_.size() - 1]);
+ return result.str();
+ }
+ else if (address_.size() == 16) {
+ std::ostringstream result;
+ result << std::hex;
+ result.fill('0');
+ for (size_t i = 0; i < (address_.size() / 2) - 1; ++i) {
+ result << std::setw(2) << boost::numeric_cast<unsigned int>(address_[2*i]) << std::setw(2) << boost::numeric_cast<unsigned int>(address_[(2*i)+1]) << ":";
+ }
+ result << std::setw(2) << boost::numeric_cast<unsigned int>(address_[address_.size() - 2]) << std::setw(2) << boost::numeric_cast<unsigned int>(address_[address_.size() - 1]);
+ return result.str();
+ }
+ else {
+ assert(false);
+ return "";
+ }
+}
+
+}
diff --git a/Swiften/Network/HostAddress.h b/Swiften/Network/HostAddress.h
new file mode 100644
index 0000000..2c9760d
--- /dev/null
+++ b/Swiften/Network/HostAddress.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_HOSTADDRESS
+#define SWIFTEN_HOSTADDRESS
+
+#include <string>
+#include <vector>
+
+namespace Swift {
+ class HostAddress {
+ public:
+ HostAddress();
+ HostAddress(const unsigned char* address, int length);
+
+ const std::vector<unsigned char>& getRawAddress() const {
+ return address_;
+ }
+
+ std::string toString() const;
+
+ private:
+ std::vector<unsigned char> address_;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/HostAddressPort.h b/Swiften/Network/HostAddressPort.h
new file mode 100644
index 0000000..8668ae4
--- /dev/null
+++ b/Swiften/Network/HostAddressPort.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_HostAddressPort_H
+#define SWIFTEN_HostAddressPort_H
+
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+ class HostAddressPort {
+ public:
+ HostAddressPort(const HostAddress& address, int port) : address_(address), port_(port) {
+ }
+
+ const HostAddress& getAddress() const {
+ return address_;
+ }
+
+ int getPort() const {
+ return port_;
+ }
+
+ private:
+ HostAddress address_;
+ int port_;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/Makefile.inc b/Swiften/Network/Makefile.inc
new file mode 100644
index 0000000..055554a
--- /dev/null
+++ b/Swiften/Network/Makefile.inc
@@ -0,0 +1,13 @@
+SWIFTEN_SOURCES += \
+ Swiften/Network/HostAddress.cpp \
+ Swiften/Network/DomainNameResolver.cpp \
+ Swiften/Network/ConnectionFactory.cpp \
+ Swiften/Network/BoostConnection.cpp \
+ Swiften/Network/BoostConnectionFactory.cpp \
+ Swiften/Network/Timer.cpp
+
+include Swiften/Network/UnitTest/Makefile.inc
+
+ifneq ($(WIN32),1)
+LIBS += -lresolv
+endif
diff --git a/Swiften/Network/Timer.cpp b/Swiften/Network/Timer.cpp
new file mode 100644
index 0000000..8b2b57f
--- /dev/null
+++ b/Swiften/Network/Timer.cpp
@@ -0,0 +1,40 @@
+#include "Swiften/Network/Timer.h"
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+Timer::Timer(int milliseconds) :
+ timeout_(milliseconds), ioService_(0), thread_(0), timer_(0) {
+ ioService_ = new boost::asio::io_service();
+}
+
+Timer::~Timer() {
+ MainEventLoop::removeEventsFromOwner(this);
+ ioService_->stop();
+ thread_->join();
+ delete timer_;
+ delete thread_;
+ delete ioService_;
+}
+
+void Timer::start() {
+ thread_ = new boost::thread(boost::bind(&Timer::doStart, this));
+}
+
+void Timer::doStart() {
+ timer_ = new boost::asio::deadline_timer(*ioService_);
+ timer_->expires_from_now(boost::posix_time::milliseconds(timeout_));
+ timer_->async_wait(boost::bind(&Timer::handleTimerTick, this));
+ ioService_->run();
+}
+
+void Timer::handleTimerTick() {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onTick)), this);
+ timer_->expires_from_now(boost::posix_time::milliseconds(timeout_));
+ timer_->async_wait(boost::bind(&Timer::handleTimerTick, this));
+}
+
+}
diff --git a/Swiften/Network/Timer.h b/Swiften/Network/Timer.h
new file mode 100644
index 0000000..8e4b4c2
--- /dev/null
+++ b/Swiften/Network/Timer.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_Timer_H
+#define SWIFTEN_Timer_H
+
+#include <boost/asio.hpp>
+#include <boost/signals.hpp>
+#include <boost/thread.hpp>
+
+namespace Swift {
+ class Timer {
+ public:
+ Timer(int milliseconds);
+ ~Timer();
+
+ void start();
+
+ public:
+ boost::signal<void ()> onTick;
+
+ private:
+ void doStart();
+ void handleTimerTick();
+
+ private:
+ int timeout_;
+ boost::asio::io_service* ioService_;
+ boost::thread* thread_;
+ boost::asio::deadline_timer* timer_;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/UnitTest/HostAddressTest.cpp b/Swiften/Network/UnitTest/HostAddressTest.cpp
new file mode 100644
index 0000000..b805647
--- /dev/null
+++ b/Swiften/Network/UnitTest/HostAddressTest.cpp
@@ -0,0 +1,33 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Network/HostAddress.h"
+
+using namespace Swift;
+
+class HostAddressTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(HostAddressTest);
+ CPPUNIT_TEST(testToString);
+ CPPUNIT_TEST(testToString_IPv6);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ HostAddressTest() {}
+
+ void testToString() {
+ unsigned char address[4] = {10, 0, 1, 253};
+ HostAddress testling(address, 4);
+
+ CPPUNIT_ASSERT_EQUAL(std::string("10.0.1.253"), testling.toString());
+ }
+
+ void testToString_IPv6() {
+ unsigned char address[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17};
+ HostAddress testling(address, 16);
+
+ CPPUNIT_ASSERT_EQUAL(std::string("0102:0304:0506:0708:090a:0b0c:0d0e:0f11"), testling.toString());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(HostAddressTest);
diff --git a/Swiften/Network/UnitTest/Makefile.inc b/Swiften/Network/UnitTest/Makefile.inc
new file mode 100644
index 0000000..4f157f6
--- /dev/null
+++ b/Swiften/Network/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+ Swiften/Network/UnitTest/HostAddressTest.cpp
diff --git a/Swiften/Notifier/GrowlNotifier.cpp b/Swiften/Notifier/GrowlNotifier.cpp
new file mode 100644
index 0000000..13d3e6d
--- /dev/null
+++ b/Swiften/Notifier/GrowlNotifier.cpp
@@ -0,0 +1,91 @@
+// FIXME: Is it safe to pass boost::function<void()> by raw values?
+// FIXME: Should we release the strings created in the constructor?
+
+#include <cassert>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Notifier/GrowlNotifier.h"
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+namespace {
+ struct Context {
+ Context() {}
+ Context(const boost::function<void()>& callback) : callback(callback) {}
+
+ boost::function<void()> callback;
+ };
+
+ void notificationClicked(CFPropertyListRef growlContext) {
+ Context context;
+
+ CFDataRef growlContextData = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) growlContext, 0);
+ assert(CFDataGetLength(growlContextData) == sizeof(Context));
+ CFDataGetBytes(growlContextData, CFRangeMake(0, CFDataGetLength(growlContextData)), (UInt8*) &context);
+
+ context.callback();
+ }
+
+ void notificationTimedout(CFPropertyListRef) {
+ }
+}
+
+namespace Swift {
+
+GrowlNotifier::GrowlNotifier(const String& name) {
+ // All notifications
+ CFMutableArrayRef allNotifications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactAvailable)));
+ CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactUnavailable)));
+ CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactStatusChange)));
+ CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(IncomingMessage)));
+
+ // Default Notifications
+ CFMutableArrayRef defaultNotifications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(defaultNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactAvailable)));
+ CFArrayAppendValue(defaultNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(IncomingMessage)));
+
+ // Initialize delegate
+ InitGrowlDelegate(&delegate_);
+ delegate_.applicationName = SWIFTEN_STRING_TO_CFSTRING(name);
+ CFTypeRef keys[] = { GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT };
+ CFTypeRef values[] = { allNotifications, defaultNotifications };
+ delegate_.registrationDictionary = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ delegate_.growlNotificationWasClicked = &notificationClicked;
+ delegate_.growlNotificationTimedOut = &notificationTimedout;
+ Growl_SetDelegate(&delegate_);
+}
+
+void GrowlNotifier::showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function<void()> callback) {
+ CFStringRef cfSubject = SWIFTEN_STRING_TO_CFSTRING(subject);
+ CFStringRef cfDescription = SWIFTEN_STRING_TO_CFSTRING(description);
+ CFStringRef cfName = SWIFTEN_STRING_TO_CFSTRING(typeToString(type));
+ CFDataRef cfIcon = CFDataCreate( NULL, (UInt8*) picture.getData(), picture.getSize());
+
+ Context context(callback);
+ CFDataRef cfContextData[1];
+ cfContextData[0] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &context, sizeof(Context));
+ CFArrayRef cfContext = CFArrayCreate( kCFAllocatorDefault, (const void **) cfContextData, 1, &kCFTypeArrayCallBacks );
+ CFRelease(cfContextData[0]);
+
+ Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(cfSubject, cfDescription, cfName, cfIcon, 0, false, cfContext);
+
+ CFRelease(cfContext);
+ CFRelease(cfIcon);
+ CFRelease(cfName);
+ CFRelease(cfDescription);
+ CFRelease(cfSubject);
+}
+
+String GrowlNotifier::typeToString(Type type) {
+ switch (type) {
+ case ContactAvailable: return "Contact Becomes Available";
+ case ContactUnavailable: return "Contact Becomes Unavailable";
+ case ContactStatusChange: return "Contact Changes Status";
+ case IncomingMessage: return "Incoming Message";
+ }
+ assert(false);
+ return "";
+}
+
+}
diff --git a/Swiften/Notifier/GrowlNotifier.h b/Swiften/Notifier/GrowlNotifier.h
new file mode 100644
index 0000000..3bf53be
--- /dev/null
+++ b/Swiften/Notifier/GrowlNotifier.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Growl/Growl.h>
+
+#include "Swiften/Notifier/Notifier.h"
+
+namespace Swift {
+ /**
+ * Preconditions for using growlnotifier:
+ * - Must be part a bundle.
+ * - The Carbon/Cocoa application loop must be running (e.g. through QApplication)
+ * such that notifications are coming through.
+ * TODO: Find out what the easiest way is to do this without a QApplication.
+ */
+ class GrowlNotifier : public Notifier {
+ public:
+ GrowlNotifier(const String& name);
+
+ virtual void showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function<void()> callback);
+
+ private:
+ String typeToString(Type type);
+
+ private:
+ Growl_Delegate delegate_;
+ };
+}
diff --git a/Swiften/Notifier/Makefile.inc b/Swiften/Notifier/Makefile.inc
new file mode 100644
index 0000000..6d7e79a
--- /dev/null
+++ b/Swiften/Notifier/Makefile.inc
@@ -0,0 +1,7 @@
+SWIFTEN_SOURCES += \
+ Swiften/Notifier/Notifier.cpp
+
+ifeq ($(HAVE_GROWL),yes)
+ SWIFTEN_SOURCES += \
+ Swiften/Notifier/GrowlNotifier.cpp
+endif
diff --git a/Swiften/Notifier/Notifier.cpp b/Swiften/Notifier/Notifier.cpp
new file mode 100644
index 0000000..88cb0ee
--- /dev/null
+++ b/Swiften/Notifier/Notifier.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Notifier/Notifier.h"
+
+namespace Swift {
+
+Notifier::~Notifier() {
+}
+
+}
diff --git a/Swiften/Notifier/Notifier.h b/Swiften/Notifier/Notifier.h
new file mode 100644
index 0000000..2ab3ba8
--- /dev/null
+++ b/Swiften/Notifier/Notifier.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/function.hpp>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class Notifier {
+ public:
+ virtual ~Notifier();
+
+ enum Type { ContactAvailable, ContactUnavailable, ContactStatusChange, IncomingMessage };
+
+ /**
+ * Picture is a PNG image.
+ */
+ virtual void showMessage(
+ Type type,
+ const String& subject,
+ const String& description,
+ const ByteArray& picture,
+ boost::function<void()> callback) = 0;
+ };
+}
diff --git a/Swiften/Parser/AttributeMap.h b/Swiften/Parser/AttributeMap.h
new file mode 100644
index 0000000..6662b98
--- /dev/null
+++ b/Swiften/Parser/AttributeMap.h
@@ -0,0 +1,25 @@
+#ifndef ATTRIBUTEMAP_H
+#define ATTRIBUTEMAP_H
+
+#include <map>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class AttributeMap : public std::map<String,String> {
+ public:
+ AttributeMap() {}
+
+ String getAttribute(const String& attribute) const {
+ AttributeMap::const_iterator i = find(attribute);
+ if (i == end()) {
+ return "";
+ }
+ else {
+ return i->second;
+ }
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/AuthFailureParser.h b/Swiften/Parser/AuthFailureParser.h
new file mode 100644
index 0000000..3a950ef
--- /dev/null
+++ b/Swiften/Parser/AuthFailureParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_AuthFailureParser_H
+#define SWIFTEN_AuthFailureParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/AuthFailure.h"
+
+namespace Swift {
+ class AuthFailureParser : public GenericElementParser<AuthFailure> {
+ public:
+ AuthFailureParser() : GenericElementParser<AuthFailure>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/AuthRequestParser.cpp b/Swiften/Parser/AuthRequestParser.cpp
new file mode 100644
index 0000000..5338b88
--- /dev/null
+++ b/Swiften/Parser/AuthRequestParser.cpp
@@ -0,0 +1,27 @@
+#include "Swiften/Parser/AuthRequestParser.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthRequestParser::AuthRequestParser() : GenericElementParser<AuthRequest>(), depth_(0) {
+}
+
+void AuthRequestParser::handleStartElement(const String&, const String&, const AttributeMap& attribute) {
+ if (depth_ == 0) {
+ getElementGeneric()->setMechanism(attribute.getAttribute("mechanism"));
+ }
+ ++depth_;
+}
+
+void AuthRequestParser::handleEndElement(const String&, const String&) {
+ --depth_;
+ if (depth_ == 0) {
+ getElementGeneric()->setMessage(Base64::decode(text_));
+ }
+}
+
+void AuthRequestParser::handleCharacterData(const String& text) {
+ text_ += text;
+}
+
+}
diff --git a/Swiften/Parser/AuthRequestParser.h b/Swiften/Parser/AuthRequestParser.h
new file mode 100644
index 0000000..8916922
--- /dev/null
+++ b/Swiften/Parser/AuthRequestParser.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_AuthRequestParser_H
+#define SWIFTEN_AuthRequestParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class AuthRequestParser : public GenericElementParser<AuthRequest> {
+ public:
+ AuthRequestParser();
+
+ virtual void handleStartElement(const String&, const String& ns, const AttributeMap&);
+ virtual void handleEndElement(const String&, const String& ns);
+ virtual void handleCharacterData(const String&);
+
+ private:
+ String text_;
+ int depth_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/AuthSuccessParser.h b/Swiften/Parser/AuthSuccessParser.h
new file mode 100644
index 0000000..bb6515d
--- /dev/null
+++ b/Swiften/Parser/AuthSuccessParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_AUTHSUCCESSPARSER_H
+#define SWIFTEN_AUTHSUCCESSPARSER_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/AuthSuccess.h"
+
+namespace Swift {
+ class AuthSuccessParser : public GenericElementParser<AuthSuccess> {
+ public:
+ AuthSuccessParser() : GenericElementParser<AuthSuccess>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/CompressFailureParser.h b/Swiften/Parser/CompressFailureParser.h
new file mode 100644
index 0000000..d53e0ef
--- /dev/null
+++ b/Swiften/Parser/CompressFailureParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_CompressFailureParser_H
+#define SWIFTEN_CompressFailureParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/CompressFailure.h"
+
+namespace Swift {
+ class CompressFailureParser : public GenericElementParser<CompressFailure> {
+ public:
+ CompressFailureParser() : GenericElementParser<CompressFailure>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/CompressParser.cpp b/Swiften/Parser/CompressParser.cpp
new file mode 100644
index 0000000..7ca752d
--- /dev/null
+++ b/Swiften/Parser/CompressParser.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Parser/CompressParser.h"
+
+namespace Swift {
+
+CompressParser::CompressParser() : GenericElementParser<CompressRequest>(), currentDepth_(0), inMethod_(false) {
+}
+
+void CompressParser::handleStartElement(const String& element, const String&, const AttributeMap&) {
+ if (currentDepth_ == 1 && element == "method") {
+ inMethod_ = true;
+ currentText_ = "";
+ }
+ ++currentDepth_;
+}
+
+void CompressParser::handleEndElement(const String&, const String&) {
+ --currentDepth_;
+ if (currentDepth_ == 1 && inMethod_) {
+ getElementGeneric()->setMethod(currentText_);
+ inMethod_ = false;
+ }
+}
+
+void CompressParser::handleCharacterData(const String& data) {
+ currentText_ += data;
+}
+
+}
diff --git a/Swiften/Parser/CompressParser.h b/Swiften/Parser/CompressParser.h
new file mode 100644
index 0000000..8931778
--- /dev/null
+++ b/Swiften/Parser/CompressParser.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_CompressParser_H
+#define SWIFTEN_CompressParser_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/CompressRequest.h"
+
+namespace Swift {
+ class CompressParser : public GenericElementParser<CompressRequest> {
+ public:
+ CompressParser();
+
+ private:
+ void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes);
+ void handleEndElement(const String& element, const String& ns);
+ void handleCharacterData(const String& data);
+
+ private:
+ int currentDepth_;
+ String currentText_;
+ bool inMethod_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/CompressedParser.h b/Swiften/Parser/CompressedParser.h
new file mode 100644
index 0000000..365f619
--- /dev/null
+++ b/Swiften/Parser/CompressedParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_COMPRESSEDPARSER_H
+#define SWIFTEN_COMPRESSEDPARSER_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/Compressed.h"
+
+namespace Swift {
+ class CompressedParser : public GenericElementParser<Compressed> {
+ public:
+ CompressedParser() : GenericElementParser<Compressed>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/ElementParser.cpp b/Swiften/Parser/ElementParser.cpp
new file mode 100644
index 0000000..1c04d92
--- /dev/null
+++ b/Swiften/Parser/ElementParser.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Parser/ElementParser.h"
+
+namespace Swift {
+
+ElementParser::~ElementParser() {
+}
+
+}
diff --git a/Swiften/Parser/ElementParser.h b/Swiften/Parser/ElementParser.h
new file mode 100644
index 0000000..3848f0c
--- /dev/null
+++ b/Swiften/Parser/ElementParser.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_ElementParser_H
+#define SWIFTEN_ElementParser_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Parser/AttributeMap.h"
+
+namespace Swift {
+ class ElementParser {
+ public:
+ virtual ~ElementParser();
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) = 0;
+ virtual void handleEndElement(const String& element, const String& ns) = 0;
+ virtual void handleCharacterData(const String& data) = 0;
+
+ virtual boost::shared_ptr<Element> getElement() const = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/ExpatParser.cpp b/Swiften/Parser/ExpatParser.cpp
new file mode 100644
index 0000000..6d23c93
--- /dev/null
+++ b/Swiften/Parser/ExpatParser.cpp
@@ -0,0 +1,66 @@
+#include "Swiften/Parser/ExpatParser.h"
+
+#include <iostream>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/XMLParserClient.h"
+
+namespace Swift {
+
+static const char NAMESPACE_SEPARATOR = '\x01';
+
+static void handleStartElement(void* client, const XML_Char* name, const XML_Char** attributes) {
+ std::pair<String,String> nsTagPair = String(name).getSplittedAtFirst(NAMESPACE_SEPARATOR);
+ if (nsTagPair.second == "") {
+ nsTagPair.second = nsTagPair.first;
+ nsTagPair.first = "";
+ }
+ AttributeMap attributeValues;
+ const XML_Char** currentAttribute = attributes;
+ while (*currentAttribute) {
+ std::pair<String,String> nsAttributePair = String(*currentAttribute).getSplittedAtFirst(NAMESPACE_SEPARATOR);
+ if (nsAttributePair.second == "") {
+ nsAttributePair.second = nsAttributePair.first;
+ nsAttributePair.first = "";
+ }
+ attributeValues[nsAttributePair.second] = String(*(currentAttribute+1));
+ currentAttribute += 2;
+ }
+
+ static_cast<XMLParserClient*>(client)->handleStartElement(nsTagPair.second, nsTagPair.first, attributeValues);
+}
+
+static void handleEndElement(void* client, const XML_Char* name) {
+ std::pair<String,String> nsTagPair = String(name).getSplittedAtFirst(NAMESPACE_SEPARATOR);
+ static_cast<XMLParserClient*>(client)->handleEndElement(nsTagPair.second, nsTagPair.first);
+}
+
+static void handleCharacterData(void* client, const XML_Char* data, int len) {
+ static_cast<XMLParserClient*>(client)->handleCharacterData(String(data, len));
+}
+
+static void handleXMLDeclaration(void*, const XML_Char*, const XML_Char*, int) {
+}
+
+
+ExpatParser::ExpatParser(XMLParserClient* client) : XMLParser(client) {
+ parser_ = XML_ParserCreateNS("UTF-8", NAMESPACE_SEPARATOR);
+ XML_SetUserData(parser_, client);
+ XML_SetElementHandler(parser_, handleStartElement, handleEndElement);
+ XML_SetCharacterDataHandler(parser_, handleCharacterData);
+ XML_SetXmlDeclHandler(parser_, handleXMLDeclaration);
+}
+
+ExpatParser::~ExpatParser() {
+ XML_ParserFree(parser_);
+}
+
+bool ExpatParser::parse(const String& data) {
+ bool success = XML_Parse(parser_, data.getUTF8Data(), data.getUTF8Size(), false) == XML_STATUS_OK;
+ /*if (!success) {
+ std::cout << "ERROR: " << XML_ErrorString(XML_GetErrorCode(parser_)) << " while parsing " << data << std::endl;
+ }*/
+ return success;
+}
+
+}
diff --git a/Swiften/Parser/ExpatParser.h b/Swiften/Parser/ExpatParser.h
new file mode 100644
index 0000000..2b5e646
--- /dev/null
+++ b/Swiften/Parser/ExpatParser.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_ExpatParser_H
+#define SWIFTEN_ExpatParser_H
+
+#include <expat.h>
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Parser/XMLParser.h"
+
+namespace Swift {
+ class ExpatParser : public XMLParser, public boost::noncopyable {
+ public:
+ ExpatParser(XMLParserClient* client);
+ ~ExpatParser();
+
+ bool parse(const String& data);
+
+ private:
+ XML_Parser parser_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/GenericElementParser.h b/Swiften/Parser/GenericElementParser.h
new file mode 100644
index 0000000..e1b9cf7
--- /dev/null
+++ b/Swiften/Parser/GenericElementParser.h
@@ -0,0 +1,43 @@
+#ifndef SWIFTEN_GenericElementParser_H
+#define SWIFTEN_GenericElementParser_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Parser/ElementParser.h"
+
+namespace Swift {
+ class String;
+ class PayloadParserFactoryCollection;
+
+ template<typename ElementType>
+ class GenericElementParser : public ElementParser {
+ public:
+ GenericElementParser() {
+ stanza_ = boost::shared_ptr<ElementType>(new ElementType());
+ }
+
+ virtual boost::shared_ptr<Element> getElement() const {
+ return stanza_;
+ }
+
+ protected:
+ virtual boost::shared_ptr<ElementType> getElementGeneric() const {
+ return stanza_;
+ }
+
+ private:
+ virtual void handleStartElement(const String&, const String&, const AttributeMap&) {
+ }
+
+ virtual void handleEndElement(const String&, const String&) {
+ }
+
+ virtual void handleCharacterData(const String&) {
+ }
+
+ private:
+ boost::shared_ptr<ElementType> stanza_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/GenericPayloadParser.h b/Swiften/Parser/GenericPayloadParser.h
new file mode 100644
index 0000000..a07b795
--- /dev/null
+++ b/Swiften/Parser/GenericPayloadParser.h
@@ -0,0 +1,32 @@
+#ifndef GENERICPAYLOADPARSER_H
+#define GENERICPAYLOADPARSER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Parser/PayloadParser.h"
+
+namespace Swift {
+ class String;
+
+ template<typename PAYLOAD_TYPE>
+ class GenericPayloadParser : public PayloadParser {
+ public:
+ GenericPayloadParser() : PayloadParser() {
+ payload_ = boost::shared_ptr<PAYLOAD_TYPE>(new PAYLOAD_TYPE());
+ }
+
+ virtual boost::shared_ptr<Payload> getPayload() const {
+ return payload_;
+ }
+
+ protected:
+ virtual boost::shared_ptr<PAYLOAD_TYPE> getPayloadInternal() const {
+ return payload_;
+ }
+
+ private:
+ boost::shared_ptr<PAYLOAD_TYPE> payload_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/GenericPayloadParserFactory.h b/Swiften/Parser/GenericPayloadParserFactory.h
new file mode 100644
index 0000000..d537b46
--- /dev/null
+++ b/Swiften/Parser/GenericPayloadParserFactory.h
@@ -0,0 +1,28 @@
+#ifndef SWIFTEN_GENERICPAYLOADPARSERFACTORY_H
+#define SWIFTEN_GENERICPAYLOADPARSERFACTORY_H
+
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+
+ template<typename PARSER_TYPE>
+ class GenericPayloadParserFactory : public PayloadParserFactory {
+ public:
+ GenericPayloadParserFactory(const String& tag, const String& xmlns = "") : tag_(tag), xmlns_(xmlns) {}
+
+ virtual bool canParse(const String& element, const String& ns, const AttributeMap&) const {
+ return element == tag_ && (xmlns_.isEmpty() ? true : xmlns_ == ns);
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new PARSER_TYPE();
+ }
+
+ private:
+ String tag_;
+ String xmlns_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/GenericStanzaParser.h b/Swiften/Parser/GenericStanzaParser.h
new file mode 100644
index 0000000..ba1807a
--- /dev/null
+++ b/Swiften/Parser/GenericStanzaParser.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_GenericStanzaParser_H
+#define SWIFTEN_GenericStanzaParser_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Parser/StanzaParser.h"
+
+namespace Swift {
+ class String;
+ class PayloadParserFactoryCollection;
+
+ template<typename STANZA_TYPE>
+ class GenericStanzaParser : public StanzaParser {
+ public:
+ GenericStanzaParser(PayloadParserFactoryCollection* collection) :
+ StanzaParser(collection) {
+ stanza_ = boost::shared_ptr<STANZA_TYPE>(new STANZA_TYPE());
+ }
+
+ virtual boost::shared_ptr<Element> getElement() const {
+ return stanza_;
+ }
+
+ virtual boost::shared_ptr<STANZA_TYPE> getStanzaGeneric() const {
+ return stanza_;
+ }
+
+ private:
+ boost::shared_ptr<STANZA_TYPE> stanza_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/IQParser.cpp b/Swiften/Parser/IQParser.cpp
new file mode 100644
index 0000000..2b4b364
--- /dev/null
+++ b/Swiften/Parser/IQParser.cpp
@@ -0,0 +1,33 @@
+#include <iostream>
+
+#include "Swiften/Parser/IQParser.h"
+
+namespace Swift {
+
+IQParser::IQParser(PayloadParserFactoryCollection* factories) :
+ GenericStanzaParser<IQ>(factories) {
+}
+
+void IQParser::handleStanzaAttributes(const AttributeMap& attributes) {
+ AttributeMap::const_iterator type = attributes.find("type");
+ if (type != attributes.end()) {
+ if (type->second == "set") {
+ getStanzaGeneric()->setType(IQ::Set);
+ }
+ else if (type->second == "get") {
+ getStanzaGeneric()->setType(IQ::Get);
+ }
+ else if (type->second == "result") {
+ getStanzaGeneric()->setType(IQ::Result);
+ }
+ else if (type->second == "error") {
+ getStanzaGeneric()->setType(IQ::Error);
+ }
+ else {
+ std::cerr << "Unknown IQ type: " << type->second << std::endl;
+ getStanzaGeneric()->setType(IQ::Get);
+ }
+ }
+}
+
+}
diff --git a/Swiften/Parser/IQParser.h b/Swiften/Parser/IQParser.h
new file mode 100644
index 0000000..cd2fc05
--- /dev/null
+++ b/Swiften/Parser/IQParser.h
@@ -0,0 +1,17 @@
+#ifndef SWIFTEN_IQParser_H
+#define SWIFTEN_IQParser_H
+
+#include "Swiften/Parser/GenericStanzaParser.h"
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+ class IQParser : public GenericStanzaParser<IQ> {
+ public:
+ IQParser(PayloadParserFactoryCollection* factories);
+
+ private:
+ virtual void handleStanzaAttributes(const AttributeMap&);
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/LibXMLParser.cpp b/Swiften/Parser/LibXMLParser.cpp
new file mode 100644
index 0000000..8f14f21
--- /dev/null
+++ b/Swiften/Parser/LibXMLParser.cpp
@@ -0,0 +1,63 @@
+#include "Swiften/Parser/LibXMLParser.h"
+
+#include <iostream>
+#include <cassert>
+#include <cstring>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/XMLParserClient.h"
+
+namespace Swift {
+
+static void handleStartElement(void *client, const xmlChar* name, const xmlChar** attributes) {
+ AttributeMap attributeValues;
+ const xmlChar** currentAttribute = attributes;
+ if (currentAttribute) {
+ while (*currentAttribute) {
+ attributeValues[String(reinterpret_cast<const char*>(*currentAttribute))] = String(reinterpret_cast<const char*>(*(currentAttribute+1)));
+ currentAttribute += 2;
+ }
+ }
+ static_cast<XMLParserClient*>(client)->handleStartElement(reinterpret_cast<const char*>(name), attributeValues);
+}
+
+static void handleEndElement(void *client, const xmlChar* name) {
+ static_cast<XMLParserClient*>(client)->handleEndElement(reinterpret_cast<const char*>(name));
+}
+
+static void handleCharacterData(void* client, const xmlChar* data, int len) {
+ static_cast<XMLParserClient*>(client)->handleCharacterData(String(reinterpret_cast<const char*>(data), len));
+}
+
+static void handleError(void*, const char*, ... ) {
+}
+
+static void handleWarning(void*, const char*, ... ) {
+}
+
+
+
+LibXMLParser::LibXMLParser(XMLParserClient* client) : XMLParser(client) {
+ memset(&handler_, 0, sizeof(handler_) );
+ handler_.initialized = XML_SAX2_MAGIC;
+ handler_.startElement = &handleStartElement;
+ handler_.endElement = &handleEndElement;
+ handler_.characters = &handleCharacterData;
+ handler_.warning = &handleWarning;
+ handler_.error = &handleError;
+
+ context_ = xmlCreatePushParserCtxt(&handler_, client, 0, 0, 0);
+ assert(context_);
+}
+
+LibXMLParser::~LibXMLParser() {
+ if (context_) {
+ xmlFreeParserCtxt(context_);
+ }
+}
+
+bool LibXMLParser::parse(const String& data) {
+ return xmlParseChunk(context_, data.getUTF8Data(), data.getUTF8Size(), false) == XML_ERR_OK;
+}
+
+}
diff --git a/Swiften/Parser/LibXMLParser.h b/Swiften/Parser/LibXMLParser.h
new file mode 100644
index 0000000..f8faef6
--- /dev/null
+++ b/Swiften/Parser/LibXMLParser.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_LibXMLParser_H
+#define SWIFTEN_LibXMLParser_H
+
+#include <libxml/parser.h>
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Parser/XMLParser.h"
+
+namespace Swift {
+ class LibXMLParser : public XMLParser, public boost::noncopyable {
+ public:
+ LibXMLParser(XMLParserClient* client);
+ ~LibXMLParser();
+
+ bool parse(const String& data);
+
+ private:
+ xmlSAXHandler handler_;
+ xmlParserCtxtPtr context_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/Makefile.inc b/Swiften/Parser/Makefile.inc
new file mode 100644
index 0000000..f47ae29
--- /dev/null
+++ b/Swiften/Parser/Makefile.inc
@@ -0,0 +1,32 @@
+SWIFTEN_SOURCES += \
+ Swiften/Parser/XMLParser.cpp \
+ Swiften/Parser/XMLParserClient.cpp \
+ Swiften/Parser/XMLParserFactory.cpp \
+ Swiften/Parser/PlatformXMLParserFactory.cpp \
+ Swiften/Parser/XMPPParser.cpp \
+ Swiften/Parser/XMPPParserClient.cpp \
+ Swiften/Parser/MessageParser.cpp \
+ Swiften/Parser/IQParser.cpp \
+ Swiften/Parser/PresenceParser.cpp \
+ Swiften/Parser/StreamFeaturesParser.cpp \
+ Swiften/Parser/CompressParser.cpp \
+ Swiften/Parser/AuthRequestParser.cpp \
+ Swiften/Parser/StanzaParser.cpp \
+ Swiften/Parser/ElementParser.cpp \
+ Swiften/Parser/PayloadParser.cpp \
+ Swiften/Parser/PayloadParserFactory.cpp \
+ Swiften/Parser/PayloadParserFactoryCollection.cpp \
+ Swiften/Parser/SerializingParser.cpp
+
+ifeq ($(HAVE_LIBXML),yes)
+SWIFTEN_SOURCES += \
+ Swiften/Parser/LibXMLParser.cpp
+endif
+
+ifeq ($(HAVE_EXPAT),yes)
+SWIFTEN_SOURCES += \
+ Swiften/Parser/ExpatParser.cpp
+endif
+
+include Swiften/Parser/PayloadParsers/Makefile.inc
+include Swiften/Parser/UnitTest/Makefile.inc
diff --git a/Swiften/Parser/MessageParser.cpp b/Swiften/Parser/MessageParser.cpp
new file mode 100644
index 0000000..5e83fad
--- /dev/null
+++ b/Swiften/Parser/MessageParser.cpp
@@ -0,0 +1,33 @@
+#include <iostream>
+
+#include "Swiften/Parser/MessageParser.h"
+
+namespace Swift {
+
+MessageParser::MessageParser(PayloadParserFactoryCollection* factories) :
+ GenericStanzaParser<Message>(factories) {
+ getStanzaGeneric()->setType(Message::Normal);
+}
+
+void MessageParser::handleStanzaAttributes(const AttributeMap& attributes) {
+ AttributeMap::const_iterator type = attributes.find("type");
+ if (type != attributes.end()) {
+ if (type->second == "chat") {
+ getStanzaGeneric()->setType(Message::Chat);
+ }
+ else if (type->second == "error") {
+ getStanzaGeneric()->setType(Message::Error);
+ }
+ else if (type->second == "groupchat") {
+ getStanzaGeneric()->setType(Message::Groupchat);
+ }
+ else if (type->second == "headline") {
+ getStanzaGeneric()->setType(Message::Headline);
+ }
+ else {
+ getStanzaGeneric()->setType(Message::Normal);
+ }
+ }
+}
+
+}
diff --git a/Swiften/Parser/MessageParser.h b/Swiften/Parser/MessageParser.h
new file mode 100644
index 0000000..05c9280
--- /dev/null
+++ b/Swiften/Parser/MessageParser.h
@@ -0,0 +1,17 @@
+#ifndef SWIFTEN_MESSAGEPARSER_H
+#define SWIFTEN_MESSAGEPARSER_H
+
+#include "Swiften/Parser/GenericStanzaParser.h"
+#include "Swiften/Elements/Message.h"
+
+namespace Swift {
+ class MessageParser : public GenericStanzaParser<Message> {
+ public:
+ MessageParser(PayloadParserFactoryCollection* factories);
+
+ private:
+ virtual void handleStanzaAttributes(const AttributeMap&);
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParser.cpp b/Swiften/Parser/PayloadParser.cpp
new file mode 100644
index 0000000..072edef
--- /dev/null
+++ b/Swiften/Parser/PayloadParser.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Parser/PayloadParser.h"
+
+namespace Swift {
+
+PayloadParser::~PayloadParser() {
+}
+
+}
diff --git a/Swiften/Parser/PayloadParser.h b/Swiften/Parser/PayloadParser.h
new file mode 100644
index 0000000..fc1e1c8
--- /dev/null
+++ b/Swiften/Parser/PayloadParser.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_PAYLOADPARSER_H
+#define SWIFTEN_PAYLOADPARSER_H
+
+#include <boost/shared_ptr.hpp>
+#include "Swiften/Parser/AttributeMap.h"
+
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class String;
+
+ class PayloadParser {
+ public:
+ virtual ~PayloadParser();
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) = 0;
+ virtual void handleEndElement(const String& element, const String& ns) = 0;
+ virtual void handleCharacterData(const String& data) = 0;
+
+ virtual boost::shared_ptr<Payload> getPayload() const = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParserFactory.cpp b/Swiften/Parser/PayloadParserFactory.cpp
new file mode 100644
index 0000000..b31f8ae
--- /dev/null
+++ b/Swiften/Parser/PayloadParserFactory.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Parser/PayloadParserFactory.h"
+
+namespace Swift {
+
+PayloadParserFactory::~PayloadParserFactory() {
+}
+
+}
diff --git a/Swiften/Parser/PayloadParserFactory.h b/Swiften/Parser/PayloadParserFactory.h
new file mode 100644
index 0000000..728b4e8
--- /dev/null
+++ b/Swiften/Parser/PayloadParserFactory.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_PAYLOADPARSERFACTORY_H
+#define SWIFTEN_PAYLOADPARSERFACTORY_H
+
+#include "Swiften/Parser/AttributeMap.h"
+
+namespace Swift {
+ class String;
+ class PayloadParser;
+
+ class PayloadParserFactory {
+ public:
+ virtual ~PayloadParserFactory();
+
+ virtual bool canParse(const String& element, const String& ns, const AttributeMap& attributes) const = 0;
+ virtual PayloadParser* createPayloadParser() = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParserFactoryCollection.cpp
new file mode 100644
index 0000000..f361ce8
--- /dev/null
+++ b/Swiften/Parser/PayloadParserFactoryCollection.cpp
@@ -0,0 +1,27 @@
+#include <boost/bind.hpp>
+#include <algorithm>
+
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+
+namespace Swift {
+
+PayloadParserFactoryCollection::PayloadParserFactoryCollection() {
+}
+
+void PayloadParserFactoryCollection::addFactory(PayloadParserFactory* factory) {
+ factories_.push_back(factory);
+}
+
+void PayloadParserFactoryCollection::removeFactory(PayloadParserFactory* factory) {
+ factories_.erase(remove(factories_.begin(), factories_.end(), factory), factories_.end());
+}
+
+PayloadParserFactory* PayloadParserFactoryCollection::getPayloadParserFactory(const String& element, const String& ns, const AttributeMap& attributes) {
+ std::vector<PayloadParserFactory*>::const_iterator i = std::find_if(
+ factories_.begin(), factories_.end(),
+ boost::bind(&PayloadParserFactory::canParse, _1, element, ns, attributes));
+ return (i != factories_.end() ? *i : NULL);
+}
+
+}
diff --git a/Swiften/Parser/PayloadParserFactoryCollection.h b/Swiften/Parser/PayloadParserFactoryCollection.h
new file mode 100644
index 0000000..fad94c3
--- /dev/null
+++ b/Swiften/Parser/PayloadParserFactoryCollection.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_PAYLOADPARSERFACTORYCOLLECTION_H
+#define SWIFTEN_PAYLOADPARSERFACTORYCOLLECTION_H
+
+#include <vector>
+
+#include "Swiften/Parser/AttributeMap.h"
+
+namespace Swift {
+ class PayloadParserFactory;
+ class String;
+
+ class PayloadParserFactoryCollection {
+ public:
+ PayloadParserFactoryCollection();
+
+ void addFactory(PayloadParserFactory* factory);
+ void removeFactory(PayloadParserFactory* factory);
+ PayloadParserFactory* getPayloadParserFactory(const String& element, const String& ns, const AttributeMap& attributes);
+
+ private:
+ std::vector<PayloadParserFactory*> factories_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/BodyParser.cpp b/Swiften/Parser/PayloadParsers/BodyParser.cpp
new file mode 100644
index 0000000..e5898ff
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/BodyParser.cpp
@@ -0,0 +1,23 @@
+#include "Swiften/Parser/PayloadParsers/BodyParser.h"
+
+namespace Swift {
+
+BodyParser::BodyParser() : level_(0) {
+}
+
+void BodyParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++level_;
+}
+
+void BodyParser::handleEndElement(const String&, const String&) {
+ --level_;
+ if (level_ == 0) {
+ getPayloadInternal()->setText(text_);
+ }
+}
+
+void BodyParser::handleCharacterData(const String& data) {
+ text_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/BodyParser.h b/Swiften/Parser/PayloadParsers/BodyParser.h
new file mode 100644
index 0000000..2d272ea
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/BodyParser.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_BodyParser_H
+#define SWIFTEN_BodyParser_H
+
+#include "Swiften/Elements/Body.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class BodyParser : public GenericPayloadParser<Body> {
+ public:
+ BodyParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ int level_;
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/BodyParserFactory.h b/Swiften/Parser/PayloadParsers/BodyParserFactory.h
new file mode 100644
index 0000000..3da7393
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/BodyParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_BodyParserFACTORY_H
+#define SWIFTEN_BodyParserFACTORY_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/BodyParser.h"
+
+namespace Swift {
+ class BodyParserFactory : public GenericPayloadParserFactory<BodyParser> {
+ public:
+ BodyParserFactory() : GenericPayloadParserFactory<BodyParser>("body") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp b/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp
new file mode 100644
index 0000000..ffa24ad
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp
@@ -0,0 +1,27 @@
+#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h"
+
+namespace Swift {
+
+DiscoInfoParser::DiscoInfoParser() : level_(TopLevel) {
+}
+
+void DiscoInfoParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+ if (level_ == PayloadLevel) {
+ if (element == "identity") {
+ getPayloadInternal()->addIdentity(DiscoInfo::Identity(attributes.getAttribute("name"), attributes.getAttribute("category"), attributes.getAttribute("type"), attributes.getAttribute("lang")));
+ }
+ else if (element == "feature") {
+ getPayloadInternal()->addFeature(attributes.getAttribute("var"));
+ }
+ }
+ ++level_;
+}
+
+void DiscoInfoParser::handleEndElement(const String&, const String&) {
+ --level_;
+}
+
+void DiscoInfoParser::handleCharacterData(const String&) {
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/DiscoInfoParser.h b/Swiften/Parser/PayloadParsers/DiscoInfoParser.h
new file mode 100644
index 0000000..b7be972
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/DiscoInfoParser.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_DiscoInfoParser_H
+#define SWIFTEN_DiscoInfoParser_H
+
+#include "Swiften/Elements/DiscoInfo.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class DiscoInfoParser : public GenericPayloadParser<DiscoInfo> {
+ public:
+ DiscoInfoParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1
+ };
+ int level_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h b/Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h
new file mode 100644
index 0000000..ef1c31c
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_DiscoInfoParserFactory_H
+#define SWIFTEN_DiscoInfoParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h"
+
+namespace Swift {
+ class DiscoInfoParserFactory : public GenericPayloadParserFactory<DiscoInfoParser> {
+ public:
+ DiscoInfoParserFactory() : GenericPayloadParserFactory<DiscoInfoParser>("query", "http://jabber.org/protocol/disco#info") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/ErrorParser.cpp b/Swiften/Parser/PayloadParsers/ErrorParser.cpp
new file mode 100644
index 0000000..13380c8
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ErrorParser.cpp
@@ -0,0 +1,109 @@
+#include "Swiften/Parser/PayloadParsers/ErrorParser.h"
+
+namespace Swift {
+
+ErrorParser::ErrorParser() : level_(TopLevel) {
+}
+
+void ErrorParser::handleStartElement(const String&, const String&, const AttributeMap& attributes) {
+ if (level_ == TopLevel) {
+ String type = attributes.getAttribute("type");
+ if (type == "continue") {
+ getPayloadInternal()->setType(Error::Continue);
+ }
+ else if (type == "modify") {
+ getPayloadInternal()->setType(Error::Modify);
+ }
+ else if (type == "auth") {
+ getPayloadInternal()->setType(Error::Auth);
+ }
+ else if (type == "wait") {
+ getPayloadInternal()->setType(Error::Wait);
+ }
+ else {
+ getPayloadInternal()->setType(Error::Cancel);
+ }
+ }
+ ++level_;
+}
+
+void ErrorParser::handleEndElement(const String& element, const String&) {
+ --level_;
+ if (level_ == PayloadLevel) {
+ if (element == "text") {
+ getPayloadInternal()->setText(currentText_);
+ }
+ else if (element == "bad-request") {
+ getPayloadInternal()->setCondition(Error::BadRequest);
+ }
+ else if (element == "conflict") {
+ getPayloadInternal()->setCondition(Error::Conflict);
+ }
+ else if (element == "feature-not-implemented") {
+ getPayloadInternal()->setCondition(Error::FeatureNotImplemented);
+ }
+ else if (element == "forbidden") {
+ getPayloadInternal()->setCondition(Error::Forbidden);
+ }
+ else if (element == "gone") {
+ getPayloadInternal()->setCondition(Error::Gone);
+ }
+ else if (element == "internal-server-error") {
+ getPayloadInternal()->setCondition(Error::InternalServerError);
+ }
+ else if (element == "item-not-found") {
+ getPayloadInternal()->setCondition(Error::ItemNotFound);
+ }
+ else if (element == "jid-malformed") {
+ getPayloadInternal()->setCondition(Error::JIDMalformed);
+ }
+ else if (element == "not-acceptable") {
+ getPayloadInternal()->setCondition(Error::NotAcceptable);
+ }
+ else if (element == "not-allowed") {
+ getPayloadInternal()->setCondition(Error::NotAllowed);
+ }
+ else if (element == "not-authorized") {
+ getPayloadInternal()->setCondition(Error::NotAuthorized);
+ }
+ else if (element == "payment-required") {
+ getPayloadInternal()->setCondition(Error::PaymentRequired);
+ }
+ else if (element == "recipient-unavailable") {
+ getPayloadInternal()->setCondition(Error::RecipientUnavailable);
+ }
+ else if (element == "redirect") {
+ getPayloadInternal()->setCondition(Error::Redirect);
+ }
+ else if (element == "registration-required") {
+ getPayloadInternal()->setCondition(Error::RegistrationRequired);
+ }
+ else if (element == "remote-server-not-found") {
+ getPayloadInternal()->setCondition(Error::RemoteServerNotFound);
+ }
+ else if (element == "remote-server-timeout") {
+ getPayloadInternal()->setCondition(Error::RemoteServerTimeout);
+ }
+ else if (element == "resource-constraint") {
+ getPayloadInternal()->setCondition(Error::ResourceConstraint);
+ }
+ else if (element == "service-unavailable") {
+ getPayloadInternal()->setCondition(Error::ServiceUnavailable);
+ }
+ else if (element == "subscription-required") {
+ getPayloadInternal()->setCondition(Error::SubscriptionRequired);
+ }
+ else if (element == "unexpected-request") {
+ getPayloadInternal()->setCondition(Error::UnexpectedRequest);
+ }
+ else {
+ getPayloadInternal()->setCondition(Error::UndefinedCondition);
+ }
+ }
+}
+
+void ErrorParser::handleCharacterData(const String& data) {
+ currentText_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/ErrorParser.h b/Swiften/Parser/PayloadParsers/ErrorParser.h
new file mode 100644
index 0000000..76db205
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ErrorParser.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_ErrorParser_H
+#define SWIFTEN_ErrorParser_H
+
+#include "Swiften/Elements/Error.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class ErrorParser : public GenericPayloadParser<Error> {
+ public:
+ ErrorParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1
+ };
+ int level_;
+ String currentText_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/ErrorParserFactory.h b/Swiften/Parser/PayloadParsers/ErrorParserFactory.h
new file mode 100644
index 0000000..36d1f55
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ErrorParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_ErrorParserFactory_H
+#define SWIFTEN_ErrorParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ErrorParser.h"
+
+namespace Swift {
+ class ErrorParserFactory : public GenericPayloadParserFactory<ErrorParser> {
+ public:
+ ErrorParserFactory() : GenericPayloadParserFactory<ErrorParser>("error") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
new file mode 100644
index 0000000..fb2fea7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -0,0 +1,46 @@
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ErrorParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/BodyParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/PriorityParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ResourceBindParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StartSessionParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StatusParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StatusShowParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/RosterParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h"
+
+using namespace boost;
+
+namespace Swift {
+
+FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new StatusParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new StatusShowParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new BodyParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new PriorityParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new ErrorParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new SoftwareVersionParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new RosterParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new DiscoInfoParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new ResourceBindParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new StartSessionParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new SecurityLabelParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new SecurityLabelsCatalogParserFactory()));
+ foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
+ addFactory(factory.get());
+ }
+}
+
+FullPayloadParserFactoryCollection::~FullPayloadParserFactoryCollection() {
+ foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
+ removeFactory(factory.get());
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h
new file mode 100644
index 0000000..3c383ec
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h
@@ -0,0 +1,21 @@
+#ifndef SWIFTEN_FULLPAYLOADPARSERFACTORYCOLLECTION_H
+#define SWIFTEN_FULLPAYLOADPARSERFACTORYCOLLECTION_H
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+
+namespace Swift {
+ class FullPayloadParserFactoryCollection : public PayloadParserFactoryCollection {
+ public:
+ FullPayloadParserFactoryCollection();
+ ~FullPayloadParserFactoryCollection();
+
+ private:
+ std::vector< boost::shared_ptr<PayloadParserFactory> > factories_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/Makefile.inc b/Swiften/Parser/PayloadParsers/Makefile.inc
new file mode 100644
index 0000000..828dc5e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/Makefile.inc
@@ -0,0 +1,15 @@
+SWIFTEN_SOURCES += \
+ Swiften/Parser/PayloadParsers/BodyParser.cpp \
+ Swiften/Parser/PayloadParsers/PriorityParser.cpp \
+ Swiften/Parser/PayloadParsers/StatusParser.cpp \
+ Swiften/Parser/PayloadParsers/StatusShowParser.cpp \
+ Swiften/Parser/PayloadParsers/SoftwareVersionParser.cpp \
+ Swiften/Parser/PayloadParsers/SecurityLabelParser.cpp \
+ Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.cpp \
+ Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp \
+ Swiften/Parser/PayloadParsers/ErrorParser.cpp \
+ Swiften/Parser/PayloadParsers/RosterParser.cpp \
+ Swiften/Parser/PayloadParsers/ResourceBindParser.cpp \
+ Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+
+include Swiften/Parser/PayloadParsers/UnitTest/Makefile.inc
diff --git a/Swiften/Parser/PayloadParsers/PriorityParser.cpp b/Swiften/Parser/PayloadParsers/PriorityParser.cpp
new file mode 100644
index 0000000..3dcca51
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/PriorityParser.cpp
@@ -0,0 +1,25 @@
+#include "Swiften/Parser/PayloadParsers/PriorityParser.h"
+
+#include <boost/lexical_cast.hpp>
+
+namespace Swift {
+
+PriorityParser::PriorityParser() : level_(0) {
+}
+
+void PriorityParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++level_;
+}
+
+void PriorityParser::handleEndElement(const String&, const String&) {
+ --level_;
+ if (level_ == 0) {
+ getPayloadInternal()->setPriority(boost::lexical_cast<int>(text_));
+ }
+}
+
+void PriorityParser::handleCharacterData(const String& data) {
+ text_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/PriorityParser.h b/Swiften/Parser/PayloadParsers/PriorityParser.h
new file mode 100644
index 0000000..7f3836f
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/PriorityParser.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_PriorityParser_H
+#define SWIFTEN_PriorityParser_H
+
+#include "Swiften/Elements/Priority.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class PriorityParser : public GenericPayloadParser<Priority> {
+ public:
+ PriorityParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ int level_;
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/PriorityParserFactory.h b/Swiften/Parser/PayloadParsers/PriorityParserFactory.h
new file mode 100644
index 0000000..5386326
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/PriorityParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_PriorityParserFactory_H
+#define SWIFTEN_PriorityParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/PriorityParser.h"
+
+namespace Swift {
+ class PriorityParserFactory : public GenericPayloadParserFactory<PriorityParser> {
+ public:
+ PriorityParserFactory() : GenericPayloadParserFactory<PriorityParser>("priority") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/ResourceBindParser.cpp b/Swiften/Parser/PayloadParsers/ResourceBindParser.cpp
new file mode 100644
index 0000000..c5ca787
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ResourceBindParser.cpp
@@ -0,0 +1,37 @@
+#include "Swiften/Parser/PayloadParsers/ResourceBindParser.h"
+
+namespace Swift {
+
+ResourceBindParser::ResourceBindParser() : level_(0), inJID_(false), inResource_(false) {
+}
+
+void ResourceBindParser::handleStartElement(const String& element, const String&, const AttributeMap&) {
+ if (level_ == 1) {
+ text_ = "";
+ if (element == "resource") {
+ inResource_ = true;
+ }
+ if (element == "jid") {
+ inJID_ = true;
+ }
+ }
+ ++level_;
+}
+
+void ResourceBindParser::handleEndElement(const String&, const String&) {
+ --level_;
+ if (level_ == 1) {
+ if (inJID_) {
+ getPayloadInternal()->setJID(JID(text_));
+ }
+ else if (inResource_) {
+ getPayloadInternal()->setResource(text_);
+ }
+ }
+}
+
+void ResourceBindParser::handleCharacterData(const String& data) {
+ text_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/ResourceBindParser.h b/Swiften/Parser/PayloadParsers/ResourceBindParser.h
new file mode 100644
index 0000000..1341140
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ResourceBindParser.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_ResourceBindParser_H
+#define SWIFTEN_ResourceBindParser_H
+
+#include "Swiften/Elements/ResourceBind.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class ResourceBindParser : public GenericPayloadParser<ResourceBind> {
+ public:
+ ResourceBindParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ int level_;
+ bool inJID_;
+ bool inResource_;
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/ResourceBindParserFactory.h b/Swiften/Parser/PayloadParsers/ResourceBindParserFactory.h
new file mode 100644
index 0000000..54af9c9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ResourceBindParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_ResourceBindParserFactory_H
+#define SWIFTEN_ResourceBindParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ResourceBindParser.h"
+
+namespace Swift {
+ class ResourceBindParserFactory : public GenericPayloadParserFactory<ResourceBindParser> {
+ public:
+ ResourceBindParserFactory() : GenericPayloadParserFactory<ResourceBindParser>("bind", "urn:ietf:params:xml:ns:xmpp-bind") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/RosterParser.cpp b/Swiften/Parser/PayloadParsers/RosterParser.cpp
new file mode 100644
index 0000000..0c4e99b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/RosterParser.cpp
@@ -0,0 +1,66 @@
+#include "Swiften/Parser/PayloadParsers/RosterParser.h"
+
+namespace Swift {
+
+RosterParser::RosterParser() : level_(TopLevel) {
+}
+
+void RosterParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+ if (level_ == PayloadLevel) {
+ if (element == "item") {
+ inItem_ = true;
+ currentItem_ = RosterItemPayload();
+
+ currentItem_.setJID(JID(attributes.getAttribute("jid")));
+ currentItem_.setName(attributes.getAttribute("name"));
+
+ String subscription = attributes.getAttribute("subscription");
+ if (subscription == "both") {
+ currentItem_.setSubscription(RosterItemPayload::Both);
+ }
+ else if (subscription == "to") {
+ currentItem_.setSubscription(RosterItemPayload::To);
+ }
+ else if (subscription == "from") {
+ currentItem_.setSubscription(RosterItemPayload::From);
+ }
+ else if (subscription == "remove") {
+ currentItem_.setSubscription(RosterItemPayload::Remove);
+ }
+ else {
+ currentItem_.setSubscription(RosterItemPayload::None);
+ }
+
+ if (attributes.getAttribute("ask") == "subscribe") {
+ currentItem_.setSubscriptionRequested();
+ }
+ }
+ }
+ else if (level_ == ItemLevel) {
+ if (element == "group") {
+ currentText_ = "";
+ }
+ }
+ ++level_;
+}
+
+void RosterParser::handleEndElement(const String& element, const String&) {
+ --level_;
+ if (level_ == PayloadLevel) {
+ if (inItem_) {
+ getPayloadInternal()->addItem(currentItem_);
+ inItem_ = false;
+ }
+ }
+ else if (level_ == ItemLevel) {
+ if (element == "group") {
+ currentItem_.addGroup(currentText_);
+ }
+ }
+}
+
+void RosterParser::handleCharacterData(const String& data) {
+ currentText_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/RosterParser.h b/Swiften/Parser/PayloadParsers/RosterParser.h
new file mode 100644
index 0000000..bd8186a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/RosterParser.h
@@ -0,0 +1,29 @@
+#ifndef SWIFTEN_RosterParser_H
+#define SWIFTEN_RosterParser_H
+
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class RosterParser : public GenericPayloadParser<RosterPayload> {
+ public:
+ RosterParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1,
+ ItemLevel = 2
+ };
+ int level_;
+ bool inItem_;
+ RosterItemPayload currentItem_;
+ String currentText_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/RosterParserFactory.h b/Swiften/Parser/PayloadParsers/RosterParserFactory.h
new file mode 100644
index 0000000..f51b3ab
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/RosterParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_RosterParserFactory_H
+#define SWIFTEN_RosterParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/RosterParser.h"
+
+namespace Swift {
+ class RosterParserFactory : public GenericPayloadParserFactory<RosterParser> {
+ public:
+ RosterParserFactory() : GenericPayloadParserFactory<RosterParser>("query", "jabber:iq:roster") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/SecurityLabelParser.cpp b/Swiften/Parser/PayloadParsers/SecurityLabelParser.cpp
new file mode 100644
index 0000000..7e65575
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SecurityLabelParser.cpp
@@ -0,0 +1,59 @@
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParser.h"
+#include "Swiften/Parser/SerializingParser.h"
+
+namespace Swift {
+
+SecurityLabelParser::SecurityLabelParser() : level_(TopLevel), labelParser_(0) {
+}
+
+void SecurityLabelParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ ++level_;
+ if (level_ == DisplayMarkingOrLabelLevel) {
+ if (element == "displaymarking") {
+ currentText_ = "";
+ getPayloadInternal()->setBackgroundColor(attributes.getAttribute("bgcolor"));
+ getPayloadInternal()->setForegroundColor(attributes.getAttribute("fgcolor"));
+ }
+ else if (element == "label" || element == "equivalentlabel") {
+ assert(!labelParser_);
+ labelParser_ = new SerializingParser();
+ }
+ }
+ else if (level_ >= SecurityLabelLevel && labelParser_) {
+ labelParser_->handleStartElement(element, ns, attributes);
+ }
+}
+
+void SecurityLabelParser::handleEndElement(const String& element, const String& ns) {
+ if (level_ == DisplayMarkingOrLabelLevel) {
+ if (element == "displaymarking") {
+ getPayloadInternal()->setDisplayMarking(currentText_);
+ }
+ else if (labelParser_) {
+ if (element == "label") {
+ getPayloadInternal()->setLabel(labelParser_->getResult());
+ }
+ else {
+ getPayloadInternal()->addEquivalentLabel(labelParser_->getResult());
+ }
+ delete labelParser_;
+ labelParser_ = 0;
+ }
+ }
+ else if (labelParser_ && level_ >= SecurityLabelLevel) {
+ labelParser_->handleEndElement(element, ns);
+ }
+ --level_;
+
+}
+
+void SecurityLabelParser::handleCharacterData(const String& data) {
+ if (labelParser_) {
+ labelParser_->handleCharacterData(data);
+ }
+ else {
+ currentText_ += data;
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/SecurityLabelParser.h b/Swiften/Parser/PayloadParsers/SecurityLabelParser.h
new file mode 100644
index 0000000..70040d9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SecurityLabelParser.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_SecurityLabelParser_H
+#define SWIFTEN_SecurityLabelParser_H
+
+#include "Swiften/Elements/SecurityLabel.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class SerializingParser;
+
+ class SecurityLabelParser : public GenericPayloadParser<SecurityLabel> {
+ public:
+ SecurityLabelParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1,
+ DisplayMarkingOrLabelLevel = 2,
+ SecurityLabelLevel = 3
+ };
+ int level_;
+ SerializingParser* labelParser_;
+ String currentText_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h b/Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h
new file mode 100644
index 0000000..0341fbb
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_SecurityLabelParserFactory_H
+#define SWIFTEN_SecurityLabelParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParser.h"
+
+namespace Swift {
+ class SecurityLabelParserFactory : public GenericPayloadParserFactory<SecurityLabelParser> {
+ public:
+ SecurityLabelParserFactory() : GenericPayloadParserFactory<SecurityLabelParser>("securitylabel", "urn:xmpp:sec-label:0") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.cpp b/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.cpp
new file mode 100644
index 0000000..d571f0b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.cpp
@@ -0,0 +1,55 @@
+#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParser.h"
+
+namespace Swift {
+
+SecurityLabelsCatalogParser::SecurityLabelsCatalogParser() : level_(TopLevel), labelParser_(0) {
+ labelParserFactory_ = new SecurityLabelParserFactory();
+}
+
+SecurityLabelsCatalogParser::~SecurityLabelsCatalogParser() {
+ delete labelParserFactory_;
+}
+
+void SecurityLabelsCatalogParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ ++level_;
+ if (level_ == PayloadLevel) {
+ getPayloadInternal()->setTo(JID(attributes.getAttribute("to")));
+ getPayloadInternal()->setName(attributes.getAttribute("name"));
+ getPayloadInternal()->setDescription(attributes.getAttribute("desc"));
+ }
+ else if (level_ == LabelLevel) {
+ assert(!labelParser_);
+ if (labelParserFactory_->canParse(element, ns, attributes)) {
+ labelParser_ = dynamic_cast<SecurityLabelParser*>(labelParserFactory_->createPayloadParser());
+ assert(labelParser_);
+ }
+ }
+
+ if (labelParser_) {
+ labelParser_->handleStartElement(element, ns, attributes);
+ }
+}
+
+void SecurityLabelsCatalogParser::handleEndElement(const String& element, const String& ns) {
+ if (labelParser_) {
+ labelParser_->handleEndElement(element, ns);
+ }
+ if (level_ == LabelLevel && labelParser_) {
+ SecurityLabel* label = dynamic_cast<SecurityLabel*>(labelParser_->getPayload().get());
+ assert(label);
+ getPayloadInternal()->addLabel(SecurityLabel(*label));
+ delete labelParser_;
+ labelParser_ = 0;
+ }
+ --level_;
+}
+
+void SecurityLabelsCatalogParser::handleCharacterData(const String& data) {
+ if (labelParser_) {
+ labelParser_->handleCharacterData(data);
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h b/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h
new file mode 100644
index 0000000..ec31235
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h
@@ -0,0 +1,32 @@
+#ifndef SWIFTEN_SecurityLabelsCatalogParser_H
+#define SWIFTEN_SecurityLabelsCatalogParser_H
+
+#include "Swiften/Elements/SecurityLabelsCatalog.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class SecurityLabelParserFactory;
+ class SecurityLabelParser;
+
+ class SecurityLabelsCatalogParser : public GenericPayloadParser<SecurityLabelsCatalog> {
+ public:
+ SecurityLabelsCatalogParser();
+ ~SecurityLabelsCatalogParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1,
+ LabelLevel = 2
+ };
+ int level_;
+ SecurityLabelParserFactory* labelParserFactory_;
+ SecurityLabelParser* labelParser_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h b/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h
new file mode 100644
index 0000000..99a310b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_SecurityLabelsCatalogParserFactory_H
+#define SWIFTEN_SecurityLabelsCatalogParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h"
+
+namespace Swift {
+ class SecurityLabelsCatalogParserFactory : public GenericPayloadParserFactory<SecurityLabelsCatalogParser> {
+ public:
+ SecurityLabelsCatalogParserFactory() : GenericPayloadParserFactory<SecurityLabelsCatalogParser>("catalog", "urn:xmpp:sec-label:catalog:0") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/SoftwareVersionParser.cpp b/Swiften/Parser/PayloadParsers/SoftwareVersionParser.cpp
new file mode 100644
index 0000000..dae9f94
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SoftwareVersionParser.cpp
@@ -0,0 +1,32 @@
+#include "Swiften/Parser/PayloadParsers/SoftwareVersionParser.h"
+
+namespace Swift {
+
+SoftwareVersionParser::SoftwareVersionParser() : level_(TopLevel) {
+}
+
+void SoftwareVersionParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++level_;
+}
+
+void SoftwareVersionParser::handleEndElement(const String& element, const String&) {
+ --level_;
+ if (level_ == PayloadLevel) {
+ if (element == "name") {
+ getPayloadInternal()->setName(currentText_);
+ }
+ else if (element == "version") {
+ getPayloadInternal()->setVersion(currentText_);
+ }
+ else if (element == "os") {
+ getPayloadInternal()->setOS(currentText_);
+ }
+ currentText_ = "";
+ }
+}
+
+void SoftwareVersionParser::handleCharacterData(const String& data) {
+ currentText_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/SoftwareVersionParser.h b/Swiften/Parser/PayloadParsers/SoftwareVersionParser.h
new file mode 100644
index 0000000..bfffc90
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SoftwareVersionParser.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_SoftwareVersionParser_H
+#define SWIFTEN_SoftwareVersionParser_H
+
+#include "Swiften/Elements/SoftwareVersion.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class SoftwareVersionParser : public GenericPayloadParser<SoftwareVersion> {
+ public:
+ SoftwareVersionParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ enum Level {
+ TopLevel = 0,
+ PayloadLevel = 1
+ };
+ int level_;
+ String currentText_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h b/Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h
new file mode 100644
index 0000000..cb33e0b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_SoftwareVersionParserFactory_H
+#define SWIFTEN_SoftwareVersionParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SoftwareVersionParser.h"
+
+namespace Swift {
+ class SoftwareVersionParserFactory : public GenericPayloadParserFactory<SoftwareVersionParser> {
+ public:
+ SoftwareVersionParserFactory() : GenericPayloadParserFactory<SoftwareVersionParser>("query", "jabber:iq:version") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/StartSessionParser.h b/Swiften/Parser/PayloadParsers/StartSessionParser.h
new file mode 100644
index 0000000..059d036
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StartSessionParser.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_StartSessionParser_H
+#define SWIFTEN_StartSessionParser_H
+
+#include "Swiften/Elements/StartSession.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class StartSessionParser : public GenericPayloadParser<StartSession> {
+ public:
+ StartSessionParser() {}
+
+ virtual void handleStartElement(const String&, const String&, const AttributeMap&) {}
+ virtual void handleEndElement(const String&, const String&) {}
+ virtual void handleCharacterData(const String&) {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/StartSessionParserFactory.h b/Swiften/Parser/PayloadParsers/StartSessionParserFactory.h
new file mode 100644
index 0000000..5eed749
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StartSessionParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StartSessionParserFactory_H
+#define SWIFTEN_StartSessionParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StartSessionParser.h"
+
+namespace Swift {
+ class StartSessionParserFactory : public GenericPayloadParserFactory<StartSessionParser> {
+ public:
+ StartSessionParserFactory() : GenericPayloadParserFactory<StartSessionParser>("session", "urn:ietf:params:xml:ns:xmpp-session") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/StatusParser.cpp b/Swiften/Parser/PayloadParsers/StatusParser.cpp
new file mode 100644
index 0000000..c7771b9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StatusParser.cpp
@@ -0,0 +1,23 @@
+#include "Swiften/Parser/PayloadParsers/StatusParser.h"
+
+namespace Swift {
+
+StatusParser::StatusParser() : level_(0) {
+}
+
+void StatusParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++level_;
+}
+
+void StatusParser::handleEndElement(const String&, const String&) {
+ --level_;
+ if (level_ == 0) {
+ getPayloadInternal()->setText(text_);
+ }
+}
+
+void StatusParser::handleCharacterData(const String& data) {
+ text_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StatusParser.h b/Swiften/Parser/PayloadParsers/StatusParser.h
new file mode 100644
index 0000000..36ae094
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StatusParser.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_StatusParser_H
+#define SWIFTEN_StatusParser_H
+
+#include "Swiften/Elements/Status.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class StatusParser : public GenericPayloadParser<Status> {
+ public:
+ StatusParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ int level_;
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/StatusParserFactory.h b/Swiften/Parser/PayloadParsers/StatusParserFactory.h
new file mode 100644
index 0000000..72af5a9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StatusParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StatusParserFactory_H
+#define SWIFTEN_StatusParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StatusParser.h"
+
+namespace Swift {
+ class StatusParserFactory : public GenericPayloadParserFactory<StatusParser> {
+ public:
+ StatusParserFactory() : GenericPayloadParserFactory<StatusParser>("status") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/StatusShowParser.cpp b/Swiften/Parser/PayloadParsers/StatusShowParser.cpp
new file mode 100644
index 0000000..c3719af
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StatusShowParser.cpp
@@ -0,0 +1,37 @@
+#include "Swiften/Parser/PayloadParsers/StatusShowParser.h"
+
+namespace Swift {
+
+StatusShowParser::StatusShowParser() : level_(0) {
+}
+
+void StatusShowParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++level_;
+}
+
+void StatusShowParser::handleEndElement(const String&, const String&) {
+ --level_;
+ if (level_ == 0) {
+ if (text_ == "away") {
+ getPayloadInternal()->setType(StatusShow::Away);
+ }
+ else if (text_ == "chat") {
+ getPayloadInternal()->setType(StatusShow::FFC);
+ }
+ else if (text_ == "xa") {
+ getPayloadInternal()->setType(StatusShow::XA);
+ }
+ else if (text_ == "dnd") {
+ getPayloadInternal()->setType(StatusShow::DND);
+ }
+ else {
+ getPayloadInternal()->setType(StatusShow::Online);
+ }
+ }
+}
+
+void StatusShowParser::handleCharacterData(const String& data) {
+ text_ += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StatusShowParser.h b/Swiften/Parser/PayloadParsers/StatusShowParser.h
new file mode 100644
index 0000000..a8ddb09
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StatusShowParser.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_StatusShowParser_H
+#define SWIFTEN_StatusShowParser_H
+
+#include "Swiften/Elements/StatusShow.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class StatusShowParser : public GenericPayloadParser<StatusShow> {
+ public:
+ StatusShowParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ int level_;
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/StatusShowParserFactory.h b/Swiften/Parser/PayloadParsers/StatusShowParserFactory.h
new file mode 100644
index 0000000..2ddc190
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StatusShowParserFactory.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StatusShowParserFactory_H
+#define SWIFTEN_StatusShowParserFactory_H
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StatusShowParser.h"
+
+namespace Swift {
+ class StatusShowParserFactory : public GenericPayloadParserFactory<StatusShowParser> {
+ public:
+ StatusShowParserFactory() : GenericPayloadParserFactory<StatusShowParser>("show") {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp
new file mode 100644
index 0000000..c747452
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp
@@ -0,0 +1,29 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/BodyParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class BodyParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(BodyParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ BodyParserTest() {}
+
+ void testParse() {
+ BodyParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<body>foo<baz>bar</baz>fum</body>"));
+
+ Body* payload = dynamic_cast<Body*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("foobarfum"), payload->getText());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BodyParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp
new file mode 100644
index 0000000..5aed12f
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp
@@ -0,0 +1,48 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class DiscoInfoParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(DiscoInfoParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ DiscoInfoParserTest() {}
+
+ void testParse() {
+ DiscoInfoParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<query xmlns=\"http://jabber.org/protocol/disco#info\">"
+ "<identity name=\"Swift\" category=\"client\" type=\"pc\" xml:lang=\"en\"/>"
+ "<identity name=\"Vlug\" category=\"client\" type=\"pc\" xml:lang=\"nl\"/>"
+ "<feature var=\"foo-feature\"/>"
+ "<feature var=\"bar-feature\"/>"
+ "<feature var=\"baz-feature\"/>"
+ "</query>"));
+
+ DiscoInfo* payload = dynamic_cast<DiscoInfo*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getIdentities().size()));
+ CPPUNIT_ASSERT_EQUAL(String("Swift"), payload->getIdentities()[0].getName());
+ CPPUNIT_ASSERT_EQUAL(String("pc"), payload->getIdentities()[0].getType());
+ CPPUNIT_ASSERT_EQUAL(String("client"), payload->getIdentities()[0].getCategory());
+ CPPUNIT_ASSERT_EQUAL(String("en"), payload->getIdentities()[0].getLanguage());
+ CPPUNIT_ASSERT_EQUAL(String("Vlug"), payload->getIdentities()[1].getName());
+ CPPUNIT_ASSERT_EQUAL(String("pc"), payload->getIdentities()[1].getType());
+ CPPUNIT_ASSERT_EQUAL(String("client"), payload->getIdentities()[1].getCategory());
+ CPPUNIT_ASSERT_EQUAL(String("nl"), payload->getIdentities()[1].getLanguage());
+ CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(payload->getFeatures().size()));
+ CPPUNIT_ASSERT_EQUAL(String("foo-feature"), payload->getFeatures()[0]);
+ CPPUNIT_ASSERT_EQUAL(String("bar-feature"), payload->getFeatures()[1]);
+ CPPUNIT_ASSERT_EQUAL(String("baz-feature"), payload->getFeatures()[2]);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp
new file mode 100644
index 0000000..719702d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp
@@ -0,0 +1,35 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/ErrorParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class ErrorParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ErrorParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ErrorParserTest() {}
+
+ void testParse() {
+ ErrorParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<error type=\"modify\">"
+ "<bad-request xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
+ "<text xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">boo</text>"
+ "</error>"));
+
+ Error* payload = dynamic_cast<Error*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(Error::BadRequest, payload->getCondition());
+ CPPUNIT_ASSERT_EQUAL(Error::Modify, payload->getType());
+ CPPUNIT_ASSERT_EQUAL(String("boo"), payload->getText());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ErrorParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/Makefile.inc b/Swiften/Parser/PayloadParsers/UnitTest/Makefile.inc
new file mode 100644
index 0000000..f4d4cf9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/Makefile.inc
@@ -0,0 +1,12 @@
+UNITTEST_SOURCES += \
+ Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp \
+ Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h b/Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h
new file mode 100644
index 0000000..bac33bf
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h
@@ -0,0 +1,10 @@
+#ifndef SWIFTEN_PayloadParserTester_H
+#define SWIFTEN_PayloadParserTester_H
+
+#include "Swiften/Parser/UnitTest/ParserTester.h"
+
+namespace Swift {
+ typedef ParserTester<PayloadParser> PayloadParserTester;
+}
+
+#endif
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp
new file mode 100644
index 0000000..a186ebd
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp
@@ -0,0 +1,29 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/PriorityParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class PriorityParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PriorityParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PriorityParserTest() {}
+
+ void testParse() {
+ PriorityParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<priority>-120</priority>"));
+
+ Priority* payload = dynamic_cast<Priority*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(-120, payload->getPriority());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PriorityParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp
new file mode 100644
index 0000000..67cb9cc
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp
@@ -0,0 +1,40 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/ResourceBindParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class ResourceBindParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ResourceBindParserTest);
+ CPPUNIT_TEST(testParse_JID);
+ CPPUNIT_TEST(testParse_Resource);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ResourceBindParserTest() {}
+
+ void testParse_JID() {
+ ResourceBindParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>somenode@example.com/someresource</jid></bind>"));
+
+ ResourceBind* payload = dynamic_cast<ResourceBind*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(JID("somenode@example.com/someresource"), payload->getJID());
+ }
+
+ void testParse_Resource() {
+ ResourceBindParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>someresource</resource></bind>"));
+
+ ResourceBind* payload = dynamic_cast<ResourceBind*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("someresource"), payload->getResource());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ResourceBindParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp
new file mode 100644
index 0000000..7f0fc64
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp
@@ -0,0 +1,51 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/RosterParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/XMLPayloadParser.h"
+
+using namespace Swift;
+
+class RosterParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RosterParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ RosterParserTest() {}
+
+ void testParse() {
+ RosterParser testling;
+ XMLPayloadParser parser(&testling);
+ parser.parse(
+ "<query xmlns='jabber:iq:roster'>"
+ " <item jid='foo@bar.com' name='Foo @ Bar' subscription='from' ask='subscribe'>"
+ " <group>Group 1</group>"
+ " <group>Group 2</group>"
+ " </item>"
+ " <item jid='baz@blo.com' name='Baz'/>"
+ "</query>");
+
+ RosterPayload* payload = dynamic_cast<RosterPayload*>(testling.getPayload().get());
+ const RosterPayload::RosterItemPayloads& items = payload->getItems();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items.size());
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), items[0].getJID());
+ CPPUNIT_ASSERT_EQUAL(String("Foo @ Bar"), items[0].getName());
+ CPPUNIT_ASSERT_EQUAL(RosterItemPayload::From, items[0].getSubscription());
+ CPPUNIT_ASSERT(items[0].getSubscriptionRequested());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items[0].getGroups().size());
+ CPPUNIT_ASSERT_EQUAL(String("Group 1"), items[0].getGroups()[0]);
+ CPPUNIT_ASSERT_EQUAL(String("Group 2"), items[0].getGroups()[1]);
+
+ CPPUNIT_ASSERT_EQUAL(JID("baz@blo.com"), items[1].getJID());
+ CPPUNIT_ASSERT_EQUAL(String("Baz"), items[1].getName());
+ CPPUNIT_ASSERT_EQUAL(RosterItemPayload::None, items[1].getSubscription());
+ CPPUNIT_ASSERT(!items[1].getSubscriptionRequested());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), items[1].getGroups().size());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(RosterParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp
new file mode 100644
index 0000000..5da3fbd
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp
@@ -0,0 +1,46 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class SecurityLabelParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SecurityLabelParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SecurityLabelParserTest() {}
+
+ void testParse() {
+ SecurityLabelParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<securitylabel xmlns=\"urn:xmpp:sec-label:0\">"
+ "<displaymarking fgcolor=\"black\" bgcolor=\"red\">SECRET</displaymarking>"
+ "<label>"
+ "<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQYCAQQGASk=</esssecuritylabel>"
+ "</label>"
+ "<equivalentlabel>"
+ "<icismlabel xmlns=\"http://example.gov/IC-ISM/0\" classification=\"S\" ownerProducer=\"USA\" disseminationControls=\"FOUO\"/>"
+ "</equivalentlabel>"
+ "<equivalentlabel>"
+ "<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MRUCAgD9DA9BcXVhIChvYnNvbGV0ZSk=</esssecuritylabel>"
+ "</equivalentlabel>"
+ "</securitylabel>"));
+
+ SecurityLabel* payload = dynamic_cast<SecurityLabel*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("SECRET"), payload->getDisplayMarking());
+ CPPUNIT_ASSERT_EQUAL(String("black"), payload->getForegroundColor());
+ CPPUNIT_ASSERT_EQUAL(String("red"), payload->getBackgroundColor());
+ CPPUNIT_ASSERT_EQUAL(String("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQYCAQQGASk=</esssecuritylabel>"), payload->getLabel());
+ CPPUNIT_ASSERT_EQUAL(String("<icismlabel classification=\"S\" disseminationControls=\"FOUO\" ownerProducer=\"USA\" xmlns=\"http://example.gov/IC-ISM/0\"/>"), payload->getEquivalentLabels()[0]);
+ CPPUNIT_ASSERT_EQUAL(String("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MRUCAgD9DA9BcXVhIChvYnNvbGV0ZSk=</esssecuritylabel>"), payload->getEquivalentLabels()[1]);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SecurityLabelParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp
new file mode 100644
index 0000000..0021c0d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp
@@ -0,0 +1,46 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class SecurityLabelsCatalogParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SecurityLabelsCatalogParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SecurityLabelsCatalogParserTest() {}
+
+ void testParse() {
+ SecurityLabelsCatalogParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<catalog desc=\"an example set of labels\" name=\"Default\" to=\"example.com\" xmlns=\"urn:xmpp:sec-label:catalog:0\">"
+ "<securitylabel xmlns=\"urn:xmpp:sec-label:0\">"
+ "<displaymarking bgcolor=\"red\" fgcolor=\"black\">SECRET</displaymarking>"
+ "<label><esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQYCAQQGASk=</esssecuritylabel></label>"
+ "</securitylabel>"
+ "<securitylabel xmlns=\"urn:xmpp:sec-label:0\">"
+ "<displaymarking bgcolor=\"navy\" fgcolor=\"black\">CONFIDENTIAL</displaymarking>"
+ "<label><esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQMGASk=</esssecuritylabel></label>"
+ "</securitylabel>"
+ "</catalog>"));
+
+ SecurityLabelsCatalog* payload = dynamic_cast<SecurityLabelsCatalog*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("Default"), payload->getName());
+ CPPUNIT_ASSERT_EQUAL(String("an example set of labels"), payload->getDescription());
+ CPPUNIT_ASSERT_EQUAL(JID("example.com"), payload->getTo());
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getLabels().size()));
+ CPPUNIT_ASSERT_EQUAL(String("SECRET"), payload->getLabels()[0].getDisplayMarking());
+ CPPUNIT_ASSERT_EQUAL(String("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQYCAQQGASk=</esssecuritylabel>"), payload->getLabels()[0].getLabel());
+ CPPUNIT_ASSERT_EQUAL(String("CONFIDENTIAL"), payload->getLabels()[1].getDisplayMarking());
+ CPPUNIT_ASSERT_EQUAL(String("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQMGASk=</esssecuritylabel>"), payload->getLabels()[1].getLabel());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SecurityLabelsCatalogParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp
new file mode 100644
index 0000000..0ff1d00
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp
@@ -0,0 +1,36 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/SoftwareVersionParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class SoftwareVersionParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SoftwareVersionParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SoftwareVersionParserTest() {}
+
+ void testParse() {
+ SoftwareVersionParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<query xmlns=\"jabber:iq:version\">"
+ "<name>myclient</name>"
+ "<version>1.0</version>"
+ "<os>Mac OS X</os>"
+ "</query>"));
+
+ SoftwareVersion* payload = dynamic_cast<SoftwareVersion*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("myclient"), payload->getName());
+ CPPUNIT_ASSERT_EQUAL(String("1.0"), payload->getVersion());
+ CPPUNIT_ASSERT_EQUAL(String("Mac OS X"), payload->getOS());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SoftwareVersionParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp
new file mode 100644
index 0000000..f1fa7b1
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp
@@ -0,0 +1,29 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/StatusParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class StatusParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StatusParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StatusParserTest() {}
+
+ void testParse() {
+ StatusParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<status>foo<baz>bar</baz>fum</status>"));
+
+ Status* payload = dynamic_cast<Status*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("foobarfum"), payload->getText());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StatusParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp
new file mode 100644
index 0000000..d89fdc5
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp
@@ -0,0 +1,73 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/StatusShowParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class StatusShowParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StatusShowParserTest);
+ CPPUNIT_TEST(testParse_Invalid);
+ CPPUNIT_TEST(testParse_Away);
+ CPPUNIT_TEST(testParse_FFC);
+ CPPUNIT_TEST(testParse_XA);
+ CPPUNIT_TEST(testParse_DND);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StatusShowParserTest() {}
+
+ void testParse_Invalid() {
+ StatusShowParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<show>invalid</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(testling.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::Online == payload->getType());
+ }
+
+ void testParse_Away() {
+ StatusShowParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<show>away</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(testling.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::Away == payload->getType());
+ }
+
+ void testParse_FFC() {
+ StatusShowParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<show>chat</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(testling.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::FFC == payload->getType());
+ }
+
+ void testParse_XA() {
+ StatusShowParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<show>xa</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(testling.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::XA == payload->getType());
+ }
+
+ void testParse_DND() {
+ StatusShowParser testling;
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<show>dnd</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(testling.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::DND == payload->getType());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StatusShowParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/XMLPayloadParser.h b/Swiften/Parser/PayloadParsers/UnitTest/XMLPayloadParser.h
new file mode 100644
index 0000000..2b893c4
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/XMLPayloadParser.h
@@ -0,0 +1,42 @@
+#ifndef SWIFTEN_XMLPayloadParser_H
+#define SWIFTEN_XMLPayloadParser_H
+
+#include "Swiften/Parser/PayloadParser.h"
+#include "Swiften/Parser/XMLParserClient.h"
+#include "Swiften/Parser/XMLParser.h"
+#include "Swiften/Parser/PlatformXMLParserFactory.h"
+
+namespace Swift {
+ class XMLPayloadParser : public XMLParserClient {
+ public:
+ XMLPayloadParser(PayloadParser* payloadParser) :
+ payloadParser_(payloadParser) {
+ xmlParser_ = PlatformXMLParserFactory().createXMLParser(this);
+ }
+
+ ~XMLPayloadParser() {
+ delete xmlParser_;
+ }
+
+ bool parse(const String& data) {
+ return xmlParser_->parse(data);
+ }
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ payloadParser_->handleStartElement(element, ns, attributes);
+ }
+
+ virtual void handleEndElement(const String& element, const String& ns) {
+ payloadParser_->handleEndElement(element, ns);
+ }
+
+ virtual void handleCharacterData(const String& data) {
+ payloadParser_->handleCharacterData(data);
+ }
+
+ private:
+ XMLParser* xmlParser_;
+ PayloadParser* payloadParser_;
+ };
+}
+#endif
diff --git a/Swiften/Parser/PlatformXMLParserFactory.cpp b/Swiften/Parser/PlatformXMLParserFactory.cpp
new file mode 100644
index 0000000..14557d1
--- /dev/null
+++ b/Swiften/Parser/PlatformXMLParserFactory.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Parser/PlatformXMLParserFactory.h"
+
+#include <cassert>
+
+#ifdef HAVE_SWIFTEN_CONFIG_H
+#include "Swiften/config.h"
+#endif
+#ifdef HAVE_LIBXML
+#include "Swiften/Parser/LibXMLParser.h"
+#else
+#include "Swiften/Parser/ExpatParser.h"
+#endif
+
+
+namespace Swift {
+
+PlatformXMLParserFactory::PlatformXMLParserFactory() {
+}
+
+XMLParser* PlatformXMLParserFactory::createXMLParser(XMLParserClient* client) {
+#ifdef HAVE_LIBXML
+ return new LibXMLParser(client);
+#else
+ return new ExpatParser(client);
+#endif
+}
+
+}
diff --git a/Swiften/Parser/PlatformXMLParserFactory.h b/Swiften/Parser/PlatformXMLParserFactory.h
new file mode 100644
index 0000000..28b1657
--- /dev/null
+++ b/Swiften/Parser/PlatformXMLParserFactory.h
@@ -0,0 +1,15 @@
+#ifndef SWIFTEN_PlatformXMLParserFactory_H
+#define SWIFTEN_PlatformXMLParserFactory_H
+
+#include "Swiften/Parser/XMLParserFactory.h"
+
+namespace Swift {
+ class PlatformXMLParserFactory : public XMLParserFactory {
+ public:
+ PlatformXMLParserFactory();
+
+ virtual XMLParser* createXMLParser(XMLParserClient*);
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/PresenceParser.cpp b/Swiften/Parser/PresenceParser.cpp
new file mode 100644
index 0000000..72cdd4c
--- /dev/null
+++ b/Swiften/Parser/PresenceParser.cpp
@@ -0,0 +1,45 @@
+#include <iostream>
+
+#include "Swiften/Parser/PresenceParser.h"
+
+namespace Swift {
+
+PresenceParser::PresenceParser(PayloadParserFactoryCollection* factories) :
+ GenericStanzaParser<Presence>(factories) {
+}
+
+void PresenceParser::handleStanzaAttributes(const AttributeMap& attributes) {
+ AttributeMap::const_iterator type = attributes.find("type");
+ if (type != attributes.end()) {
+ if (type->second == "unavailable") {
+ getStanzaGeneric()->setType(Presence::Unavailable);
+ }
+ else if (type->second == "probe") {
+ getStanzaGeneric()->setType(Presence::Probe);
+ }
+ else if (type->second == "subscribe") {
+ getStanzaGeneric()->setType(Presence::Subscribe);
+ }
+ else if (type->second == "subscribed") {
+ getStanzaGeneric()->setType(Presence::Subscribed);
+ }
+ else if (type->second == "unsubscribe") {
+ getStanzaGeneric()->setType(Presence::Unsubscribe);
+ }
+ else if (type->second == "unsubscribed") {
+ getStanzaGeneric()->setType(Presence::Unsubscribed);
+ }
+ else if (type->second == "error") {
+ getStanzaGeneric()->setType(Presence::Error);
+ }
+ else {
+ std::cerr << "Unknown Presence type: " << type->second << std::endl;
+ getStanzaGeneric()->setType(Presence::Available);
+ }
+ }
+ else {
+ getStanzaGeneric()->setType(Presence::Available);
+ }
+}
+
+}
diff --git a/Swiften/Parser/PresenceParser.h b/Swiften/Parser/PresenceParser.h
new file mode 100644
index 0000000..b3ba445
--- /dev/null
+++ b/Swiften/Parser/PresenceParser.h
@@ -0,0 +1,17 @@
+#ifndef SWIFTEN_PresenceParser_H
+#define SWIFTEN_PresenceParser_H
+
+#include "Swiften/Parser/GenericStanzaParser.h"
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+ class PresenceParser : public GenericStanzaParser<Presence> {
+ public:
+ PresenceParser(PayloadParserFactoryCollection* factories);
+
+ private:
+ virtual void handleStanzaAttributes(const AttributeMap&);
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/SerializingParser.cpp b/Swiften/Parser/SerializingParser.cpp
new file mode 100644
index 0000000..f69e732
--- /dev/null
+++ b/Swiften/Parser/SerializingParser.cpp
@@ -0,0 +1,41 @@
+#include "Swiften/Parser/SerializingParser.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Base/foreach.h"
+#include <iostream>
+
+namespace Swift {
+
+SerializingParser::SerializingParser() {
+}
+
+void SerializingParser::handleStartElement(const String& tag, const String& ns, const AttributeMap& attributes) {
+ boost::shared_ptr<XMLElement> element(new XMLElement(tag, ns));
+ for (AttributeMap::const_iterator i = attributes.begin(); i != attributes.end(); ++i) {
+ element->setAttribute((*i).first, (*i).second);
+ }
+
+ if (elementStack_.empty()) {
+ rootElement_ = element;
+ }
+ else {
+ (*(elementStack_.end() - 1))->addNode(element);
+ }
+ elementStack_.push_back(element);
+}
+
+void SerializingParser::handleEndElement(const String&, const String&) {
+ assert(!elementStack_.empty());
+ elementStack_.pop_back();
+}
+
+void SerializingParser::handleCharacterData(const String& data) {
+ if (!elementStack_.empty()) {
+ (*(elementStack_.end()-1))->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(data)));
+ }
+}
+
+String SerializingParser::getResult() const {
+ return (rootElement_ ? rootElement_->serialize() : "");
+}
+
+}
diff --git a/Swiften/Parser/SerializingParser.h b/Swiften/Parser/SerializingParser.h
new file mode 100644
index 0000000..b1d4575
--- /dev/null
+++ b/Swiften/Parser/SerializingParser.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_SerializingParser_H
+#define SWIFTEN_SerializingParser_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/AttributeMap.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class SerializingParser {
+ public:
+ SerializingParser();
+
+ void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes);
+ void handleEndElement(const String& element, const String& ns);
+ void handleCharacterData(const String& data);
+
+ String getResult() const;
+
+ private:
+ std::vector< boost::shared_ptr<XMLElement> > elementStack_;
+ boost::shared_ptr<XMLElement> rootElement_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/StanzaParser.cpp b/Swiften/Parser/StanzaParser.cpp
new file mode 100644
index 0000000..952265c
--- /dev/null
+++ b/Swiften/Parser/StanzaParser.cpp
@@ -0,0 +1,78 @@
+#include "Swiften/Parser/StanzaParser.h"
+
+#include <iostream>
+#include <cassert>
+
+#include "Swiften/Parser/PayloadParser.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/UnknownPayloadParser.h"
+
+namespace Swift {
+
+StanzaParser::StanzaParser(PayloadParserFactoryCollection* factories) :
+ currentDepth_(0), factories_(factories) {
+}
+
+StanzaParser::~StanzaParser() {
+}
+
+void StanzaParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ if (inStanza()) {
+ if (!inPayload()) {
+ assert(!currentPayloadParser_.get());
+ PayloadParserFactory* payloadParserFactory = factories_->getPayloadParserFactory(element, ns, attributes);
+ if (payloadParserFactory) {
+ currentPayloadParser_.reset(payloadParserFactory->createPayloadParser());
+ }
+ else {
+ currentPayloadParser_.reset(new UnknownPayloadParser());
+ }
+ }
+ assert(currentPayloadParser_.get());
+ currentPayloadParser_->handleStartElement(element, ns, attributes);
+ }
+ else {
+ AttributeMap::const_iterator from = attributes.find("from");
+ if (from != attributes.end()) {
+ getStanza()->setFrom(JID(from->second));
+ }
+ AttributeMap::const_iterator to = attributes.find("to");
+ if (to != attributes.end()) {
+ getStanza()->setTo(JID(to->second));
+ }
+ AttributeMap::const_iterator id = attributes.find("id");
+ if (id != attributes.end()) {
+ getStanza()->setID(id->second);
+ }
+ handleStanzaAttributes(attributes);
+ }
+ ++currentDepth_;
+}
+
+void StanzaParser::handleEndElement(const String& element, const String& ns) {
+ assert(inStanza());
+ if (inPayload()) {
+ assert(currentPayloadParser_.get());
+ currentPayloadParser_->handleEndElement(element, ns);
+ --currentDepth_;
+ if (!inPayload()) {
+ boost::shared_ptr<Payload> payload(currentPayloadParser_->getPayload());
+ if (payload) {
+ getStanza()->addPayload(payload);
+ }
+ currentPayloadParser_.reset();
+ }
+ }
+ else {
+ --currentDepth_;
+ }
+}
+
+void StanzaParser::handleCharacterData(const String& data) {
+ if (currentPayloadParser_.get()) {
+ currentPayloadParser_->handleCharacterData(data);
+ }
+}
+
+}
diff --git a/Swiften/Parser/StanzaParser.h b/Swiften/Parser/StanzaParser.h
new file mode 100644
index 0000000..c6cf753
--- /dev/null
+++ b/Swiften/Parser/StanzaParser.h
@@ -0,0 +1,48 @@
+#ifndef SWIFTEN_StanzaParser_H
+#define SWIFTEN_StanzaParser_H
+
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Parser/ElementParser.h"
+#include "Swiften/Parser/AttributeMap.h"
+
+namespace Swift {
+ class PayloadParser;
+ class PayloadParserFactoryCollection;
+
+ class StanzaParser : public ElementParser, public boost::noncopyable {
+ public:
+ StanzaParser(PayloadParserFactoryCollection* factories);
+ ~StanzaParser();
+
+ void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes);
+ void handleEndElement(const String& element, const String& ns);
+ void handleCharacterData(const String& data);
+
+ virtual boost::shared_ptr<Element> getElement() const = 0;
+ virtual void handleStanzaAttributes(const AttributeMap&) {}
+
+ virtual boost::shared_ptr<Stanza> getStanza() const {
+ return boost::dynamic_pointer_cast<Stanza>(getElement());
+ }
+
+ private:
+ bool inPayload() const {
+ return currentDepth_ > 1;
+ }
+
+ bool inStanza() const {
+ return currentDepth_ > 0;
+ }
+
+
+ private:
+ int currentDepth_;
+ PayloadParserFactoryCollection* factories_;
+ std::auto_ptr<PayloadParser> currentPayloadParser_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/StartTLSFailureParser.h b/Swiften/Parser/StartTLSFailureParser.h
new file mode 100644
index 0000000..c955ca0
--- /dev/null
+++ b/Swiften/Parser/StartTLSFailureParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StartTLSFailureParser_H
+#define SWIFTEN_StartTLSFailureParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/StartTLSFailure.h"
+
+namespace Swift {
+ class StartTLSFailureParser : public GenericElementParser<StartTLSFailure> {
+ public:
+ StartTLSFailureParser() : GenericElementParser<StartTLSFailure>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/StartTLSParser.h b/Swiften/Parser/StartTLSParser.h
new file mode 100644
index 0000000..afacec2
--- /dev/null
+++ b/Swiften/Parser/StartTLSParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_StartTLSParser_H
+#define SWIFTEN_StartTLSParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/StartTLSRequest.h"
+
+namespace Swift {
+ class StartTLSParser : public GenericElementParser<StartTLSRequest> {
+ public:
+ StartTLSParser() : GenericElementParser<StartTLSRequest>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/StreamFeaturesParser.cpp b/Swiften/Parser/StreamFeaturesParser.cpp
new file mode 100644
index 0000000..5072e7c
--- /dev/null
+++ b/Swiften/Parser/StreamFeaturesParser.cpp
@@ -0,0 +1,61 @@
+#include "Swiften/Parser/StreamFeaturesParser.h"
+
+namespace Swift {
+
+StreamFeaturesParser::StreamFeaturesParser() : GenericElementParser<StreamFeatures>(), currentDepth_(0), inMechanisms_(false), inMechanism_(false), inCompression_(false), inCompressionMethod_(false) {
+}
+
+void StreamFeaturesParser::handleStartElement(const String& element, const String& ns, const AttributeMap&) {
+ if (currentDepth_ == 1) {
+ if (element == "starttls" && ns == "urn:ietf:params:xml:ns:xmpp-tls") {
+ getElementGeneric()->setHasStartTLS();
+ }
+ else if (element == "session" && ns == "urn:ietf:params:xml:ns:xmpp-session") {
+ getElementGeneric()->setHasSession();
+ }
+ else if (element == "bind" && ns == "urn:ietf:params:xml:ns:xmpp-bind") {
+ getElementGeneric()->setHasResourceBind();
+ }
+ else if (element == "mechanisms" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
+ inMechanisms_ = true;
+ }
+ else if (element == "compression" && ns == "http://jabber.org/features/compress") {
+ inCompression_ = true;
+ }
+ }
+ else if (currentDepth_ == 2) {
+ if (inCompression_ && element == "method") {
+ inCompressionMethod_ = true;
+ currentText_ = "";
+ }
+ else if (inMechanisms_ && element == "mechanism") {
+ inMechanism_ = true;
+ currentText_ = "";
+ }
+ }
+ ++currentDepth_;
+}
+
+void StreamFeaturesParser::handleEndElement(const String&, const String&) {
+ --currentDepth_;
+ if (currentDepth_ == 1) {
+ inCompression_ = false;
+ inMechanisms_ = false;
+ }
+ else if (currentDepth_ == 2) {
+ if (inCompressionMethod_) {
+ getElementGeneric()->addCompressionMethod(currentText_);
+ inCompressionMethod_ = false;
+ }
+ else if (inMechanism_) {
+ getElementGeneric()->addAuthenticationMechanism(currentText_);
+ inMechanism_ = false;
+ }
+ }
+}
+
+void StreamFeaturesParser::handleCharacterData(const String& data) {
+ currentText_ += data;
+}
+
+}
diff --git a/Swiften/Parser/StreamFeaturesParser.h b/Swiften/Parser/StreamFeaturesParser.h
new file mode 100644
index 0000000..184a7e6
--- /dev/null
+++ b/Swiften/Parser/StreamFeaturesParser.h
@@ -0,0 +1,28 @@
+#ifndef SWIFTEN_STREAMFEATURESPARSER_H
+#define SWIFTEN_STREAMFEATURESPARSER_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/StreamFeatures.h"
+
+namespace Swift {
+ class StreamFeaturesParser : public GenericElementParser<StreamFeatures> {
+ public:
+ StreamFeaturesParser();
+
+ private:
+ void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes);
+ void handleEndElement(const String& element, const String& ns);
+ void handleCharacterData(const String& data);
+
+ private:
+ int currentDepth_;
+ String currentText_;
+ bool inMechanisms_;
+ bool inMechanism_;
+ bool inCompression_;
+ bool inCompressionMethod_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/TLSProceedParser.h b/Swiften/Parser/TLSProceedParser.h
new file mode 100644
index 0000000..2ad5438
--- /dev/null
+++ b/Swiften/Parser/TLSProceedParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_TLSProceedParser_H
+#define SWIFTEN_TLSProceedParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/TLSProceed.h"
+
+namespace Swift {
+ class TLSProceedParser : public GenericElementParser<TLSProceed> {
+ public:
+ TLSProceedParser() : GenericElementParser<TLSProceed>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/UnitTest/ElementParserTester.h b/Swiften/Parser/UnitTest/ElementParserTester.h
new file mode 100644
index 0000000..4d84c44
--- /dev/null
+++ b/Swiften/Parser/UnitTest/ElementParserTester.h
@@ -0,0 +1,10 @@
+#ifndef SWIFTEN_ElementParserTester_H
+#define SWIFTEN_ElementParserTester_H
+
+#include "Swiften/Parser/UnitTest/ParserTester.h"
+
+namespace Swift {
+ typedef ParserTester<ElementParser> ElementParserTester;
+}
+
+#endif
diff --git a/Swiften/Parser/UnitTest/IQParserTest.cpp b/Swiften/Parser/UnitTest/IQParserTest.cpp
new file mode 100644
index 0000000..22b0adc
--- /dev/null
+++ b/Swiften/Parser/UnitTest/IQParserTest.cpp
@@ -0,0 +1,70 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/IQParser.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/UnitTest/StanzaParserTester.h"
+
+using namespace Swift;
+
+class IQParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(IQParserTest);
+ CPPUNIT_TEST(testParse_Set);
+ CPPUNIT_TEST(testParse_Get);
+ CPPUNIT_TEST(testParse_Result);
+ CPPUNIT_TEST(testParse_Error);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ IQParserTest() {}
+
+ void setUp() {
+ factoryCollection_ = new PayloadParserFactoryCollection();
+ }
+
+ void tearDown() {
+ delete factoryCollection_;
+ }
+
+ void testParse_Set() {
+ IQParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<iq type=\"set\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(IQ::Set, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Get() {
+ IQParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<iq type=\"get\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(IQ::Get, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Result() {
+ IQParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<iq type=\"result\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(IQ::Result, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Error() {
+ IQParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<iq type=\"error\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(IQ::Error, testling.getStanzaGeneric()->getType());
+ }
+
+ private:
+ PayloadParserFactoryCollection* factoryCollection_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IQParserTest);
diff --git a/Swiften/Parser/UnitTest/Makefile.inc b/Swiften/Parser/UnitTest/Makefile.inc
new file mode 100644
index 0000000..4f9c59e
--- /dev/null
+++ b/Swiften/Parser/UnitTest/Makefile.inc
@@ -0,0 +1,9 @@
+UNITTEST_SOURCES += \
+ Swiften/Parser/UnitTest/XMPPParserTest.cpp \
+ Swiften/Parser/UnitTest/StanzaParserTest.cpp \
+ Swiften/Parser/UnitTest/MessageParserTest.cpp \
+ Swiften/Parser/UnitTest/PresenceParserTest.cpp \
+ Swiften/Parser/UnitTest/IQParserTest.cpp \
+ Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp \
+ Swiften/Parser/UnitTest/SerializingParserTest.cpp \
+ Swiften/Parser/UnitTest/XMLParserTest.cpp
diff --git a/Swiften/Parser/UnitTest/MessageParserTest.cpp b/Swiften/Parser/UnitTest/MessageParserTest.cpp
new file mode 100644
index 0000000..61a8d20
--- /dev/null
+++ b/Swiften/Parser/UnitTest/MessageParserTest.cpp
@@ -0,0 +1,80 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/MessageParser.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/UnitTest/StanzaParserTester.h"
+
+using namespace Swift;
+
+class MessageParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(MessageParserTest);
+ CPPUNIT_TEST(testParse_Normal);
+ CPPUNIT_TEST(testParse_Chat);
+ CPPUNIT_TEST(testParse_Error);
+ CPPUNIT_TEST(testParse_Groupchat);
+ CPPUNIT_TEST(testParse_Headline);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ MessageParserTest() {}
+
+ void setUp() {
+ factoryCollection_ = new PayloadParserFactoryCollection();
+ }
+
+ void tearDown() {
+ delete factoryCollection_;
+ }
+
+ void testParse_Chat() {
+ MessageParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<message type=\"chat\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Message::Chat, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Groupchat() {
+ MessageParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<message type=\"groupchat\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Message::Groupchat, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Error() {
+ MessageParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<message type=\"error\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Message::Error, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Headline() {
+ MessageParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<message type=\"headline\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Message::Headline, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Normal() {
+ MessageParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<message/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Message::Normal, testling.getStanzaGeneric()->getType());
+ }
+
+ private:
+ PayloadParserFactoryCollection* factoryCollection_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MessageParserTest);
diff --git a/Swiften/Parser/UnitTest/ParserTester.h b/Swiften/Parser/UnitTest/ParserTester.h
new file mode 100644
index 0000000..7aacc8e
--- /dev/null
+++ b/Swiften/Parser/UnitTest/ParserTester.h
@@ -0,0 +1,44 @@
+#ifndef SWIFTEN_ParserTester_H
+#define SWIFTEN_ParserTester_H
+
+#include "Swiften/Parser/XMLParserClient.h"
+#include "Swiften/Parser/PlatformXMLParserFactory.h"
+#include "Swiften/Parser/XMLParser.h"
+
+namespace Swift {
+ class XMLParser;
+
+ template<typename ParserType>
+ class ParserTester : public XMLParserClient {
+ public:
+ ParserTester(ParserType* parser) : parser_(parser) {
+ xmlParser_ = PlatformXMLParserFactory().createXMLParser(this);
+ }
+
+ ~ParserTester() {
+ delete xmlParser_;
+ }
+
+ bool parse(const String& data) {
+ return xmlParser_->parse(data);
+ }
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ parser_->handleStartElement(element, ns, attributes);
+ }
+
+ virtual void handleEndElement(const String& element, const String& ns) {
+ parser_->handleEndElement(element, ns);
+ }
+
+ virtual void handleCharacterData(const String& data) {
+ parser_->handleCharacterData(data);
+ }
+
+ private:
+ XMLParser* xmlParser_;
+ ParserType* parser_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/UnitTest/PresenceParserTest.cpp b/Swiften/Parser/UnitTest/PresenceParserTest.cpp
new file mode 100644
index 0000000..5305161
--- /dev/null
+++ b/Swiften/Parser/UnitTest/PresenceParserTest.cpp
@@ -0,0 +1,110 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PresenceParser.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/UnitTest/StanzaParserTester.h"
+
+using namespace Swift;
+
+class PresenceParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PresenceParserTest);
+ CPPUNIT_TEST(testParse_Available);
+ CPPUNIT_TEST(testParse_Unavailable);
+ CPPUNIT_TEST(testParse_Subscribe);
+ CPPUNIT_TEST(testParse_Subscribed);
+ CPPUNIT_TEST(testParse_Unsubscribe);
+ CPPUNIT_TEST(testParse_Unsubscribed);
+ CPPUNIT_TEST(testParse_Probe);
+ CPPUNIT_TEST(testParse_Error);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PresenceParserTest() {}
+
+ void setUp() {
+ factoryCollection_ = new PayloadParserFactoryCollection();
+ }
+
+ void tearDown() {
+ delete factoryCollection_;
+ }
+
+ void testParse_Available() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Available, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Unavailable() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"unavailable\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Unavailable, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Probe() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"probe\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Probe, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Subscribe() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"subscribe\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Subscribe, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Subscribed() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"subscribed\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Subscribed, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Unsubscribe() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"unsubscribe\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Unsubscribe, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Unsubscribed() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"unsubscribed\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Unsubscribed, testling.getStanzaGeneric()->getType());
+ }
+
+ void testParse_Error() {
+ PresenceParser testling(factoryCollection_);
+ StanzaParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse("<presence type=\"error\"/>"));
+
+ CPPUNIT_ASSERT_EQUAL(Presence::Error, testling.getStanzaGeneric()->getType());
+ }
+
+ private:
+ PayloadParserFactoryCollection* factoryCollection_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PresenceParserTest);
diff --git a/Swiften/Parser/UnitTest/SerializingParserTest.cpp b/Swiften/Parser/UnitTest/SerializingParserTest.cpp
new file mode 100644
index 0000000..e08a3d0
--- /dev/null
+++ b/Swiften/Parser/UnitTest/SerializingParserTest.cpp
@@ -0,0 +1,58 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/SerializingParser.h"
+#include "Swiften/Parser/UnitTest/StanzaParserTester.h"
+
+using namespace Swift;
+
+class SerializingParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SerializingParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST(testParse_Empty);
+ CPPUNIT_TEST(testParse_ToplevelCharacterData);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SerializingParserTest() {}
+
+ void testParse() {
+ SerializingParser testling;
+ ParserTester<SerializingParser> parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<message type=\"chat\" to=\"me@foo.com\">"
+ "<body>Hello&lt;&amp;World</body>"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "foo<b>bar</b>baz"
+ "</html>"
+ "</message>"));
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<message to=\"me@foo.com\" type=\"chat\">"
+ "<body>Hello&lt;&amp;World</body>"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">foo<b xmlns=\"http://www.w3.org/1999/xhtml\">bar</b>baz</html>"
+ "</message>"), testling.getResult());
+ }
+
+ void testParse_Empty() {
+ SerializingParser testling;
+
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.getResult());
+ }
+
+ void testParse_ToplevelCharacterData() {
+ SerializingParser testling;
+
+ AttributeMap attributes;
+ testling.handleCharacterData("foo");
+ testling.handleStartElement("message", "", attributes);
+ testling.handleEndElement("message", "");
+ testling.handleCharacterData("bar");
+
+ CPPUNIT_ASSERT_EQUAL(String("<message/>"), testling.getResult());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SerializingParserTest);
diff --git a/Swiften/Parser/UnitTest/StanzaParserTest.cpp b/Swiften/Parser/UnitTest/StanzaParserTest.cpp
new file mode 100644
index 0000000..3cb1879
--- /dev/null
+++ b/Swiften/Parser/UnitTest/StanzaParserTest.cpp
@@ -0,0 +1,209 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/StanzaParser.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Elements/Payload.h"
+
+using namespace Swift;
+
+class StanzaParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StanzaParserTest);
+ CPPUNIT_TEST(testHandleEndElement_OnePayload);
+ CPPUNIT_TEST(testHandleEndElement_MultiplePayloads);
+ CPPUNIT_TEST(testHandleEndElement_StrayCharacterData);
+ CPPUNIT_TEST(testHandleEndElement_UnknownPayload);
+ CPPUNIT_TEST(testHandleParse_BasicAttributes);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StanzaParserTest() {}
+
+ void setUp() {
+ factoryCollection_ = new PayloadParserFactoryCollection();
+ factoryCollection_->addFactory(&factory1_);
+ factoryCollection_->addFactory(&factory2_);
+ }
+
+ void tearDown() {
+ delete factoryCollection_;
+ }
+
+ void testHandleEndElement_OnePayload() {
+ MyStanzaParser testling(factoryCollection_);
+
+ AttributeMap attributes;
+ attributes["foo"] = "fum";
+ attributes["bar"] = "baz";
+ testling.handleStartElement("mystanza", "", attributes);
+ testling.handleStartElement("mypayload1", "", attributes);
+ testling.handleStartElement("child", "", attributes);
+ testling.handleEndElement("child", "");
+ testling.handleEndElement("mypayload1", "");
+ testling.handleEndElement("mystanza", "");
+
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload1>());
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload1>()->hasChild);
+ }
+
+ void testHandleEndElement_MultiplePayloads() {
+ MyStanzaParser testling(factoryCollection_);
+
+ AttributeMap attributes;
+ testling.handleStartElement("mystanza", "", attributes);
+ testling.handleStartElement("mypayload1", "", attributes);
+ testling.handleEndElement("mypayload1", "");
+ testling.handleStartElement("mypayload2", "", attributes);
+ testling.handleEndElement("mypayload2", "");
+ testling.handleEndElement("mystanza", "");
+
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload1>());
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload2>());
+ }
+
+ void testHandleEndElement_StrayCharacterData() {
+ MyStanzaParser testling(factoryCollection_);
+
+ AttributeMap attributes;
+ testling.handleStartElement("mystanza", "", attributes);
+ testling.handleStartElement("mypayload1", "", attributes);
+ testling.handleEndElement("mypayload1", "");
+ testling.handleCharacterData("bla");
+ testling.handleStartElement("mypayload2", "", attributes);
+ testling.handleEndElement("mypayload2", "");
+ testling.handleEndElement("mystanza", "");
+
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload1>());
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload2>());
+ }
+
+ void testHandleEndElement_UnknownPayload() {
+ MyStanzaParser testling(factoryCollection_);
+
+ AttributeMap attributes;
+ testling.handleStartElement("mystanza", "", attributes);
+ testling.handleStartElement("mypayload1", "", attributes);
+ testling.handleEndElement("mypayload1", "");
+ testling.handleStartElement("unknown-payload", "", attributes);
+ testling.handleStartElement("unknown-payload-child", "", attributes);
+ testling.handleEndElement("unknown-payload-child", "");
+ testling.handleEndElement("unknown-payload", "");
+ testling.handleStartElement("mypayload2", "", attributes);
+ testling.handleEndElement("mypayload2", "");
+ testling.handleEndElement("mystanza", "");
+
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload1>());
+ CPPUNIT_ASSERT(testling.getStanza()->getPayload<MyPayload2>());
+ }
+
+ void testHandleParse_BasicAttributes() {
+ MyStanzaParser testling(factoryCollection_);
+
+ AttributeMap attributes;
+ attributes["to"] = "foo@example.com/blo";
+ attributes["from"] = "bar@example.com/baz";
+ attributes["id"] = "id-123";
+ testling.handleStartElement("mystanza", "", attributes);
+ testling.handleEndElement("mypayload1", "");
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@example.com/blo"), testling.getStanza()->getTo());
+ CPPUNIT_ASSERT_EQUAL(JID("bar@example.com/baz"), testling.getStanza()->getFrom());
+ CPPUNIT_ASSERT_EQUAL(String("id-123"), testling.getStanza()->getID());
+ }
+
+ private:
+ class MyPayload1 : public Payload
+ {
+ public:
+ MyPayload1() : hasChild(false) {}
+
+ bool hasChild;
+ };
+
+ class MyPayload1Parser : public GenericPayloadParser<MyPayload1>
+ {
+ public:
+ MyPayload1Parser() {}
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap&) {
+ if (element != "mypayload1") {
+ getPayloadInternal()->hasChild = true;
+ }
+ }
+
+ virtual void handleEndElement(const String&, const String&) {}
+ virtual void handleCharacterData(const String&) {}
+ };
+
+ class MyPayload1ParserFactory : public PayloadParserFactory
+ {
+ public:
+ MyPayload1ParserFactory() {}
+
+ PayloadParser* createPayloadParser() { return new MyPayload1Parser(); }
+
+ bool canParse(const String& element, const String&, const AttributeMap&) const {
+ return element == "mypayload1";
+ }
+ };
+
+ class MyPayload2 : public Payload
+ {
+ public:
+ MyPayload2() {}
+ };
+
+ class MyPayload2Parser : public GenericPayloadParser<MyPayload2>
+ {
+ public:
+ MyPayload2Parser() {}
+
+ virtual void handleStartElement(const String&, const String&, const AttributeMap&) {}
+ virtual void handleEndElement(const String&, const String&) {}
+ virtual void handleCharacterData(const String&) {}
+ };
+
+
+ class MyPayload2ParserFactory : public PayloadParserFactory
+ {
+ public:
+ MyPayload2ParserFactory() {}
+
+ PayloadParser* createPayloadParser() { return new MyPayload2Parser(); }
+ bool canParse(const String& element, const String&, const AttributeMap&) const {
+ return element == "mypayload2";
+ }
+ };
+
+ class MyStanza : public Stanza
+ {
+ public:
+ MyStanza() {}
+ };
+
+ class MyStanzaParser : public StanzaParser
+ {
+ public:
+ MyStanzaParser(PayloadParserFactoryCollection* collection) : StanzaParser(collection)
+ {
+ stanza_ = boost::shared_ptr<MyStanza>(new MyStanza());
+ }
+
+ virtual boost::shared_ptr<Element> getElement() const {
+ return stanza_;
+ }
+
+ private:
+ boost::shared_ptr<MyStanza> stanza_;
+ };
+
+ MyPayload1ParserFactory factory1_;
+ MyPayload2ParserFactory factory2_;
+ PayloadParserFactoryCollection* factoryCollection_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StanzaParserTest);
diff --git a/Swiften/Parser/UnitTest/StanzaParserTester.h b/Swiften/Parser/UnitTest/StanzaParserTester.h
new file mode 100644
index 0000000..cbd484f
--- /dev/null
+++ b/Swiften/Parser/UnitTest/StanzaParserTester.h
@@ -0,0 +1,11 @@
+#ifndef SWIFTEN_StanzaParserTester_H
+#define SWIFTEN_StanzaParserTester_H
+
+#include "Swiften/Parser/StanzaParser.h"
+#include "Swiften/Parser/UnitTest/ParserTester.h"
+
+namespace Swift {
+ typedef ParserTester<StanzaParser> StanzaParserTester;
+}
+
+#endif
diff --git a/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp b/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp
new file mode 100644
index 0000000..7fd0512
--- /dev/null
+++ b/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp
@@ -0,0 +1,63 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/StreamFeaturesParser.h"
+#include "Swiften/Parser/UnitTest/ElementParserTester.h"
+
+using namespace Swift;
+
+class StreamFeaturesParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StreamFeaturesParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST(testParse_Empty);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StreamFeaturesParserTest() {}
+
+ void testParse() {
+ StreamFeaturesParser testling;
+ ElementParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<stream:features xmlns:stream='http://etherx.jabber.org/streams'>"
+ "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>"
+ "<compression xmlns=\"http://jabber.org/features/compress\">"
+ "<method>zlib</method>"
+ "<method>lzw</method>"
+ "</compression>"
+ "<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
+ "<mechanism>DIGEST-MD5</mechanism>"
+ "<mechanism>PLAIN</mechanism>"
+ "</mechanisms>"
+ "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
+ "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
+ "</stream:features>"));
+
+ StreamFeatures* element = dynamic_cast<StreamFeatures*>(testling.getElement().get());
+ CPPUNIT_ASSERT(element->hasStartTLS());
+ CPPUNIT_ASSERT(element->hasSession());
+ CPPUNIT_ASSERT(element->hasResourceBind());
+ CPPUNIT_ASSERT(element->hasCompressionMethod("zlib"));
+ CPPUNIT_ASSERT(element->hasCompressionMethod("lzw"));
+ CPPUNIT_ASSERT(element->hasAuthenticationMechanisms());
+ CPPUNIT_ASSERT(element->hasAuthenticationMechanism("DIGEST-MD5"));
+ CPPUNIT_ASSERT(element->hasAuthenticationMechanism("PLAIN"));
+ }
+
+ void testParse_Empty() {
+ StreamFeaturesParser testling;
+ ElementParserTester parser(&testling);
+
+ parser.parse("<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
+
+ StreamFeatures* element = dynamic_cast<StreamFeatures*>(testling.getElement().get());
+ CPPUNIT_ASSERT(!element->hasStartTLS());
+ CPPUNIT_ASSERT(!element->hasSession());
+ CPPUNIT_ASSERT(!element->hasResourceBind());
+ CPPUNIT_ASSERT(!element->hasAuthenticationMechanisms());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StreamFeaturesParserTest);
diff --git a/Swiften/Parser/UnitTest/XMLParserTest.cpp b/Swiften/Parser/UnitTest/XMLParserTest.cpp
new file mode 100644
index 0000000..a26b31b
--- /dev/null
+++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp
@@ -0,0 +1,194 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+
+#ifdef HAVE_CONFIG_H
+#include "Swiften/config.h"
+#endif
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/XMLParserClient.h"
+#ifdef HAVE_EXPAT
+#include "Swiften/Parser/ExpatParser.h"
+#endif
+#ifdef HAVE_LIBXML
+#include "Swiften/Parser/LibXMLParser.h"
+#endif
+
+using namespace Swift;
+
+template <typename ParserType>
+class XMLParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMLParserTest);
+ CPPUNIT_TEST(testParse_NestedElements);
+ CPPUNIT_TEST(testParse_CharacterData);
+ CPPUNIT_TEST(testParse_NamespacePrefix);
+ CPPUNIT_TEST(testParse_UnhandledXML);
+ CPPUNIT_TEST(testParse_InvalidXML);
+ CPPUNIT_TEST(testParse_InErrorState);
+ CPPUNIT_TEST(testParse_Incremental);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ XMLParserTest() {}
+
+ void testParse_NestedElements() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse(
+ "<iq type=\"get\">"
+ "<query xmlns='jabber:iq:version'/>"
+ "</iq>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[0].data);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.size());
+ CPPUNIT_ASSERT_EQUAL(String("get"), client_.events[0].attributes["type"]);
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("query"), client_.events[1].data);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[1].attributes.size());
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[1].attributes["xmlns"]);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(String("query"), client_.events[2].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[3].data);
+ }
+
+ void testParse_CharacterData() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse("<html>bla<i>bli</i>blo</html>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(7), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("html"), client_.events[0].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::CharacterData, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("bla"), client_.events[1].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(String("i"), client_.events[2].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::CharacterData, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(String("bli"), client_.events[3].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[4].type);
+ CPPUNIT_ASSERT_EQUAL(String("i"), client_.events[4].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::CharacterData, client_.events[5].type);
+ CPPUNIT_ASSERT_EQUAL(String("blo"), client_.events[5].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[6].type);
+ CPPUNIT_ASSERT_EQUAL(String("html"), client_.events[6].data);
+ }
+
+ void testParse_NamespacePrefix() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse("<p:x xmlns:p='bla'><p:y/></p:x>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("p:x"), client_.events[0].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("p:y"), client_.events[1].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(String("p:y"), client_.events[2].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(String("p:x"), client_.events[3].data);
+ }
+
+ void testParse_UnhandledXML() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse("<iq><!-- Testing --></iq>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[0].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[1].data);
+ }
+
+ void testParse_InvalidXML() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(!testling.parse("<iq><bla></iq>"));
+ }
+
+ void testParse_InErrorState() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(!testling.parse("<iq><bla></iq>"));
+ CPPUNIT_ASSERT(!testling.parse("<iq/>"));
+ }
+
+ void testParse_Incremental() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse("<iq"));
+ CPPUNIT_ASSERT(testling.parse("></iq>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[0].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[1].data);
+ }
+
+ private:
+ class Client : public XMLParserClient {
+ public:
+ enum Type { StartElement, EndElement, CharacterData };
+ struct Event {
+ Event(
+ Type type,
+ const String& data,
+ const AttributeMap& attributes)
+ : type(type), data(data), attributes(attributes) {}
+ Event(Type type, const String& data)
+ : type(type), data(data) {}
+
+ Type type;
+ String data;
+ AttributeMap attributes;
+ };
+
+ Client() {}
+
+ virtual void handleStartElement(const String& element, const AttributeMap& attributes) {
+ events.push_back(Event(StartElement, element, attributes));
+ }
+
+ virtual void handleEndElement(const String& element) {
+ events.push_back(Event(EndElement, element));
+ }
+
+ virtual void handleCharacterData(const String& data) {
+ events.push_back(Event(CharacterData, data));
+ }
+
+ std::vector<Event> events;
+ } client_;
+};
+
+#ifdef HAVE_EXPAT
+CPPUNIT_TEST_SUITE_REGISTRATION(XMLParserTest<ExpatParser>);
+#endif
+#ifdef HAVE_LIBXML
+CPPUNIT_TEST_SUITE_REGISTRATION(XMLParserTest<LibXMLParser>);
+#endif
diff --git a/Swiften/Parser/UnitTest/XMPPParserTest.cpp b/Swiften/Parser/UnitTest/XMPPParserTest.cpp
new file mode 100644
index 0000000..787828c
--- /dev/null
+++ b/Swiften/Parser/UnitTest/XMPPParserTest.cpp
@@ -0,0 +1,166 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/XMPPParser.h"
+#include "Swiften/Parser/ElementParser.h"
+#include "Swiften/Parser/XMPPParserClient.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/UnknownElement.h"
+
+using namespace Swift;
+
+class XMPPParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMPPParserTest);
+ CPPUNIT_TEST(testParse_SimpleSession);
+ CPPUNIT_TEST(testParse_Presence);
+ CPPUNIT_TEST(testParse_IQ);
+ CPPUNIT_TEST(testParse_Message);
+ CPPUNIT_TEST(testParse_StreamFeatures);
+ CPPUNIT_TEST(testParse_UnknownElement);
+ CPPUNIT_TEST(testParse_StrayCharacterData);
+ CPPUNIT_TEST(testParse_InvalidStreamStart);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ XMPPParserTest() {}
+
+ void testParse_SimpleSession() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<?xml version='1.0'?>"));
+ CPPUNIT_ASSERT(testling.parse("<stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' >"));
+ CPPUNIT_ASSERT(testling.parse("<presence/>"));
+ CPPUNIT_ASSERT(testling.parse("<presence/>"));
+ CPPUNIT_ASSERT(testling.parse("<iq/>"));
+ CPPUNIT_ASSERT(testling.parse("</stream:stream>"));
+
+ CPPUNIT_ASSERT_EQUAL(5, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::StreamStart, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(Client::StreamEnd, client_.events[4].type);
+ }
+
+ void testParse_Presence() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>"));
+ CPPUNIT_ASSERT(testling.parse("<presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[1].type);
+ CPPUNIT_ASSERT(dynamic_cast<Presence*>(client_.events[1].element.get()));
+ }
+
+ void testParse_IQ() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>"));
+ CPPUNIT_ASSERT(testling.parse("<iq/>"));
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[1].type);
+ CPPUNIT_ASSERT(dynamic_cast<IQ*>(client_.events[1].element.get()));
+ }
+
+ void testParse_Message() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>"));
+ CPPUNIT_ASSERT(testling.parse("<message/>"));
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[1].type);
+ CPPUNIT_ASSERT(dynamic_cast<Message*>(client_.events[1].element.get()));
+ }
+
+ void testParse_StreamFeatures() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>"));
+ CPPUNIT_ASSERT(testling.parse("<stream:features/>"));
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[1].type);
+ CPPUNIT_ASSERT(dynamic_cast<StreamFeatures*>(client_.events[1].element.get()));
+ }
+
+ void testParse_UnknownElement() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>"));
+ CPPUNIT_ASSERT(testling.parse("<presence/>"));
+ CPPUNIT_ASSERT(testling.parse("<foo/>"));
+ CPPUNIT_ASSERT(testling.parse("<bar/>"));
+ CPPUNIT_ASSERT(testling.parse("<presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(5, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[2].type);
+ CPPUNIT_ASSERT(dynamic_cast<UnknownElement*>(client_.events[2].element.get()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[3].type);
+ CPPUNIT_ASSERT(dynamic_cast<UnknownElement*>(client_.events[3].element.get()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[4].type);
+ CPPUNIT_ASSERT(dynamic_cast<Presence*>(client_.events[4].element.get()));
+ }
+
+ void testParse_StrayCharacterData() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>"));
+ CPPUNIT_ASSERT(testling.parse("<presence/>"));
+ CPPUNIT_ASSERT(testling.parse("bla"));
+ CPPUNIT_ASSERT(testling.parse("<iq/>"));
+
+ CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::ElementEvent, client_.events[2].type);
+ CPPUNIT_ASSERT(dynamic_cast<IQ*>(client_.events[2].element.get()));
+ }
+
+ void testParse_InvalidStreamStart() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(!testling.parse("<tream>"));
+ }
+
+ private:
+ class Client : public XMPPParserClient {
+ public:
+ enum Type { StreamStart, ElementEvent, StreamEnd };
+ struct Event {
+ Event(Type type, boost::shared_ptr<Element> element)
+ : type(type), element(element) {}
+
+ Event(Type type) : type(type) {}
+
+ Type type;
+ boost::shared_ptr<Element> element;
+ };
+
+ Client() {}
+
+ void handleStreamStart() {
+ events.push_back(Event(StreamStart));
+ }
+
+ void handleElement(boost::shared_ptr<Element> element) {
+ events.push_back(Event(ElementEvent, element));
+ }
+
+ void handleStreamEnd() {
+ events.push_back(Event(StreamEnd));
+ }
+
+ std::vector<Event> events;
+ } client_;
+ PayloadParserFactoryCollection factories_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPParserTest);
diff --git a/Swiften/Parser/UnknownElementParser.h b/Swiften/Parser/UnknownElementParser.h
new file mode 100644
index 0000000..c016664
--- /dev/null
+++ b/Swiften/Parser/UnknownElementParser.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_UnknownElementParser_H
+#define SWIFTEN_UnknownElementParser_H
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/UnknownElement.h"
+
+namespace Swift {
+ class UnknownElementParser : public GenericElementParser<UnknownElement> {
+ public:
+ UnknownElementParser() : GenericElementParser<UnknownElement>() {}
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/UnknownPayloadParser.h b/Swiften/Parser/UnknownPayloadParser.h
new file mode 100644
index 0000000..ad56885
--- /dev/null
+++ b/Swiften/Parser/UnknownPayloadParser.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_UNKNOWNPAYLOADPARSER_H
+#define SWIFTEN_UNKNOWNPAYLOADPARSER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Parser/PayloadParser.h"
+
+namespace Swift {
+ class String;
+
+ class UnknownPayloadParser : public PayloadParser {
+ public:
+ UnknownPayloadParser() {}
+
+ virtual void handleStartElement(const String&, const String&, const AttributeMap&) {}
+ virtual void handleEndElement(const String&, const String&) {}
+ virtual void handleCharacterData(const String&) {}
+
+ virtual boost::shared_ptr<Payload> getPayload() const {
+ return boost::shared_ptr<Payload>();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/XMLParser.cpp b/Swiften/Parser/XMLParser.cpp
new file mode 100644
index 0000000..a827e99
--- /dev/null
+++ b/Swiften/Parser/XMLParser.cpp
@@ -0,0 +1,11 @@
+#include "Swiften/Parser/XMLParser.h"
+
+namespace Swift {
+
+XMLParser::XMLParser(XMLParserClient* client) : client_(client) {
+}
+
+XMLParser::~XMLParser() {
+}
+
+}
diff --git a/Swiften/Parser/XMLParser.h b/Swiften/Parser/XMLParser.h
new file mode 100644
index 0000000..7ed90db
--- /dev/null
+++ b/Swiften/Parser/XMLParser.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_XMLParser_H
+#define SWIFTEN_XMLParser_H
+
+namespace Swift {
+ class String;
+ class XMLParserClient;
+
+ class XMLParser {
+ public:
+ XMLParser(XMLParserClient* client);
+ virtual ~XMLParser();
+
+ virtual bool parse(const String& data) = 0;
+
+ protected:
+ XMLParserClient* getClient() const {
+ return client_;
+ }
+
+ private:
+ XMLParserClient* client_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/XMLParserClient.cpp b/Swiften/Parser/XMLParserClient.cpp
new file mode 100644
index 0000000..53a103f
--- /dev/null
+++ b/Swiften/Parser/XMLParserClient.cpp
@@ -0,0 +1,9 @@
+#include "Swiften/Parser/XMLParserClient.h"
+
+namespace Swift {
+
+XMLParserClient::~XMLParserClient() {
+}
+
+}
+
diff --git a/Swiften/Parser/XMLParserClient.h b/Swiften/Parser/XMLParserClient.h
new file mode 100644
index 0000000..7179ac6
--- /dev/null
+++ b/Swiften/Parser/XMLParserClient.h
@@ -0,0 +1,19 @@
+#ifndef XMLPARSERCLIENT_H
+#define XMLPARSERCLIENT_H
+
+#include "Swiften/Parser/AttributeMap.h"
+
+namespace Swift {
+ class String;
+
+ class XMLParserClient {
+ public:
+ virtual ~XMLParserClient();
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) = 0;
+ virtual void handleEndElement(const String& element, const String& ns) = 0;
+ virtual void handleCharacterData(const String& data) = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/XMLParserFactory.cpp b/Swiften/Parser/XMLParserFactory.cpp
new file mode 100644
index 0000000..e21bb5c
--- /dev/null
+++ b/Swiften/Parser/XMLParserFactory.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Parser/XMLParserFactory.h"
+
+namespace Swift {
+
+XMLParserFactory::~XMLParserFactory() {
+}
+
+}
diff --git a/Swiften/Parser/XMLParserFactory.h b/Swiften/Parser/XMLParserFactory.h
new file mode 100644
index 0000000..8d67b17
--- /dev/null
+++ b/Swiften/Parser/XMLParserFactory.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_XMLParserFactory_H
+#define SWIFTEN_XMLParserFactory_H
+
+namespace Swift {
+ class XMLParser;
+ class XMLParserClient;
+
+ class XMLParserFactory {
+ public:
+ virtual ~XMLParserFactory();
+
+ virtual XMLParser* createXMLParser(XMLParserClient*) = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/XMPPParser.cpp b/Swiften/Parser/XMPPParser.cpp
new file mode 100644
index 0000000..e05cbca
--- /dev/null
+++ b/Swiften/Parser/XMPPParser.cpp
@@ -0,0 +1,145 @@
+#include "Swiften/Parser/XMPPParser.h"
+
+#include <iostream>
+#include <cassert>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Parser/XMLParser.h"
+#include "Swiften/Parser/PlatformXMLParserFactory.h"
+#include "Swiften/Parser/XMPPParserClient.h"
+#include "Swiften/Parser/XMPPParser.h"
+#include "Swiften/Parser/ElementParser.h"
+#include "Swiften/Parser/PresenceParser.h"
+#include "Swiften/Parser/IQParser.h"
+#include "Swiften/Parser/MessageParser.h"
+#include "Swiften/Parser/StreamFeaturesParser.h"
+#include "Swiften/Parser/AuthRequestParser.h"
+#include "Swiften/Parser/AuthSuccessParser.h"
+#include "Swiften/Parser/AuthFailureParser.h"
+#include "Swiften/Parser/StartTLSParser.h"
+#include "Swiften/Parser/StartTLSFailureParser.h"
+#include "Swiften/Parser/CompressParser.h"
+#include "Swiften/Parser/CompressFailureParser.h"
+#include "Swiften/Parser/CompressedParser.h"
+#include "Swiften/Parser/UnknownElementParser.h"
+#include "Swiften/Parser/TLSProceedParser.h"
+
+// TODO: Whenever an error occurs in the handlers, stop the parser by returing
+// a bool value, and stopping the XML parser
+
+namespace Swift {
+
+XMPPParser::XMPPParser(
+ XMPPParserClient* client,
+ PayloadParserFactoryCollection* payloadParserFactories) :
+ xmlParser_(0),
+ client_(client),
+ payloadParserFactories_(payloadParserFactories),
+ currentDepth_(0),
+ currentElementParser_(0),
+ parseErrorOccurred_(false) {
+ xmlParser_ = PlatformXMLParserFactory().createXMLParser(this);
+}
+
+XMPPParser::~XMPPParser() {
+ delete currentElementParser_;
+ delete xmlParser_;
+}
+
+bool XMPPParser::parse(const String& data) {
+ bool xmlParseResult = xmlParser_->parse(data);
+ return xmlParseResult && !parseErrorOccurred_;
+}
+
+void XMPPParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ if (!inStream()) {
+ if (element == "stream" && ns == "http://etherx.jabber.org/streams") {
+ client_->handleStreamStart();
+ }
+ else {
+ parseErrorOccurred_ = true;
+ }
+ }
+ else {
+ if (!inElement()) {
+ assert(!currentElementParser_);
+ delete currentElementParser_;
+ currentElementParser_ = createElementParser(element, ns);
+ }
+ currentElementParser_->handleStartElement(element, ns, attributes);
+ }
+ ++currentDepth_;
+}
+
+void XMPPParser::handleEndElement(const String& element, const String& ns) {
+ assert(inStream());
+ if (inElement()) {
+ assert(currentElementParser_);
+ currentElementParser_->handleEndElement(element, ns);
+ --currentDepth_;
+ if (!inElement()) {
+ client_->handleElement(currentElementParser_->getElement());
+ delete currentElementParser_;
+ currentElementParser_ = 0;
+ }
+ }
+ else {
+ assert(element == "stream");
+ --currentDepth_;
+ client_->handleStreamEnd();
+ }
+}
+
+void XMPPParser::handleCharacterData(const String& data) {
+ if (currentElementParser_) {
+ currentElementParser_->handleCharacterData(data);
+ }
+ //else {
+ // std::cerr << "XMPPParser: Ignoring stray character data: " << data << std::endl;
+ //}
+}
+
+ElementParser* XMPPParser::createElementParser(const String& element, const String& ns) {
+ if (element == "presence") {
+ return new PresenceParser(payloadParserFactories_);
+ }
+ else if (element == "iq") {
+ return new IQParser(payloadParserFactories_);
+ }
+ else if (element == "message") {
+ return new MessageParser(payloadParserFactories_);
+ }
+ else if (element == "features" && ns == "http://etherx.jabber.org/streams") {
+ return new StreamFeaturesParser();
+ }
+ else if (element == "auth") {
+ return new AuthRequestParser();
+ }
+ else if (element == "success") {
+ return new AuthSuccessParser();
+ }
+ else if (element == "failure" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
+ return new AuthFailureParser();
+ }
+ else if (element == "starttls") {
+ return new StartTLSParser();
+ }
+ else if (element == "failure" && ns == "urn:ietf:params:xml:ns:xmpp-tls") {
+ return new StartTLSFailureParser();
+ }
+ else if (element == "compress") {
+ return new CompressParser();
+ }
+ else if (element == "compressed") {
+ return new CompressedParser();
+ }
+ else if (element == "failure" && ns == "http://jabber.org/protocol/compress") {
+ return new CompressFailureParser();
+ }
+ else if (element == "proceed") {
+ return new TLSProceedParser();
+ }
+ return new UnknownElementParser();
+}
+
+}
diff --git a/Swiften/Parser/XMPPParser.h b/Swiften/Parser/XMPPParser.h
new file mode 100644
index 0000000..9e1109d
--- /dev/null
+++ b/Swiften/Parser/XMPPParser.h
@@ -0,0 +1,54 @@
+#ifndef SWIFTEN_XMPPPARSER_H
+#define SWIFTEN_XMPPPARSER_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Parser/XMLParserClient.h"
+#include "Swiften/Parser/AttributeMap.h"
+
+namespace Swift {
+ class XMLParser;
+ class XMPPParserClient;
+ class String;
+ class ElementParser;
+ class PayloadParserFactoryCollection;
+
+ class XMPPParser : public XMLParserClient, boost::noncopyable {
+ public:
+ XMPPParser(
+ XMPPParserClient* parserClient,
+ PayloadParserFactoryCollection* payloadParserFactories);
+ ~XMPPParser();
+
+ bool parse(const String&);
+
+ private:
+ virtual void handleStartElement(
+ const String& element,
+ const String& ns,
+ const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String& ns);
+ virtual void handleCharacterData(const String& data);
+
+ bool inStream() const {
+ return currentDepth_ > 0;
+ }
+
+ bool inElement() const {
+ return currentDepth_ > 1;
+ }
+
+ ElementParser* createElementParser(const String& element, const String& xmlns);
+
+ private:
+ XMLParser* xmlParser_;
+ XMPPParserClient* client_;
+ PayloadParserFactoryCollection* payloadParserFactories_;
+ int currentDepth_;
+ ElementParser* currentElementParser_;
+ bool parseErrorOccurred_;
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/XMPPParserClient.cpp b/Swiften/Parser/XMPPParserClient.cpp
new file mode 100644
index 0000000..66ea917
--- /dev/null
+++ b/Swiften/Parser/XMPPParserClient.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Parser/XMPPParserClient.h"
+
+namespace Swift {
+
+XMPPParserClient::~XMPPParserClient() {
+}
+
+}
diff --git a/Swiften/Parser/XMPPParserClient.h b/Swiften/Parser/XMPPParserClient.h
new file mode 100644
index 0000000..abecc71
--- /dev/null
+++ b/Swiften/Parser/XMPPParserClient.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_XMPPPARSERCLIENT_H
+#define SWIFTEN_XMPPPARSERCLIENT_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class XMPPParserClient
+ {
+ public:
+ virtual ~XMPPParserClient();
+
+ virtual void handleStreamStart() = 0;
+ virtual void handleElement(boost::shared_ptr<Element>) = 0;
+ virtual void handleStreamEnd() = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Presence/Makefile.inc b/Swiften/Presence/Makefile.inc
new file mode 100644
index 0000000..ca8c95e
--- /dev/null
+++ b/Swiften/Presence/Makefile.inc
@@ -0,0 +1,4 @@
+SWIFTEN_SOURCES += \
+ Swiften/Presence/PresenceOracle.cpp
+
+include Swiften/Presence/UnitTest/Makefile.inc
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
new file mode 100644
index 0000000..af98510
--- /dev/null
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -0,0 +1,29 @@
+#include "PresenceOracle.h"
+
+#include <boost/bind.hpp>
+#include "Swiften/Client/StanzaChannel.h"
+namespace Swift {
+
+typedef std::pair<JID, std::map<JID, boost::shared_ptr<Presence> > > JIDMapPair;
+typedef std::pair<JID, boost::shared_ptr<Presence> > JIDPresencePair;
+
+PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) {
+ stanzaChannel->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
+}
+
+void PresenceOracle::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ JID bareJID = JID(presence->getFrom().toBare());
+ std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
+ boost::shared_ptr<Presence> last;
+ foreach(JIDPresencePair pair, jidMap) {
+ if (pair.first == presence->getFrom()) {
+ last = pair.second;
+ break;
+ }
+ }
+ jidMap[presence->getFrom()] = presence;
+ entries_[bareJID] = jidMap;
+ onPresenceChange(presence, last);
+}
+
+}
diff --git a/Swiften/Presence/PresenceOracle.h b/Swiften/Presence/PresenceOracle.h
new file mode 100644
index 0000000..81f289b
--- /dev/null
+++ b/Swiften/Presence/PresenceOracle.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Presence.h"
+
+#include <map>
+#include <boost/signal.hpp>
+
+namespace Swift {
+class StanzaChannel;
+
+class PresenceOracle {
+ public:
+ PresenceOracle(StanzaChannel* stanzaChannel);
+ ~PresenceOracle() {};
+
+ boost::signal<void (boost::shared_ptr<Presence>, boost::shared_ptr<Presence>)> onPresenceChange;
+
+ private:
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+ std::map<JID, std::map<JID, boost::shared_ptr<Presence> > > entries_;
+ StanzaChannel* stanzaChannel_;
+};
+}
+
diff --git a/Swiften/Presence/UnitTest/Makefile.inc b/Swiften/Presence/UnitTest/Makefile.inc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swiften/Presence/UnitTest/Makefile.inc
diff --git a/Swiften/QA/ClientTest/ClientTest.cpp b/Swiften/QA/ClientTest/ClientTest.cpp
new file mode 100644
index 0000000..20a03a4
--- /dev/null
+++ b/Swiften/QA/ClientTest/ClientTest.cpp
@@ -0,0 +1,63 @@
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/Network/Timer.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+
+using namespace Swift;
+
+SimpleEventLoop eventLoop;
+
+Client* client = 0;
+bool rosterReceived = false;
+
+void printIncomingData(const String& data) {
+ std::cout << "<- " << data << std::endl;
+}
+
+void printOutgoingData(const String& data) {
+ std::cout << "-> " << data << std::endl;
+}
+
+void handleRosterReceived(boost::shared_ptr<Payload>) {
+ rosterReceived = true;
+ eventLoop.stop();
+}
+
+void handleConnected() {
+ GetRosterRequest* rosterRequest = new GetRosterRequest(client, Request::AutoDeleteAfterResponse);
+ rosterRequest->onResponse.connect(boost::bind(&handleRosterReceived, _1));
+ rosterRequest->send();
+}
+
+int main(int, char**) {
+ char* jid = getenv("SWIFT_CLIENTTEST_JID");
+ if (!jid) {
+ std::cerr << "Please set the SWIFT_CLIENTTEST_JID environment variable" << std::endl;
+ return -1;
+ }
+ char* pass = getenv("SWIFT_CLIENTTEST_PASS");
+ if (!pass) {
+ std::cerr << "Please set the SWIFT_CLIENTTEST_PASS environment variable" << std::endl;
+ return -1;
+ }
+
+ client = new Swift::Client(JID(jid), String(pass));
+ client->onConnected.connect(&handleConnected);
+ client->onDataRead.connect(&printIncomingData);
+ client->onDataWritten.connect(&printOutgoingData);
+ client->connect();
+
+ {
+ Timer timer(10000);
+ timer.onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
+ timer.start();
+
+ eventLoop.run();
+ }
+ delete client;
+ return !rosterReceived;
+}
diff --git a/Swiften/QA/ClientTest/Makefile.inc b/Swiften/QA/ClientTest/Makefile.inc
new file mode 100644
index 0000000..d01ccf8
--- /dev/null
+++ b/Swiften/QA/ClientTest/Makefile.inc
@@ -0,0 +1,16 @@
+CLIENTTEST_TARGET = Swiften/QA/ClientTest/ClientTest
+CLIENTTEST_SOURCES += \
+ Swiften/QA/ClientTest/ClientTest.cpp
+CLIENTTEST_OBJECTS = \
+ $(CLIENTTEST_SOURCES:.cpp=.o)
+
+TEST_TARGETS += ClientTest
+
+CLEANFILES += $(CLIENTTEST_OBJECTS) $(CLIENTTEST_TARGET)
+
+$(CLIENTTEST_TARGET): $(SWIFTEN_TARGET) $(CLIENTTEST_OBJECTS)
+ $(QUIET_LINK)$(CXX) -o $(CLIENTTEST_TARGET) $(CLIENTTEST_OBJECTS) $(LDFLAGS) $(CPPCLIENT_LDFLAGS) $(SWIFTEN_TARGET) $(LIBS)
+
+.PHONY: ClientTest
+ClientTest: $(CLIENTTEST_TARGET)
+ $(TEST_RUNNER) $(CLIENTTEST_TARGET)
diff --git a/Swiften/QA/Makefile.inc b/Swiften/QA/Makefile.inc
new file mode 100644
index 0000000..dc3a0bf
--- /dev/null
+++ b/Swiften/QA/Makefile.inc
@@ -0,0 +1,11 @@
+ifdef USE_VALGRIND
+# Not enabled: --show-reachable=yes
+TEST_RUNNER=valgrind --suppressions=Swiften/QA/valgrind.supp -q --leak-check=full --track-origins=yes
+endif
+
+include Swiften/QA/UnitTest/Makefile.inc
+include Swiften/QA/NetworkTest/Makefile.inc
+include Swiften/QA/ClientTest/Makefile.inc
+
+.PHONY: test
+test: $(TEST_TARGETS)
diff --git a/Swiften/QA/NetworkTest/BoostConnectionTest.cpp b/Swiften/QA/NetworkTest/BoostConnectionTest.cpp
new file mode 100644
index 0000000..639097a
--- /dev/null
+++ b/Swiften/QA/NetworkTest/BoostConnectionTest.cpp
@@ -0,0 +1,51 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/sleep.h"
+#include "Swiften/Network/BoostConnection.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+using namespace Swift;
+
+class BoostConnectionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(BoostConnectionTest);
+ CPPUNIT_TEST(testDestructor);
+ CPPUNIT_TEST(testDestructor_PendingEvents);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ BoostConnectionTest() {}
+
+ void setUp() {
+ eventLoop_ = new DummyEventLoop();
+ }
+
+ void tearDown() {
+ delete eventLoop_;
+ }
+
+ void testDestructor() {
+ {
+ std::string domain("el-tramo.be");
+ std::auto_ptr<BoostConnection> testling(new BoostConnection(domain));
+ testling->connect();
+ }
+ }
+
+ void testDestructor_PendingEvents() {
+ {
+ std::auto_ptr<BoostConnection> testling(new BoostConnection("el-tramo.be"));
+ testling->connect();
+ while (!eventLoop_->hasEvents()) {
+ Swift::sleep(10);
+ }
+ }
+ eventLoop_->processEvents();
+ }
+
+ private:
+ DummyEventLoop* eventLoop_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BoostConnectionTest);
diff --git a/Swiften/QA/NetworkTest/DomainNameResolverTest.cpp b/Swiften/QA/NetworkTest/DomainNameResolverTest.cpp
new file mode 100644
index 0000000..8968efd
--- /dev/null
+++ b/Swiften/QA/NetworkTest/DomainNameResolverTest.cpp
@@ -0,0 +1,64 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/DomainNameResolver.h"
+#include "Swiften/Network/DomainNameResolveException.h"
+
+using namespace Swift;
+
+class DomainNameResolverTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(DomainNameResolverTest);
+ CPPUNIT_TEST(testResolve_NoSRV);
+ CPPUNIT_TEST(testResolve_SRV);
+ CPPUNIT_TEST(testResolve_Invalid);
+ //CPPUNIT_TEST(testResolve_IPv6);
+ CPPUNIT_TEST(testResolve_International);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ DomainNameResolverTest() {}
+
+ void setUp() {
+ resolver_ = new DomainNameResolver();
+ }
+
+ void tearDown() {
+ delete resolver_;
+ }
+
+ void testResolve_NoSRV() {
+ HostAddressPort result = resolver_->resolve("xmpp.test.swift.im");
+
+ CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.0"), result.getAddress().toString());
+ CPPUNIT_ASSERT_EQUAL(5222, result.getPort());
+ }
+
+ void testResolve_SRV() {
+ HostAddressPort result = resolver_->resolve("xmpp-srv.test.swift.im");
+
+ CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.1"), result.getAddress().toString());
+ CPPUNIT_ASSERT_EQUAL(5000, result.getPort());
+ }
+
+ void testResolve_Invalid() {
+ CPPUNIT_ASSERT_THROW(resolver_->resolve("invalid.test.swift.im"), DomainNameResolveException);
+ }
+
+ void testResolve_IPv6() {
+ HostAddressPort result = resolver_->resolve("xmpp-ipv6.test.swift.im");
+ CPPUNIT_ASSERT_EQUAL(std::string("0000:0000:0000:0000:0000:ffff:0a00:0104"), result.getAddress().toString());
+ CPPUNIT_ASSERT_EQUAL(5222, result.getPort());
+ }
+
+ void testResolve_International() {
+ HostAddressPort result = resolver_->resolve("tron\xc3\xa7on.test.swift.im");
+ CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.3"), result.getAddress().toString());
+ CPPUNIT_ASSERT_EQUAL(5222, result.getPort());
+ }
+
+ private:
+ DomainNameResolver* resolver_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DomainNameResolverTest);
diff --git a/Swiften/QA/NetworkTest/Makefile.inc b/Swiften/QA/NetworkTest/Makefile.inc
new file mode 100644
index 0000000..6924a70
--- /dev/null
+++ b/Swiften/QA/NetworkTest/Makefile.inc
@@ -0,0 +1,18 @@
+NETWORKTEST_TARGET += Swiften/QA/NetworkTest/checker
+NETWORKTEST_SOURCES += \
+ Swiften/QA/NetworkTest/DomainNameResolverTest.cpp \
+ Swiften/QA/NetworkTest/BoostConnectionTest.cpp \
+ Swiften/QA/UnitTest/checker.cpp
+NETWORKTEST_OBJECTS = \
+ $(NETWORKTEST_SOURCES:.cpp=.o)
+
+TEST_TARGETS += NetworkTest
+
+CLEANFILES += $(NETWORKTEST_OBJECTS) $(NETWORKTEST_TARGET)
+
+$(NETWORKTEST_TARGET): $(SWIFTEN_TARGET) $(CPPUNIT_TARGET) $(NETWORKTEST_OBJECTS)
+ $(QUIET_LINK)$(CXX) -o $(NETWORKTEST_TARGET) $(NETWORKTEST_OBJECTS) $(LDFLAGS) $(CPPNETWORK_LDFLAGS) $(SWIFTEN_TARGET) $(CPPUNIT_TARGET) $(LIBS)
+
+.PHONY: NetworkTest
+NetworkTest: $(NETWORKTEST_TARGET)
+ $(TEST_RUNNER) $(NETWORKTEST_TARGET)
diff --git a/Swiften/QA/UnitTest/Makefile.inc b/Swiften/QA/UnitTest/Makefile.inc
new file mode 100644
index 0000000..5b456db
--- /dev/null
+++ b/Swiften/QA/UnitTest/Makefile.inc
@@ -0,0 +1,17 @@
+UNITTEST_TARGET = Swiften/QA/UnitTest/checker
+UNITTEST_SOURCES += \
+ Swiften/QA/UnitTest/checker.cpp
+UNITTEST_OBJECTS = \
+ $(UNITTEST_SOURCES:.cpp=.o)
+
+TEST_TARGETS += check
+
+CLEANFILES += $(UNITTEST_OBJECTS) $(UNITTEST_TARGET)
+
+.PHONY: check
+check: $(UNITTEST_TARGET)
+ $(TEST_RUNNER) ./$(UNITTEST_TARGET)
+
+$(UNITTEST_TARGET): $(SWIFTEN_TARGET) $(CPPUNIT_TARGET) $(UNITTEST_OBJECTS)
+ $(QUIET_LINK)$(CXX) -o $(UNITTEST_TARGET) $(UNITTEST_OBJECTS) $(LDFLAGS) $(SWIFTEN_TARGET) $(CPPUNIT_TARGET) $(LIBS)
+
diff --git a/Swiften/QA/UnitTest/checker.cpp b/Swiften/QA/UnitTest/checker.cpp
new file mode 100644
index 0000000..ea4f0d9
--- /dev/null
+++ b/Swiften/QA/UnitTest/checker.cpp
@@ -0,0 +1,16 @@
+#include <string>
+#include <cppunit/ui/text/TestRunner.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/XmlOutputter.h>
+#include <cppunit/TextTestResult.h>
+
+int main(int argc, char* argv[])
+{
+ CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
+ CppUnit::TextUi::TestRunner runner;
+ runner.addTest( registry.makeTest() );
+ if (argc >= 2 && std::string(argv[1]) != std::string("--xml")) {
+ runner.setOutputter(new CppUnit::XmlOutputter(&runner.result(), std::cout));
+ }
+ return (runner.run("") ? 0 : 1);
+}
diff --git a/Swiften/QA/UnitTest/template/FooTest.cpp b/Swiften/QA/UnitTest/template/FooTest.cpp
new file mode 100644
index 0000000..b6b9abf
--- /dev/null
+++ b/Swiften/QA/UnitTest/template/FooTest.cpp
@@ -0,0 +1,24 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+using namespace Swift;
+
+class FooTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(FooTest);
+ CPPUNIT_TEST(testBar);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ FooTest() {}
+
+ void setUp() {
+ }
+
+ void tearDown() {
+ }
+
+ void testBar() {
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FooTest);
diff --git a/Swiften/QA/valgrind.supp b/Swiften/QA/valgrind.supp
new file mode 100644
index 0000000..5e2ee00
--- /dev/null
+++ b/Swiften/QA/valgrind.supp
@@ -0,0 +1,51 @@
+{
+ ZLib doesn't allocate its buffer. This is no bug according to the FAQ.
+ Memcheck:Cond
+ fun:longest_match
+ fun:deflate_slow
+ fun:deflate
+}
+
+{
+ Not sure why this happens.
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.1
+ fun:_ZN5boost6thread12start_threadEv
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Param
+ socketcall.sendto(msg)
+ fun:sendto
+ fun:getaddrinfo
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_bin2bn
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_num_bits_word
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Value4
+ fun:BN_mod_exp_mont_consttime
+ fun:BN_mod_exp_mont
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Value4
+ fun:BN_num_bits_word
+ fun:BN_mod_exp_mont_consttime
+ fun:BN_mod_exp_mont
+}
diff --git a/Swiften/Queries/DummyIQChannel.h b/Swiften/Queries/DummyIQChannel.h
new file mode 100644
index 0000000..f72d7a5
--- /dev/null
+++ b/Swiften/Queries/DummyIQChannel.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_DummyIQChannel_H
+#define SWIFTEN_DummyIQChannel_H
+
+#include <vector>
+
+#include "Swiften/Queries/IQChannel.h"
+
+namespace Swift {
+ class DummyIQChannel : public IQChannel {
+ public:
+ DummyIQChannel() {}
+
+ virtual void sendIQ(boost::shared_ptr<IQ> iq) {
+ iqs_.push_back(iq);
+ }
+
+ virtual String getNewIQID() {
+ return "test-id";
+ }
+
+ std::vector<boost::shared_ptr<IQ> > iqs_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/GenericRequest.h b/Swiften/Queries/GenericRequest.h
new file mode 100644
index 0000000..c81760f
--- /dev/null
+++ b/Swiften/Queries/GenericRequest.h
@@ -0,0 +1,30 @@
+#ifndef SWIFTEN_GenericRequest_H
+#define SWIFTEN_GenericRequest_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Queries/Request.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class GenericRequest : public Request {
+ public:
+ GenericRequest(
+ IQ::Type type,
+ const JID& receiver,
+ boost::shared_ptr<Payload> payload,
+ IQRouter* router,
+ AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) :
+ Request(type, receiver, payload, router, autoDeleteBehavior) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<Error> error) {
+ onResponse(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(payload), error);
+ }
+
+ public:
+ boost::signal<void (boost::shared_ptr<PAYLOAD_TYPE>, const boost::optional<Error>&)> onResponse;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/IQChannel.cpp b/Swiften/Queries/IQChannel.cpp
new file mode 100644
index 0000000..539dea0
--- /dev/null
+++ b/Swiften/Queries/IQChannel.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Queries/IQChannel.h"
+
+namespace Swift {
+
+IQChannel::~IQChannel() {
+}
+
+}
diff --git a/Swiften/Queries/IQChannel.h b/Swiften/Queries/IQChannel.h
new file mode 100644
index 0000000..0dbb1be
--- /dev/null
+++ b/Swiften/Queries/IQChannel.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_IQChannel_H
+#define SWIFTEN_IQChannel_H
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+ class IQChannel {
+ public:
+ virtual ~IQChannel();
+
+ virtual void sendIQ(boost::shared_ptr<IQ>) = 0;
+ virtual String getNewIQID() = 0;
+
+ boost::signal<void (boost::shared_ptr<IQ>)> onIQReceived;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/IQHandler.cpp b/Swiften/Queries/IQHandler.cpp
new file mode 100644
index 0000000..b5e7e49
--- /dev/null
+++ b/Swiften/Queries/IQHandler.cpp
@@ -0,0 +1,14 @@
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+IQHandler::IQHandler(IQRouter* router) : router_(router) {
+ router_->addHandler(this);
+}
+
+IQHandler::~IQHandler() {
+ router_->removeHandler(this);
+}
+
+}
diff --git a/Swiften/Queries/IQHandler.h b/Swiften/Queries/IQHandler.h
new file mode 100644
index 0000000..7a8d008
--- /dev/null
+++ b/Swiften/Queries/IQHandler.h
@@ -0,0 +1,28 @@
+#ifndef SWIFTEN_IQHandler_H
+#define SWIFTEN_IQHandler_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class IQHandler {
+ public:
+ IQHandler(IQRouter* router);
+ virtual ~IQHandler();
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) = 0;
+
+ protected:
+ IQRouter* getRouter() const {
+ return router_;
+ }
+
+ private:
+ IQRouter* router_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/IQRouter.cpp b/Swiften/Queries/IQRouter.cpp
new file mode 100644
index 0000000..b474640
--- /dev/null
+++ b/Swiften/Queries/IQRouter.cpp
@@ -0,0 +1,46 @@
+#include "Swiften/Queries/IQRouter.h"
+
+#include <algorithm>
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQChannel.h"
+#include "Swiften/Elements/Error.h"
+
+namespace Swift {
+
+IQRouter::IQRouter(IQChannel* channel) : channel_(channel) {
+ channel->onIQReceived.connect(boost::bind(&IQRouter::handleIQ, this, _1));
+}
+
+void IQRouter::handleIQ(boost::shared_ptr<IQ> iq) {
+ bool handled = false;
+ foreach(IQHandler* handler, handlers_) {
+ handled |= handler->handleIQ(iq);
+ if (handled) {
+ break;
+ }
+ }
+ if (!handled && (iq->getType() == IQ::Get || iq->getType() == IQ::Set) ) {
+ channel_->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), Error::FeatureNotImplemented, Error::Cancel));
+ }
+}
+
+void IQRouter::addHandler(IQHandler* handler) {
+ handlers_.push_back(handler);
+}
+
+void IQRouter::removeHandler(IQHandler* handler) {
+ handlers_.erase(std::remove(handlers_.begin(), handlers_.end(), handler), handlers_.end());
+}
+
+void IQRouter::sendIQ(boost::shared_ptr<IQ> iq) {
+ channel_->sendIQ(iq);
+}
+
+String IQRouter::getNewIQID() {
+ return channel_->getNewIQID();
+}
+
+}
diff --git a/Swiften/Queries/IQRouter.h b/Swiften/Queries/IQRouter.h
new file mode 100644
index 0000000..2240dfb
--- /dev/null
+++ b/Swiften/Queries/IQRouter.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_IQRouter_H
+#define SWIFTEN_IQRouter_H
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+ class IQChannel;
+ class IQHandler;
+
+ class IQRouter {
+ public:
+ IQRouter(IQChannel* channel);
+
+ void addHandler(IQHandler* handler);
+ void removeHandler(IQHandler* handler);
+
+ void sendIQ(boost::shared_ptr<IQ> iq);
+ String getNewIQID();
+
+ private:
+ void handleIQ(boost::shared_ptr<IQ> iq);
+
+ private:
+ IQChannel* channel_;
+ std::vector<IQHandler*> handlers_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Makefile.inc b/Swiften/Queries/Makefile.inc
new file mode 100644
index 0000000..53a712d
--- /dev/null
+++ b/Swiften/Queries/Makefile.inc
@@ -0,0 +1,8 @@
+SWIFTEN_SOURCES += \
+ Swiften/Queries/IQRouter.cpp \
+ Swiften/Queries/IQHandler.cpp \
+ Swiften/Queries/IQChannel.cpp \
+ Swiften/Queries/Request.cpp
+
+include Swiften/Queries/Responders/Makefile.inc
+include Swiften/Queries/UnitTest/Makefile.inc
diff --git a/Swiften/Queries/Request.cpp b/Swiften/Queries/Request.cpp
new file mode 100644
index 0000000..ac361cc
--- /dev/null
+++ b/Swiften/Queries/Request.cpp
@@ -0,0 +1,39 @@
+#include "Swiften/Queries/Request.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+Request::Request(IQ::Type type, const JID& receiver, boost::shared_ptr<Payload> payload, IQRouter* router, AutoDeleteBehavior autoDeleteBehavior) : IQHandler(router), type_(type), receiver_(receiver), payload_(payload), autoDeleteBehavior_(autoDeleteBehavior) {
+ id_ = getRouter()->getNewIQID();
+}
+
+void Request::send() {
+ boost::shared_ptr<IQ> iq(new IQ(type_));
+ iq->setTo(receiver_);
+ iq->addPayload(payload_);
+ iq->setID(id_);
+ getRouter()->sendIQ(iq);
+}
+
+bool Request::handleIQ(boost::shared_ptr<IQ> iq) {
+ if (iq->getID() == id_) {
+ if (iq->getType() == IQ::Result) {
+ handleResponse(iq->getPayloadOfSameType(payload_), boost::optional<Error>());
+ if (autoDeleteBehavior_ == AutoDeleteAfterResponse) {
+ MainEventLoop::deleteLater(this);
+ }
+ return true;
+ }
+ else {
+ // FIXME: Get proper error
+ handleResponse(boost::shared_ptr<Payload>(), boost::optional<Error>(Error::UndefinedCondition));
+ return true;
+ }
+ }
+ else {
+ return false;
+ }
+}
+
+}
diff --git a/Swiften/Queries/Request.h b/Swiften/Queries/Request.h
new file mode 100644
index 0000000..f084303
--- /dev/null
+++ b/Swiften/Queries/Request.h
@@ -0,0 +1,46 @@
+#ifndef SWIFTEN_Request_H
+#define SWIFTEN_Request_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/Error.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class Request : public IQHandler {
+ public:
+ enum AutoDeleteBehavior {
+ DoNotAutoDelete,
+ AutoDeleteAfterResponse
+ };
+
+ Request(
+ IQ::Type type,
+ const JID& receiver,
+ boost::shared_ptr<Payload> payload,
+ IQRouter* router,
+ AutoDeleteBehavior = DoNotAutoDelete);
+
+ void send();
+
+ protected:
+ virtual void handleResponse(boost::shared_ptr<Payload>, boost::optional<Error>) = 0;
+
+ private:
+ bool handleIQ(boost::shared_ptr<IQ>);
+
+ private:
+ IQ::Type type_;
+ JID receiver_;
+ boost::shared_ptr<Payload> payload_;
+ AutoDeleteBehavior autoDeleteBehavior_;
+ String id_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Requests/GetDiscoInfoRequest.h b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
new file mode 100644
index 0000000..0e8508e
--- /dev/null
+++ b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_GetDiscoInfoRequest_H
+#define SWIFTEN_GetDiscoInfoRequest_H
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class GetDiscoInfoRequest : public GenericRequest<DiscoInfo> {
+ public:
+ GetDiscoInfoRequest(const JID& jid, IQRouter* router, AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) :
+ GenericRequest<DiscoInfo>(IQ::Get, jid, boost::shared_ptr<DiscoInfo>(new DiscoInfo()), router, autoDeleteBehavior) {
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Requests/GetRosterRequest.h b/Swiften/Queries/Requests/GetRosterRequest.h
new file mode 100644
index 0000000..2364d81
--- /dev/null
+++ b/Swiften/Queries/Requests/GetRosterRequest.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_GetRosterRequest_H
+#define SWIFTEN_GetRosterRequest_H
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/RosterPayload.h"
+
+namespace Swift {
+ class GetRosterRequest : public GenericRequest<RosterPayload> {
+ public:
+ GetRosterRequest(IQRouter* router, AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) :
+ GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router, autoDeleteBehavior) {
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
new file mode 100644
index 0000000..53ca3eb
--- /dev/null
+++ b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_GetSecurityLabelsCatalogRequest_H
+#define SWIFTEN_GetSecurityLabelsCatalogRequest_H
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/SecurityLabelsCatalog.h"
+
+namespace Swift {
+ class GetSecurityLabelsCatalogRequest : public GenericRequest<SecurityLabelsCatalog> {
+ public:
+ GetSecurityLabelsCatalogRequest(
+ const JID& recipient,
+ IQRouter* router,
+ AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) :
+ GenericRequest<SecurityLabelsCatalog>(
+ IQ::Get, JID(), boost::shared_ptr<SecurityLabelsCatalog>(new SecurityLabelsCatalog(recipient)), router, autoDeleteBehavior) {
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responder.h b/Swiften/Queries/Responder.h
new file mode 100644
index 0000000..b6029ae
--- /dev/null
+++ b/Swiften/Queries/Responder.h
@@ -0,0 +1,49 @@
+#ifndef SWIFTEN_Responder_H
+#define SWIFTEN_Responder_H
+
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Elements/Error.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class Responder : public IQHandler {
+ public:
+ Responder(IQRouter* router) : IQHandler(router) {
+ }
+
+ protected:
+ virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<PAYLOAD_TYPE> payload) = 0;
+ virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<PAYLOAD_TYPE> payload) = 0;
+
+ void sendResponse(const JID& to, const String& id, boost::shared_ptr<Payload> payload) {
+ getRouter()->sendIQ(IQ::createResult(to, id, payload));
+ }
+
+ void sendError(const JID& to, const String& id, Error::Condition condition, Error::Type type) {
+ getRouter()->sendIQ(IQ::createError(to, id, condition, type));
+ }
+
+ private:
+ virtual bool handleIQ(boost::shared_ptr<IQ> iq) {
+ if (iq->getType() == IQ::Set || iq->getType() == IQ::Get) {
+ boost::shared_ptr<PAYLOAD_TYPE> payload(iq->getPayload<PAYLOAD_TYPE>());
+ if (payload) {
+ bool result;
+ if (iq->getType() == IQ::Set) {
+ result = handleSetRequest(iq->getFrom(), iq->getID(), payload);
+ }
+ else {
+ result = handleGetRequest(iq->getFrom(), iq->getID(), payload);
+ }
+ if (!result) {
+ getRouter()->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), Error::NotAllowed, Error::Cancel));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.cpp b/Swiften/Queries/Responders/DiscoInfoResponder.cpp
new file mode 100644
index 0000000..e207133
--- /dev/null
+++ b/Swiften/Queries/Responders/DiscoInfoResponder.cpp
@@ -0,0 +1,40 @@
+#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+
+DiscoInfoResponder::DiscoInfoResponder(IQRouter* router) : Responder<DiscoInfo>(router) {
+}
+
+void DiscoInfoResponder::setDiscoInfo(const DiscoInfo& info) {
+ info_ = info;
+}
+
+void DiscoInfoResponder::setDiscoInfo(const String& node, const DiscoInfo& info) {
+ DiscoInfo newInfo(info);
+ newInfo.setNode(node);
+ nodeInfo_[node] = newInfo;
+}
+
+bool DiscoInfoResponder::handleGetRequest(const JID& from, const String& id, boost::shared_ptr<DiscoInfo> info) {
+ if (info->getNode().isEmpty()) {
+ sendResponse(from, id, boost::shared_ptr<DiscoInfo>(new DiscoInfo(info_)));
+ }
+ else {
+ std::map<String,DiscoInfo>::const_iterator i = nodeInfo_.find(info->getNode());
+ if (i != nodeInfo_.end()) {
+ sendResponse(from, id, boost::shared_ptr<DiscoInfo>(new DiscoInfo((*i).second)));
+ }
+ else {
+ sendError(from, id, Error::ItemNotFound, Error::Cancel);
+ }
+ }
+ return true;
+}
+
+bool DiscoInfoResponder::handleSetRequest(const JID&, const String&, boost::shared_ptr<DiscoInfo>) {
+ return false;
+}
+
+}
diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.h b/Swiften/Queries/Responders/DiscoInfoResponder.h
new file mode 100644
index 0000000..aa79163
--- /dev/null
+++ b/Swiften/Queries/Responders/DiscoInfoResponder.h
@@ -0,0 +1,29 @@
+#ifndef SWIFTEN_DiscoInfoResponder_H
+#define SWIFTEN_DiscoInfoResponder_H
+
+#include <map>
+
+#include "Swiften/Queries/Responder.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class DiscoInfoResponder : public Responder<DiscoInfo> {
+ public:
+ DiscoInfoResponder(IQRouter* router);
+
+ void setDiscoInfo(const DiscoInfo& info);
+ void setDiscoInfo(const String& node, const DiscoInfo& info);
+
+ private:
+ virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<DiscoInfo> payload);
+ virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<DiscoInfo> payload);
+
+ private:
+ DiscoInfo info_;
+ std::map<String, DiscoInfo> nodeInfo_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/Makefile.inc b/Swiften/Queries/Responders/Makefile.inc
new file mode 100644
index 0000000..5049440
--- /dev/null
+++ b/Swiften/Queries/Responders/Makefile.inc
@@ -0,0 +1,5 @@
+SWIFTEN_SOURCES += \
+ Swiften/Queries/Responders/SoftwareVersionResponder.cpp \
+ Swiften/Queries/Responders/DiscoInfoResponder.cpp
+
+include Swiften/Queries/Responders/UnitTest/Makefile.inc
diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.cpp b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp
new file mode 100644
index 0000000..dad2442
--- /dev/null
+++ b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp
@@ -0,0 +1,20 @@
+#include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+SoftwareVersionResponder::SoftwareVersionResponder(
+ const String& client, const String& version, IQRouter* router) :
+ Responder<SoftwareVersion>(router), client_(client), version_(version) {
+}
+
+bool SoftwareVersionResponder::handleGetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion>) {
+ sendResponse(from, id, boost::shared_ptr<SoftwareVersion>(new SoftwareVersion(client_, version_)));
+ return true;
+}
+
+bool SoftwareVersionResponder::handleSetRequest(const JID&, const String&, boost::shared_ptr<SoftwareVersion>) {
+ return false;
+}
+
+}
diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.h b/Swiften/Queries/Responders/SoftwareVersionResponder.h
new file mode 100644
index 0000000..d66e168
--- /dev/null
+++ b/Swiften/Queries/Responders/SoftwareVersionResponder.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_SoftwareVersionResponder_H
+#define SWIFTEN_SoftwareVersionResponder_H
+
+#include "Swiften/Queries/Responder.h"
+#include "Swiften/Elements/SoftwareVersion.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class SoftwareVersionResponder : public Responder<SoftwareVersion> {
+ public:
+ SoftwareVersionResponder(const String& client, const String& version, IQRouter* router);
+
+ private:
+ virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload);
+ virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload);
+
+ private:
+ String client_;
+ String version_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp b/Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp
new file mode 100644
index 0000000..44db138
--- /dev/null
+++ b/Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp
@@ -0,0 +1,84 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <typeinfo>
+
+#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+
+using namespace Swift;
+
+class DiscoInfoResponderTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(DiscoInfoResponderTest);
+ CPPUNIT_TEST(testHandleRequest_GetToplevelInfo);
+ CPPUNIT_TEST(testHandleRequest_GetNodeInfo);
+ CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ DiscoInfoResponderTest() {}
+
+ void setUp() {
+ channel_ = new DummyIQChannel();
+ router_ = new IQRouter(channel_);
+ }
+
+ void tearDown() {
+ delete router_;
+ delete channel_;
+ }
+
+ void testHandleRequest_GetToplevelInfo() {
+ DiscoInfoResponder testling(router_);
+ DiscoInfo discoInfo;
+ discoInfo.addFeature("foo");
+ testling.setDiscoInfo(discoInfo);
+
+ boost::shared_ptr<DiscoInfo> query(new DiscoInfo());
+ channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>());
+ CPPUNIT_ASSERT(payload);
+ CPPUNIT_ASSERT_EQUAL(String(""), payload->getNode());
+ CPPUNIT_ASSERT(payload->hasFeature("foo"));
+ }
+
+ void testHandleRequest_GetNodeInfo() {
+ DiscoInfoResponder testling(router_);
+ DiscoInfo discoInfo;
+ discoInfo.addFeature("foo");
+ testling.setDiscoInfo(discoInfo);
+ DiscoInfo discoInfoBar;
+ discoInfoBar.addFeature("bar");
+ testling.setDiscoInfo("bar-node", discoInfoBar);
+
+ boost::shared_ptr<DiscoInfo> query(new DiscoInfo());
+ query->setNode("bar-node");
+ channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>());
+ CPPUNIT_ASSERT(payload);
+ CPPUNIT_ASSERT_EQUAL(String("bar-node"), payload->getNode());
+ CPPUNIT_ASSERT(payload->hasFeature("bar"));
+ }
+
+ void testHandleRequest_GetInvalidNodeInfo() {
+ DiscoInfoResponder testling(router_);
+
+ boost::shared_ptr<DiscoInfo> query(new DiscoInfo());
+ query->setNode("bar-node");
+ channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ boost::shared_ptr<Error> payload(channel_->iqs_[0]->getPayload<Error>());
+ CPPUNIT_ASSERT(payload);
+ }
+
+ private:
+ IQRouter* router_;
+ DummyIQChannel* channel_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoResponderTest);
diff --git a/Swiften/Queries/Responders/UnitTest/Makefile.inc b/Swiften/Queries/Responders/UnitTest/Makefile.inc
new file mode 100644
index 0000000..8f06682
--- /dev/null
+++ b/Swiften/Queries/Responders/UnitTest/Makefile.inc
@@ -0,0 +1,2 @@
+UNITTEST_SOURCES += \
+ Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp
diff --git a/Swiften/Queries/UnitTest/Makefile.inc b/Swiften/Queries/UnitTest/Makefile.inc
new file mode 100644
index 0000000..265357f
--- /dev/null
+++ b/Swiften/Queries/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/Queries/UnitTest/RequestTest.cpp \
+ Swiften/Queries/UnitTest/ResponderTest.cpp
diff --git a/Swiften/Queries/UnitTest/RequestTest.cpp b/Swiften/Queries/UnitTest/RequestTest.cpp
new file mode 100644
index 0000000..31c0603
--- /dev/null
+++ b/Swiften/Queries/UnitTest/RequestTest.cpp
@@ -0,0 +1,139 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+#include "Swiften/Elements/Payload.h"
+
+using namespace Swift;
+
+class RequestTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RequestTest);
+ CPPUNIT_TEST(testSendGet);
+ CPPUNIT_TEST(testSendSet);
+ CPPUNIT_TEST(testHandleIQ);
+ CPPUNIT_TEST(testHandleIQ_InvalidID);
+ CPPUNIT_TEST(testHandleIQ_Error);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ class MyPayload : public Payload {
+ public:
+ MyPayload(const String& s = "") : text_(s) {}
+ String text_;
+ };
+
+ typedef GenericRequest<MyPayload> MyRequest;
+
+ public:
+ RequestTest() {}
+
+ void setUp() {
+ channel_ = new DummyIQChannel();
+ router_ = new IQRouter(channel_);
+ payload_ = boost::shared_ptr<Payload>(new MyPayload("foo"));
+ responsePayload_ = boost::shared_ptr<Payload>(new MyPayload("bar"));
+ responsesReceived_ = 0;
+ errorsReceived_ = 0;
+ }
+
+ void tearDown() {
+ delete router_;
+ delete channel_;
+ }
+
+ void testSendSet() {
+ MyRequest testling(IQ::Set, JID("foo@bar.com/baz"), payload_, router_);
+ testling.send();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), channel_->iqs_[0]->getTo());
+ CPPUNIT_ASSERT_EQUAL(IQ::Set, channel_->iqs_[0]->getType());
+ CPPUNIT_ASSERT_EQUAL(String("test-id"), channel_->iqs_[0]->getID());
+ }
+
+ void testSendGet() {
+ MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_);
+ testling.send();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(IQ::Get, channel_->iqs_[0]->getType());
+ }
+
+ void testHandleIQ() {
+ MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_);
+ testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2));
+ testling.send();
+
+ channel_->onIQReceived(createResponse("test-id"));
+
+ CPPUNIT_ASSERT_EQUAL(1, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, errorsReceived_);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ // FIXME: Doesn't test that it didn't handle the payload
+ void testHandleIQ_InvalidID() {
+ MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_);
+ testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2));
+ testling.send();
+
+ channel_->onIQReceived(createResponse("different-id"));
+
+ CPPUNIT_ASSERT_EQUAL(0, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, errorsReceived_);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ void testHandleIQ_Error() {
+ MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_);
+ testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2));
+ testling.send();
+
+ channel_->onIQReceived(createError("test-id"));
+
+ CPPUNIT_ASSERT_EQUAL(0, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(1, errorsReceived_);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ private:
+ void handleResponse(boost::shared_ptr<Payload> p, const boost::optional<Error>& e) {
+ if (e) {
+ ++errorsReceived_;
+ }
+ else {
+ boost::shared_ptr<MyPayload> payload(boost::dynamic_pointer_cast<MyPayload>(p));
+ CPPUNIT_ASSERT(payload);
+ CPPUNIT_ASSERT_EQUAL(String("bar"), payload->text_);
+ ++responsesReceived_;
+ }
+ }
+
+ boost::shared_ptr<IQ> createResponse(const String& id) {
+ boost::shared_ptr<IQ> iq(new IQ(IQ::Result));
+ iq->addPayload(responsePayload_);
+ iq->setID(id);
+ return iq;
+ }
+
+ boost::shared_ptr<IQ> createError(const String& id) {
+ boost::shared_ptr<IQ> iq(new IQ(IQ::Error));
+ iq->setID(id);
+ return iq;
+ }
+
+ private:
+ IQRouter* router_;
+ DummyIQChannel* channel_;
+ boost::shared_ptr<Payload> payload_;
+ boost::shared_ptr<Payload> responsePayload_;
+ int responsesReceived_;
+ int errorsReceived_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(RequestTest);
diff --git a/Swiften/Queries/UnitTest/ResponderTest.cpp b/Swiften/Queries/UnitTest/ResponderTest.cpp
new file mode 100644
index 0000000..5c758e4
--- /dev/null
+++ b/Swiften/Queries/UnitTest/ResponderTest.cpp
@@ -0,0 +1,132 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/Responder.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+#include "Swiften/Elements/SoftwareVersion.h"
+
+using namespace Swift;
+
+class ResponderTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ResponderTest);
+ CPPUNIT_TEST(testConstructor);
+ CPPUNIT_TEST(testHandleIQ_Set);
+ CPPUNIT_TEST(testHandleIQ_Get);
+ CPPUNIT_TEST(testHandleIQ_Error);
+ CPPUNIT_TEST(testHandleIQ_Result);
+ CPPUNIT_TEST(testHandleIQ_NoPayload);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ResponderTest() {}
+
+ void setUp() {
+ channel_ = new DummyIQChannel();
+ router_ = new IQRouter(channel_);
+ payload_ = boost::shared_ptr<SoftwareVersion>(new SoftwareVersion("foo"));
+ }
+
+ void tearDown() {
+ delete router_;
+ delete channel_;
+ }
+
+ void testConstructor() {
+ MyResponder testling(router_);
+
+ channel_->onIQReceived(createRequest(IQ::Set));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(testling.setPayloads_.size()));
+ }
+
+ void testHandleIQ_Set() {
+ MyResponder testling(router_);
+
+ CPPUNIT_ASSERT(dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Set)));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(testling.setPayloads_.size()));
+ CPPUNIT_ASSERT(payload_ == testling.setPayloads_[0]);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size()));
+ }
+
+ void testHandleIQ_Get() {
+ MyResponder testling(router_);
+
+ CPPUNIT_ASSERT(dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Get)));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(testling.getPayloads_.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size()));
+ CPPUNIT_ASSERT(payload_ == testling.getPayloads_[0]);
+ }
+
+ void testHandleIQ_Error() {
+ MyResponder testling(router_);
+
+ CPPUNIT_ASSERT(!dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Error)));
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size()));
+ }
+
+ void testHandleIQ_Result() {
+ MyResponder testling(router_);
+
+ CPPUNIT_ASSERT(!dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Result)));
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size()));
+ }
+
+ void testHandleIQ_NoPayload() {
+ MyResponder testling(router_);
+
+ CPPUNIT_ASSERT(!dynamic_cast<IQHandler*>(&testling)->handleIQ(boost::shared_ptr<IQ>(new IQ(IQ::Get))));
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size()));
+ }
+
+ private:
+ boost::shared_ptr<IQ> createRequest(IQ::Type type) {
+ boost::shared_ptr<IQ> iq(new IQ(type));
+ iq->addPayload(payload_);
+ iq->setID("myid");
+ iq->setFrom(JID("foo@bar.com/baz"));
+ return iq;
+ }
+
+ private:
+ class MyResponder : public Responder<SoftwareVersion> {
+ public:
+ MyResponder(IQRouter* router) : Responder<SoftwareVersion>(router), getRequestResponse_(true), setRequestResponse_(true) {}
+
+ virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload) {
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), from);
+ CPPUNIT_ASSERT_EQUAL(String("myid"), id);
+ getPayloads_.push_back(payload);
+ return getRequestResponse_;
+ }
+ virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload) {
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), from);
+ CPPUNIT_ASSERT_EQUAL(String("myid"), id);
+ setPayloads_.push_back(payload);
+ return setRequestResponse_;
+ }
+
+ bool getRequestResponse_;
+ bool setRequestResponse_;
+ std::vector<boost::shared_ptr<SoftwareVersion> > getPayloads_;
+ std::vector<boost::shared_ptr<SoftwareVersion> > setPayloads_;
+ };
+
+ private:
+ IQRouter* router_;
+ DummyIQChannel* channel_;
+ boost::shared_ptr<SoftwareVersion> payload_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ResponderTest);
diff --git a/Swiften/Roster/ContactRosterItem.cpp b/Swiften/Roster/ContactRosterItem.cpp
new file mode 100644
index 0000000..f38b0f7
--- /dev/null
+++ b/Swiften/Roster/ContactRosterItem.cpp
@@ -0,0 +1,51 @@
+#include "Swiften/Roster/ContactRosterItem.h"
+#include "Swiften/Roster/GroupRosterItem.h"
+
+namespace Swift {
+
+
+ContactRosterItem::ContactRosterItem(const JID& jid, const String& name, GroupRosterItem* parent, TreeWidgetFactory* factory) : jid_(jid), name_(name) {
+ parent->addChild(this);
+ widget_ = factory->createTreeWidgetItem(parent->getWidget());
+ widget_->setText(name.isEmpty() ? jid.toString() : name);
+ widget_->onUserAction.connect(boost::bind(&ContactRosterItem::handleUserAction, this, _1));
+ setStatusShow(StatusShow::None);
+}
+
+ContactRosterItem::~ContactRosterItem() {
+ delete widget_;
+}
+
+StatusShow::Type ContactRosterItem::getStatusShow() {
+ return statusShow_;
+}
+
+void ContactRosterItem::setStatusShow(StatusShow::Type show) {
+ statusShow_ = show;
+ int colour = 0;
+ switch (show) {
+ case StatusShow::Online: colour = 0x000000;break;
+ case StatusShow::Away: colour = 0x336699;break;
+ case StatusShow::XA: colour = 0x336699;break;
+ case StatusShow::FFC: colour = 0x000000;break;
+ case StatusShow::DND: colour = 0x990000;break;
+ case StatusShow::None: colour = 0x7F7F7F;break;
+ }
+ widget_->setTextColor(colour);
+}
+
+const JID& ContactRosterItem::getJID() const {
+ return jid_;
+}
+
+void ContactRosterItem::show() {
+ widget_->show();
+}
+
+void ContactRosterItem::hide() {
+ widget_->hide();
+}
+
+}
+
+
diff --git a/Swiften/Roster/ContactRosterItem.h b/Swiften/Roster/ContactRosterItem.h
new file mode 100644
index 0000000..20f9f65
--- /dev/null
+++ b/Swiften/Roster/ContactRosterItem.h
@@ -0,0 +1,39 @@
+#ifndef SWIFTEN_ContactRosterItem_H
+#define SWIFTEN_ContactRosterItem_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Roster/UserRosterAction.h"
+#include "Swiften/Elements/StatusShow.h"
+
+#include <boost/bind.hpp>
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class TreeWidgetItem;
+class GroupRosterItem;
+class ContactRosterItem : public RosterItem {
+ public:
+ ContactRosterItem(const JID& jid, const String& name, GroupRosterItem* parent, TreeWidgetFactory* factory);
+ ~ContactRosterItem();
+
+ StatusShow::Type getStatusShow();
+ void setStatusShow(StatusShow::Type show);
+ const JID& getJID() const;
+ void show();
+ void hide();
+
+ private:
+ JID jid_;
+ String name_;
+ TreeWidgetItem *widget_;
+ StatusShow::Type statusShow_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/GroupRosterItem.h b/Swiften/Roster/GroupRosterItem.h
new file mode 100644
index 0000000..f96a868
--- /dev/null
+++ b/Swiften/Roster/GroupRosterItem.h
@@ -0,0 +1,70 @@
+#ifndef SWIFTEN_GroupRosterItem_H
+#define SWIFTEN_GroupRosterItem_H
+
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+#include <list>
+
+namespace Swift {
+
+class GroupRosterItem : public RosterItem {
+ public:
+ GroupRosterItem(const String& name, TreeWidget* tree, TreeWidgetFactory* factory) : name_(name) {
+ widget_ = factory->createTreeWidgetItem(tree);
+ widget_->setExpanded(true);
+ widget_->setText(name);
+ widget_->setTextColor(0xFFFFFF);
+ widget_->setBackgroundColor(0x969696);
+ }
+
+ ~GroupRosterItem() {
+ delete widget_;
+ }
+
+ const String& getName() const {
+ return name_;
+ }
+
+ TreeWidgetItem* getWidget() const {
+ return widget_;
+ }
+
+ const std::list<RosterItem*>& getChildren() const {
+ return children_;
+ }
+
+ void addChild(RosterItem* item) {
+ children_.push_back(item);
+ }
+
+ void removeChild(const JID& jid) {
+ std::list<RosterItem*>::iterator it = children_.begin();
+ while (it != children_.end()) {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(*it);
+ if (contact && contact->getJID() == jid) {
+ delete contact;
+ it = children_.erase(it);
+ continue;
+ }
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group) {
+ group->removeChild(jid);
+ }
+ it++;
+ }
+ }
+
+ private:
+ String name_;
+ TreeWidgetItem* widget_;
+ std::list<RosterItem*> children_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/Makefile.inc b/Swiften/Roster/Makefile.inc
new file mode 100644
index 0000000..7c5b007
--- /dev/null
+++ b/Swiften/Roster/Makefile.inc
@@ -0,0 +1,6 @@
+SWIFTEN_SOURCES += \
+ Swiften/Roster/ContactRosterItem.cpp \
+ Swiften/Roster/Roster.cpp \
+ Swiften/Roster/XMPPRoster.cpp
+
+include Swiften/Roster/UnitTest/Makefile.inc
diff --git a/Swiften/Roster/OfflineRosterFilter.h b/Swiften/Roster/OfflineRosterFilter.h
new file mode 100644
index 0000000..512d074
--- /dev/null
+++ b/Swiften/Roster/OfflineRosterFilter.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_OfflineRosterFilter_H
+#define SWIFTEN_OfflineRosterFilter_H
+
+#include "Swiften/Roster/ContactRosterItem.h"
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Roster/RosterFilter.h"
+#include "Swiften/Elements/StatusShow.h"
+
+namespace Swift {
+
+class OfflineRosterFilter : public RosterFilter {
+ public:
+ virtual ~OfflineRosterFilter() {}
+ virtual bool operator() (RosterItem *item) const {
+ ContactRosterItem *contactItem = dynamic_cast<ContactRosterItem*>(item);
+ return contactItem && contactItem->getStatusShow() == StatusShow::None;
+ }
+};
+
+}
+#endif
+
+
+
diff --git a/Swiften/Roster/OpenChatRosterAction.h b/Swiften/Roster/OpenChatRosterAction.h
new file mode 100644
index 0000000..03715a5
--- /dev/null
+++ b/Swiften/Roster/OpenChatRosterAction.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_OpenChatRosterAction_H
+#define SWIFTEN_OpenChatRosterAction_H
+
+#include "Swiften/Roster/UserRosterAction.h"
+
+namespace Swift {
+class RosterItem;
+class TreeWidgetItem;
+
+class OpenChatRosterAction : public UserRosterAction {
+ public:
+ virtual ~OpenChatRosterAction() {};
+
+};
+
+}
+#endif
+
+
+
diff --git a/Swiften/Roster/Roster.cpp b/Swiften/Roster/Roster.cpp
new file mode 100644
index 0000000..61c0286
--- /dev/null
+++ b/Swiften/Roster/Roster.cpp
@@ -0,0 +1,127 @@
+#include "Swiften/Roster/Roster.h"
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Roster/GroupRosterItem.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+
+#include <boost/bind.hpp>
+
+#include <deque>
+
+namespace Swift {
+
+Roster::Roster(TreeWidget *treeWidget, TreeWidgetFactory *widgetFactory) : treeWidget_(treeWidget), widgetFactory_(widgetFactory) {
+}
+
+Roster::~Roster() {
+ foreach (RosterItem* item, items_) {
+ delete item;
+ }
+ delete treeWidget_;
+}
+
+TreeWidget* Roster::getWidget() {
+ return treeWidget_;
+}
+
+GroupRosterItem* Roster::getGroup(const String& groupName) {
+ foreach (RosterItem *item, children_) {
+ GroupRosterItem *group = dynamic_cast<GroupRosterItem*>(item);
+ if (group && group->getName() == groupName) {
+ return group;
+ }
+ }
+ GroupRosterItem* group = new GroupRosterItem(groupName, treeWidget_, widgetFactory_);
+ children_.push_back(group);
+ items_.push_back(group);
+ return group;
+}
+
+void Roster::handleUserAction(boost::shared_ptr<UserRosterAction> action) {
+ onUserAction(action);
+}
+
+void Roster::addContact(const JID& jid, const String& name, const String& group) {
+ ContactRosterItem *item = new ContactRosterItem(jid, name, getGroup(group), widgetFactory_);
+ items_.push_back(item);
+ item->onUserAction.connect(boost::bind(&Roster::handleUserAction, this, _1));
+ filterItem(item);
+
+}
+
+void Roster::removeContact(const JID& jid) {
+ std::vector<RosterItem*>::iterator it = children_.begin();
+ while (it != children_.end()) {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(*it);
+ if (contact && contact->getJID() == jid) {
+ delete contact;
+ it = children_.erase(it);
+ continue;
+ }
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group) {
+ group->removeChild(jid);
+ }
+ it++;
+ }
+}
+
+void Roster::applyOnItems(const RosterItemOperation& operation) {
+ std::deque<RosterItem*> queue(children_.begin(), children_.end());
+ while (!queue.empty()) {
+ RosterItem* item = *queue.begin();
+ queue.pop_front();
+ operation(item);
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item);
+ if (group) {
+ queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end());
+ }
+ }
+ filterAll();
+}
+
+void Roster::removeFilter(RosterFilter *filter) {
+ for (unsigned int i = 0; i < filters_.size(); i++) {
+ if (filters_[i] == filter) {
+ filters_.erase(filters_.begin() + i);
+ break;
+ }
+ }
+ filterAll();
+}
+
+
+void Roster::filterItem(RosterItem* rosterItem) {
+ ContactRosterItem *item = dynamic_cast<ContactRosterItem*>(rosterItem);
+ if (!item) {
+ return;
+ }
+ bool hide = true;
+ foreach (RosterFilter *filter, filters_) {
+ hide &= (*filter)(item);
+ }
+ filters_.size() > 0 && hide ? item->hide() : item->show();
+}
+
+void Roster::filterAll() {
+ std::deque<RosterItem*> queue(children_.begin(), children_.end());
+ while (!queue.empty()) {
+ RosterItem *item = *queue.begin();
+ queue.pop_front();
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item);
+ if (group) {
+ queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end());
+ } else {
+ filterItem(item);
+ }
+ }
+}
+
+}