summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-03-28 15:46:49 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-03-28 15:46:49 (GMT)
commitf53a1ef582494458301b97bf6e546be52d7ff7e8 (patch)
tree7571b5cbcbd8a8f1dd1c966c9045b6cb69f0e295 /Swiften
parent638345680d72ca6acaf123f2c8c1c391f696e371 (diff)
downloadswift-f53a1ef582494458301b97bf6e546be52d7ff7e8.zip
swift-f53a1ef582494458301b97bf6e546be52d7ff7e8.tar.bz2
Moving submodule contents back.
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/.gitignore2
-rw-r--r--Swiften/Application/Application.cpp30
-rw-r--r--Swiften/Application/Application.h33
-rw-r--r--Swiften/Application/ApplicationMessageDisplay.cpp8
-rw-r--r--Swiften/Application/ApplicationMessageDisplay.h15
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplication.cpp22
-rw-r--r--Swiften/Application/MacOSX/MacOSXApplication.h25
-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/NullApplicationMessageDisplay.h16
-rw-r--r--Swiften/Application/Platform/PlatformApplication.h24
-rw-r--r--Swiften/Application/SConscript16
-rw-r--r--Swiften/Application/UnitTest/ApplicationTest.cpp39
-rw-r--r--Swiften/Application/Unix/UnixApplication.h32
-rw-r--r--Swiften/Application/Windows/WindowsApplication.h36
-rw-r--r--Swiften/Avatars/AvatarFileStorage.cpp26
-rw-r--r--Swiften/Avatars/AvatarFileStorage.h24
-rw-r--r--Swiften/Avatars/AvatarManager.cpp108
-rw-r--r--Swiften/Avatars/AvatarManager.h51
-rw-r--r--Swiften/Avatars/AvatarStorage.cpp8
-rw-r--r--Swiften/Avatars/AvatarStorage.h18
-rw-r--r--Swiften/Avatars/UnitTest/AvatarManagerTest.cpp110
-rw-r--r--Swiften/Avatars/UnitTest/MockAvatarManager.cpp26
-rw-r--r--Swiften/Avatars/UnitTest/MockAvatarManager.h17
-rw-r--r--Swiften/Base/ByteArray.cpp34
-rw-r--r--Swiften/Base/ByteArray.h103
-rw-r--r--Swiften/Base/Error.cpp8
-rw-r--r--Swiften/Base/Error.h8
-rw-r--r--Swiften/Base/IDGenerator.cpp28
-rw-r--r--Swiften/Base/IDGenerator.h18
-rw-r--r--Swiften/Base/Platform.h44
-rw-r--r--Swiften/Base/SConscript10
-rw-r--r--Swiften/Base/String.cpp116
-rw-r--r--Swiften/Base/String.h131
-rw-r--r--Swiften/Base/UnitTest/ByteArrayTest.cpp21
-rw-r--r--Swiften/Base/UnitTest/IDGeneratorTest.cpp34
-rw-r--r--Swiften/Base/UnitTest/StringTest.cpp179
-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/Chat/ChatStateActionProvider.h7
-rw-r--r--Swiften/Chat/ChatStateMessageSender.cpp22
-rw-r--r--Swiften/Chat/ChatStateMessageSender.h20
-rw-r--r--Swiften/Chat/ChatStateNotifier.cpp45
-rw-r--r--Swiften/Chat/ChatStateNotifier.h26
-rw-r--r--Swiften/Chat/ChatStateTracker.cpp28
-rw-r--r--Swiften/Chat/ChatStateTracker.h21
-rw-r--r--Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp132
-rw-r--r--Swiften/Client/Client.cpp212
-rw-r--r--Swiften/Client/Client.h78
-rw-r--r--Swiften/Client/ClientError.h32
-rw-r--r--Swiften/Client/ClientSession.cpp276
-rw-r--r--Swiften/Client/ClientSession.h100
-rw-r--r--Swiften/Client/ClientXMLTracer.h26
-rw-r--r--Swiften/Client/DummyStanzaChannel.h38
-rw-r--r--Swiften/Client/StanzaChannel.h20
-rw-r--r--Swiften/Client/UnitTest/ClientSessionTest.cpp498
-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/Disco/CapsInfoGenerator.cpp34
-rw-r--r--Swiften/Disco/CapsInfoGenerator.h21
-rw-r--r--Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp35
-rw-r--r--Swiften/Elements/AuthChallenge.h23
-rw-r--r--Swiften/Elements/AuthFailure.h13
-rw-r--r--Swiften/Elements/AuthRequest.h36
-rw-r--r--Swiften/Elements/AuthResponse.h23
-rw-r--r--Swiften/Elements/AuthSuccess.h22
-rw-r--r--Swiften/Elements/Body.h26
-rw-r--r--Swiften/Elements/CapsInfo.h23
-rw-r--r--Swiften/Elements/ChatState.h20
-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/ErrorPayload.h67
-rw-r--r--Swiften/Elements/IQ.cpp37
-rw-r--r--Swiften/Elements/IQ.h36
-rw-r--r--Swiften/Elements/MUCPayload.h11
-rw-r--r--Swiften/Elements/Message.h43
-rw-r--r--Swiften/Elements/Payload.cpp8
-rw-r--r--Swiften/Elements/Payload.h11
-rw-r--r--Swiften/Elements/Presence.h62
-rw-r--r--Swiften/Elements/Priority.h25
-rw-r--r--Swiften/Elements/PrivateStorage.h24
-rw-r--r--Swiften/Elements/ProtocolHeader.h36
-rw-r--r--Swiften/Elements/RawXMLPayload.h22
-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/Storage.h36
-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/StanzaTest.cpp155
-rw-r--r--Swiften/Elements/UnitTest/StanzasTest.cpp3
-rw-r--r--Swiften/Elements/UnknownElement.h13
-rw-r--r--Swiften/Elements/VCard.h42
-rw-r--r--Swiften/Elements/VCardUpdate.h17
-rw-r--r--Swiften/Elements/Version.h24
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEvent.h20
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEvent.mm25
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEventLoop.h14
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEventLoop.mm21
-rw-r--r--Swiften/EventLoop/Deleter.h23
-rw-r--r--Swiften/EventLoop/DummyEventLoop.h41
-rw-r--r--Swiften/EventLoop/Event.h22
-rw-r--r--Swiften/EventLoop/EventLoop.cpp49
-rw-r--r--Swiften/EventLoop/EventLoop.h38
-rw-r--r--Swiften/EventLoop/EventOwner.cpp8
-rw-r--r--Swiften/EventLoop/EventOwner.h8
-rw-r--r--Swiften/EventLoop/MainEventLoop.cpp36
-rw-r--r--Swiften/EventLoop/MainEventLoop.h41
-rw-r--r--Swiften/EventLoop/Qt/QtEventLoop.h36
-rw-r--r--Swiften/EventLoop/SConscript17
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.cpp54
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.h30
-rw-r--r--Swiften/EventLoop/UnitTest/EventLoopTest.cpp67
-rw-r--r--Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp62
-rw-r--r--Swiften/Events/MessageEvent.h33
-rw-r--r--Swiften/Events/StanzaEvent.h18
-rw-r--r--Swiften/Events/SubscriptionRequestEvent.h36
-rw-r--r--Swiften/Examples/EchoBot/.gitignore1
-rw-r--r--Swiften/Examples/EchoBot/EchoBot.cpp56
-rw-r--r--Swiften/Examples/SConscript8
-rw-r--r--Swiften/Examples/SendMessage/.gitignore1
-rw-r--r--Swiften/Examples/SendMessage/SConscript13
-rw-r--r--Swiften/Examples/SendMessage/SendMessage.cpp53
-rw-r--r--Swiften/History/HistoryManager.cpp8
-rw-r--r--Swiften/History/HistoryManager.h16
-rw-r--r--Swiften/History/HistoryMessage.h37
-rw-r--r--Swiften/History/SConscript11
-rw-r--r--Swiften/History/SQLiteHistoryManager.cpp134
-rw-r--r--Swiften/History/SQLiteHistoryManager.h27
-rw-r--r--Swiften/History/UnitTest/SQLiteHistoryManagerTest.cpp109
-rw-r--r--Swiften/JID/JID.cpp86
-rw-r--r--Swiften/JID/JID.h80
-rw-r--r--Swiften/JID/SConscript9
-rw-r--r--Swiften/JID/UnitTest/JIDTest.cpp310
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h64
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp60
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h47
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp13
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h22
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h123
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h31
-rw-r--r--Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h73
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h55
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp129
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h50
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp31
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h32
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h55
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h63
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h58
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp8
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h19
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp8
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDQuerier.h29
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp8
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h21
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp8
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h18
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp8
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h28
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp7
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDServiceID.h66
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h22
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp130
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h71
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp20
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h25
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h32
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h27
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h25
-rw-r--r--Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp22
-rw-r--r--Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h12
-rw-r--r--Swiften/LinkLocal/IncomingLinkLocalSession.cpp62
-rw-r--r--Swiften/LinkLocal/IncomingLinkLocalSession.h37
-rw-r--r--Swiften/LinkLocal/LinkLocalConnector.cpp68
-rw-r--r--Swiften/LinkLocal/LinkLocalConnector.h57
-rw-r--r--Swiften/LinkLocal/LinkLocalService.cpp27
-rw-r--r--Swiften/LinkLocal/LinkLocalService.h46
-rw-r--r--Swiften/LinkLocal/LinkLocalServiceBrowser.cpp147
-rw-r--r--Swiften/LinkLocal/LinkLocalServiceBrowser.h68
-rw-r--r--Swiften/LinkLocal/LinkLocalServiceInfo.cpp114
-rw-r--r--Swiften/LinkLocal/LinkLocalServiceInfo.h59
-rw-r--r--Swiften/LinkLocal/OutgoingLinkLocalSession.cpp45
-rw-r--r--Swiften/LinkLocal/OutgoingLinkLocalSession.h37
-rw-r--r--Swiften/LinkLocal/SConscript37
-rw-r--r--Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp135
-rw-r--r--Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp379
-rw-r--r--Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp63
-rw-r--r--Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp62
-rw-r--r--Swiften/MUC/MUC.cpp70
-rw-r--r--Swiften/MUC/MUC.h59
-rw-r--r--Swiften/MUC/MUCBookmark.h28
-rw-r--r--Swiften/MUC/MUCBookmarkManager.cpp39
-rw-r--r--Swiften/MUC/MUCBookmarkManager.h28
-rw-r--r--Swiften/MUC/MUCOccupant.cpp15
-rw-r--r--Swiften/MUC/MUCOccupant.h21
-rw-r--r--Swiften/MUC/MUCRegistry.cpp8
-rw-r--r--Swiften/MUC/MUCRegistry.h12
-rw-r--r--Swiften/Network/BoostConnection.cpp109
-rw-r--r--Swiften/Network/BoostConnection.h42
-rw-r--r--Swiften/Network/BoostConnectionFactory.cpp13
-rw-r--r--Swiften/Network/BoostConnectionFactory.h20
-rw-r--r--Swiften/Network/BoostConnectionServer.cpp68
-rw-r--r--Swiften/Network/BoostConnectionServer.h36
-rw-r--r--Swiften/Network/BoostIOServiceThread.cpp18
-rw-r--r--Swiften/Network/BoostIOServiceThread.h23
-rw-r--r--Swiften/Network/BoostTimer.cpp34
-rw-r--r--Swiften/Network/BoostTimer.h25
-rw-r--r--Swiften/Network/BoostTimerFactory.cpp13
-rw-r--r--Swiften/Network/BoostTimerFactory.h20
-rw-r--r--Swiften/Network/CAresDomainNameResolver.cpp162
-rw-r--r--Swiften/Network/CAresDomainNameResolver.h34
-rw-r--r--Swiften/Network/Connection.h31
-rw-r--r--Swiften/Network/ConnectionFactory.cpp8
-rw-r--r--Swiften/Network/ConnectionFactory.h14
-rw-r--r--Swiften/Network/ConnectionServer.cpp8
-rw-r--r--Swiften/Network/ConnectionServer.h15
-rw-r--r--Swiften/Network/Connector.cpp126
-rw-r--r--Swiften/Network/Connector.h54
-rw-r--r--Swiften/Network/DomainNameAddressQuery.cpp8
-rw-r--r--Swiften/Network/DomainNameAddressQuery.h19
-rw-r--r--Swiften/Network/DomainNameResolveError.h10
-rw-r--r--Swiften/Network/DomainNameResolver.cpp22
-rw-r--r--Swiften/Network/DomainNameResolver.h22
-rw-r--r--Swiften/Network/DomainNameServiceQuery.cpp8
-rw-r--r--Swiften/Network/DomainNameServiceQuery.h33
-rw-r--r--Swiften/Network/DummyConnection.h40
-rw-r--r--Swiften/Network/DummyTimerFactory.cpp60
-rw-r--r--Swiften/Network/DummyTimerFactory.h22
-rw-r--r--Swiften/Network/FakeConnection.h88
-rw-r--r--Swiften/Network/HostAddress.cpp61
-rw-r--r--Swiften/Network/HostAddress.h28
-rw-r--r--Swiften/Network/HostAddressPort.h30
-rw-r--r--Swiften/Network/MainBoostIOServiceThread.cpp12
-rw-r--r--Swiften/Network/MainBoostIOServiceThread.h10
-rw-r--r--Swiften/Network/PlatformDomainNameResolver.cpp94
-rw-r--r--Swiften/Network/PlatformDomainNameResolver.h15
-rw-r--r--Swiften/Network/PlatformDomainNameServiceQuery.cpp170
-rw-r--r--Swiften/Network/PlatformDomainNameServiceQuery.h27
-rw-r--r--Swiften/Network/SConscript34
-rw-r--r--Swiften/Network/StaticDomainNameResolver.cpp85
-rw-r--r--Swiften/Network/StaticDomainNameResolver.h52
-rw-r--r--Swiften/Network/Timer.cpp8
-rw-r--r--Swiften/Network/Timer.h15
-rw-r--r--Swiften/Network/TimerFactory.cpp8
-rw-r--r--Swiften/Network/TimerFactory.h14
-rw-r--r--Swiften/Network/UnitTest/ConnectorTest.cpp245
-rw-r--r--Swiften/Network/UnitTest/HostAddressTest.cpp38
-rw-r--r--Swiften/Notifier/GrowlNotifier.cpp91
-rw-r--r--Swiften/Notifier/GrowlNotifier.h28
-rw-r--r--Swiften/Notifier/Notifier.cpp8
-rw-r--r--Swiften/Notifier/Notifier.h24
-rw-r--r--Swiften/Parser/AttributeMap.h35
-rw-r--r--Swiften/Parser/AuthChallengeParser.cpp24
-rw-r--r--Swiften/Parser/AuthChallengeParser.h20
-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/AuthResponseParser.cpp24
-rw-r--r--Swiften/Parser/AuthResponseParser.h20
-rw-r--r--Swiften/Parser/AuthSuccessParser.cpp24
-rw-r--r--Swiften/Parser/AuthSuccessParser.h20
-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.cpp70
-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.cpp65
-rw-r--r--Swiften/Parser/LibXMLParser.h20
-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.cpp31
-rw-r--r--Swiften/Parser/PayloadParserFactoryCollection.h28
-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/ChatStateParser.cpp35
-rw-r--r--Swiften/Parser/PayloadParsers/ChatStateParser.h18
-rw-r--r--Swiften/Parser/PayloadParsers/ChatStateParserFactory.h25
-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.cpp61
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h19
-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/PrivateStorageParser.cpp43
-rw-r--r--Swiften/Parser/PayloadParsers/PrivateStorageParser.h25
-rw-r--r--Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.h26
-rw-r--r--Swiften/Parser/PayloadParsers/RawXMLPayloadParser.cpp26
-rw-r--r--Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h22
-rw-r--r--Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h20
-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/StorageParser.cpp49
-rw-r--r--Swiften/Parser/PayloadParsers/StorageParser.h27
-rw-r--r--Swiften/Parser/PayloadParsers/StorageParserFactory.h11
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp28
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp47
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp34
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h8
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h55
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp28
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp89
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp34
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp38
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp50
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp45
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp45
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp35
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp28
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp68
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp64
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp51
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp31
-rw-r--r--Swiften/Parser/PayloadParsers/VCardParser.cpp53
-rw-r--r--Swiften/Parser/PayloadParsers/VCardParser.h24
-rw-r--r--Swiften/Parser/PayloadParsers/VCardParserFactory.h11
-rw-r--r--Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp26
-rw-r--r--Swiften/Parser/PayloadParsers/VCardUpdateParser.h25
-rw-r--r--Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h11
-rw-r--r--Swiften/Parser/PlatformXMLParserFactory.cpp25
-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/SConscript58
-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/AttributeMapTest.cpp71
-rw-r--r--Swiften/Parser/UnitTest/ElementParserTester.h10
-rw-r--r--Swiften/Parser/UnitTest/IQParserTest.cpp70
-rw-r--r--Swiften/Parser/UnitTest/MessageParserTest.cpp80
-rw-r--r--Swiften/Parser/UnitTest/ParserTester.h44
-rw-r--r--Swiften/Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp97
-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.cpp229
-rw-r--r--Swiften/Parser/UnitTest/XMPPParserTest.cpp186
-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.cpp159
-rw-r--r--Swiften/Parser/XMPPParser.h54
-rw-r--r--Swiften/Parser/XMPPParserClient.cpp8
-rw-r--r--Swiften/Parser/XMPPParserClient.h19
-rw-r--r--Swiften/Presence/PresenceOracle.cpp58
-rw-r--r--Swiften/Presence/PresenceOracle.h30
-rw-r--r--Swiften/Presence/PresenceSender.cpp49
-rw-r--r--Swiften/Presence/PresenceSender.h24
-rw-r--r--Swiften/Presence/UnitTest/PresenceOracleTest.cpp126
-rw-r--r--Swiften/Presence/UnitTest/PresenceSenderTest.cpp120
-rw-r--r--Swiften/QA/ClientTest/.gitignore1
-rw-r--r--Swiften/QA/ClientTest/ClientTest.cpp68
-rw-r--r--Swiften/QA/ClientTest/SConscript22
-rw-r--r--Swiften/QA/NetworkTest/.gitignore1
-rw-r--r--Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp75
-rw-r--r--Swiften/QA/NetworkTest/BoostConnectionTest.cpp88
-rw-r--r--Swiften/QA/NetworkTest/DomainNameResolverTest.cpp168
-rw-r--r--Swiften/QA/NetworkTest/SConscript18
-rw-r--r--Swiften/QA/SConscript4
-rw-r--r--Swiften/Queries/DummyIQChannel.h29
-rw-r--r--Swiften/Queries/GenericRequest.h26
-rw-r--r--Swiften/Queries/GetResponder.h14
-rw-r--r--Swiften/Queries/IQChannel.cpp8
-rw-r--r--Swiften/Queries/IQChannel.h24
-rw-r--r--Swiften/Queries/IQHandler.cpp9
-rw-r--r--Swiften/Queries/IQHandler.h19
-rw-r--r--Swiften/Queries/IQRouter.cpp81
-rw-r--r--Swiften/Queries/IQRouter.h41
-rw-r--r--Swiften/Queries/Request.cpp55
-rw-r--r--Swiften/Queries/Request.h50
-rw-r--r--Swiften/Queries/Requests/GetDiscoInfoRequest.h13
-rw-r--r--Swiften/Queries/Requests/GetPrivateStorageRequest.h30
-rw-r--r--Swiften/Queries/Requests/GetRosterRequest.h13
-rw-r--r--Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h16
-rw-r--r--Swiften/Queries/Requests/GetVCardRequest.h12
-rw-r--r--Swiften/Queries/Requests/SetPrivateStorageRequest.h24
-rw-r--r--Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp106
-rw-r--r--Swiften/Queries/Responder.h58
-rw-r--r--Swiften/Queries/Responders/DiscoInfoResponder.cpp36
-rw-r--r--Swiften/Queries/Responders/DiscoInfoResponder.h28
-rw-r--r--Swiften/Queries/Responders/RosterPushResponder.h23
-rw-r--r--Swiften/Queries/Responders/SoftwareVersionResponder.cpp16
-rw-r--r--Swiften/Queries/Responders/SoftwareVersionResponder.h23
-rw-r--r--Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp84
-rw-r--r--Swiften/Queries/SetResponder.h14
-rw-r--r--Swiften/Queries/UnitTest/IQRouterTest.cpp143
-rw-r--r--Swiften/Queries/UnitTest/RequestTest.cpp167
-rw-r--r--Swiften/Queries/UnitTest/ResponderTest.cpp132
-rw-r--r--Swiften/Roster/AppearOffline.h26
-rw-r--r--Swiften/Roster/ContactRosterItem.cpp50
-rw-r--r--Swiften/Roster/ContactRosterItem.h41
-rw-r--r--Swiften/Roster/GroupRosterItem.h68
-rw-r--r--Swiften/Roster/OfflineRosterFilter.h24
-rw-r--r--Swiften/Roster/OpenChatRosterAction.h20
-rw-r--r--Swiften/Roster/Roster.cpp139
-rw-r--r--Swiften/Roster/Roster.h49
-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/SetAvatar.h33
-rw-r--r--Swiften/Roster/SetPresence.h38
-rw-r--r--Swiften/Roster/TreeWidget.h13
-rw-r--r--Swiften/Roster/TreeWidgetFactory.h20
-rw-r--r--Swiften/Roster/TreeWidgetItem.h34
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidget.h14
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidgetFactory.h50
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidgetItem.h30
-rw-r--r--Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp0
-rw-r--r--Swiften/Roster/UnitTest/RosterTest.cpp57
-rw-r--r--Swiften/Roster/UnitTest/XMPPRosterTest.cpp164
-rw-r--r--Swiften/Roster/UserRosterAction.h32
-rw-r--r--Swiften/Roster/XMPPRoster.cpp48
-rw-r--r--Swiften/Roster/XMPPRoster.h44
-rw-r--r--Swiften/SASL/ClientAuthenticator.cpp11
-rw-r--r--Swiften/SASL/ClientAuthenticator.h43
-rw-r--r--Swiften/SASL/PLAINClientAuthenticator.cpp16
-rw-r--r--Swiften/SASL/PLAINClientAuthenticator.h13
-rw-r--r--Swiften/SASL/PLAINMessage.cpp38
-rw-r--r--Swiften/SASL/PLAINMessage.h33
-rw-r--r--Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp143
-rw-r--r--Swiften/SASL/SCRAMSHA1ClientAuthenticator.h36
-rw-r--r--Swiften/SASL/SConscript12
-rw-r--r--Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp32
-rw-r--r--Swiften/SASL/UnitTest/PLAINMessageTest.cpp61
-rw-r--r--Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp172
-rw-r--r--Swiften/SConscript221
-rw-r--r--Swiften/Serializer/AuthChallengeSerializer.cpp17
-rw-r--r--Swiften/Serializer/AuthChallengeSerializer.h15
-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/AuthResponseSerializer.cpp17
-rw-r--r--Swiften/Serializer/AuthResponseSerializer.h15
-rw-r--r--Swiften/Serializer/AuthSuccessSerializer.cpp17
-rw-r--r--Swiften/Serializer/AuthSuccessSerializer.h15
-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/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.h23
-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/ChatStateSerializer.cpp22
-rw-r--r--Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h13
-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.cpp61
-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/PrioritySerializer.h20
-rw-r--r--Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.cpp28
-rw-r--r--Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.h18
-rw-r--r--Swiften/Serializer/PayloadSerializers/RawXMLPayloadSerializer.h15
-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/StorageSerializer.cpp36
-rw-r--r--Swiften/Serializer/PayloadSerializers/StorageSerializer.h13
-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/PayloadsSerializer.cpp21
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h17
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp25
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp44
-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.cpp54
-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/PayloadSerializers/UnitTest/StorageSerializerTest.cpp60
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp31
-rw-r--r--Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp50
-rw-r--r--Swiften/Serializer/PayloadSerializers/VCardSerializer.h13
-rw-r--r--Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.cpp21
-rw-r--r--Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h13
-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/StreamFeaturesSerializerTest.cpp46
-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.h21
-rw-r--r--Swiften/Serializer/XMPPSerializer.cpp77
-rw-r--r--Swiften/Serializer/XMPPSerializer.h26
-rw-r--r--Swiften/Server/ServerFromClientSession.cpp97
-rw-r--r--Swiften/Server/ServerFromClientSession.h52
-rw-r--r--Swiften/Server/ServerSession.cpp8
-rw-r--r--Swiften/Server/ServerSession.h17
-rw-r--r--Swiften/Server/ServerStanzaRouter.cpp67
-rw-r--r--Swiften/Server/ServerStanzaRouter.h24
-rw-r--r--Swiften/Server/SimpleUserRegistry.cpp17
-rw-r--r--Swiften/Server/SimpleUserRegistry.h22
-rw-r--r--Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp144
-rw-r--r--Swiften/Server/UserRegistry.cpp8
-rw-r--r--Swiften/Server/UserRegistry.h13
-rw-r--r--Swiften/Session/BasicSessionStream.cpp140
-rw-r--r--Swiften/Session/BasicSessionStream.h75
-rw-r--r--Swiften/Session/Session.cpp82
-rw-r--r--Swiften/Session/Session.h105
-rw-r--r--Swiften/Session/SessionStream.cpp8
-rw-r--r--Swiften/Session/SessionStream.h69
-rw-r--r--Swiften/Session/SessionTracer.h29
-rw-r--r--Swiften/Settings/SettingsProvider.h24
-rw-r--r--Swiften/StreamStack/CompressionLayer.h48
-rw-r--r--Swiften/StreamStack/ConnectionLayer.h23
-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/OpenSSLLayer.cpp28
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.h27
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.cpp31
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.h13
-rw-r--r--Swiften/StreamStack/SConscript21
-rw-r--r--Swiften/StreamStack/StreamLayer.h16
-rw-r--r--Swiften/StreamStack/StreamStack.cpp40
-rw-r--r--Swiften/StreamStack/StreamStack.h44
-rw-r--r--Swiften/StreamStack/TLSLayer.h19
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.cpp8
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.h15
-rw-r--r--Swiften/StreamStack/UnitTest/StreamStackTest.cpp165
-rw-r--r--Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp119
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.cpp39
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.h32
-rw-r--r--Swiften/StreamStack/XMPPLayer.cpp80
-rw-r--r--Swiften/StreamStack/XMPPLayer.h55
-rw-r--r--Swiften/StringCodecs/Base64.cpp109
-rw-r--r--Swiften/StringCodecs/Base64.h14
-rw-r--r--Swiften/StringCodecs/HMACSHA1.cpp39
-rw-r--r--Swiften/StringCodecs/HMACSHA1.h10
-rw-r--r--Swiften/StringCodecs/Hexify.cpp22
-rw-r--r--Swiften/StringCodecs/Hexify.h11
-rw-r--r--Swiften/StringCodecs/MD5.cpp359
-rw-r--r--Swiften/StringCodecs/MD5.h10
-rw-r--r--Swiften/StringCodecs/PBKDF2.cpp20
-rw-r--r--Swiften/StringCodecs/PBKDF2.h10
-rw-r--r--Swiften/StringCodecs/SHA1.cpp197
-rw-r--r--Swiften/StringCodecs/SHA1.h10
-rw-r--r--Swiften/StringCodecs/UnitTest/Base64Test.cpp44
-rw-r--r--Swiften/StringCodecs/UnitTest/HMACSHA1Test.cpp21
-rw-r--r--Swiften/StringCodecs/UnitTest/HexifyTest.cpp21
-rw-r--r--Swiften/StringCodecs/UnitTest/MD5Test.cpp29
-rw-r--r--Swiften/StringCodecs/UnitTest/PBKDF2Test.cpp36
-rw-r--r--Swiften/StringCodecs/UnitTest/SHA1Test.cpp37
-rw-r--r--Swiften/StringPrep/SConscript9
-rw-r--r--Swiften/StringPrep/StringPrep.cpp33
-rw-r--r--Swiften/StringPrep/StringPrep.h17
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp159
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h43
-rw-r--r--Swiften/TLS/PKCS12Certificate.h37
666 files changed, 27295 insertions, 0 deletions
diff --git a/Swiften/.gitignore b/Swiften/.gitignore
new file mode 100644
index 0000000..9eca6c8
--- /dev/null
+++ b/Swiften/.gitignore
@@ -0,0 +1,2 @@
+*.a
+*.o
diff --git a/Swiften/Application/Application.cpp b/Swiften/Application/Application.cpp
new file mode 100644
index 0000000..be77e95
--- /dev/null
+++ b/Swiften/Application/Application.cpp
@@ -0,0 +1,30 @@
+#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::getAvatarDir() const {
+ return getSettingsDir() / "avatars";
+}
+
+boost::filesystem::path Application::getProfileDir(const String& profile) const {
+ boost::filesystem::path result(getHomeDir() / profile.getUTF8String());
+ boost::filesystem::create_directory(result);
+ return result;
+}
+
+}
diff --git a/Swiften/Application/Application.h b/Swiften/Application/Application.h
new file mode 100644
index 0000000..20d686c
--- /dev/null
+++ b/Swiften/Application/Application.h
@@ -0,0 +1,33 @@
+#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 getAvatarDir() const;
+ virtual boost::filesystem::path getHomeDir() const = 0;
+ virtual boost::filesystem::path getSettingsDir() const = 0;
+ boost::filesystem::path getProfileDir(const String& profile) const;
+
+ 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..79d7586
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplication.cpp
@@ -0,0 +1,22 @@
+#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;
+}
+
+boost::filesystem::path MacOSXApplication::getHomeDir() const {
+ return boost::filesystem::path(getenv("HOME"));
+}
+
+}
diff --git a/Swiften/Application/MacOSX/MacOSXApplication.h b/Swiften/Application/MacOSX/MacOSXApplication.h
new file mode 100644
index 0000000..cd5e69f
--- /dev/null
+++ b/Swiften/Application/MacOSX/MacOSXApplication.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_MacOSXApplication_H
+#define SWIFTEN_MacOSXApplication_H
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/MacOSX/MacOSXApplicationMessageDisplay.h"
+#include "Swiften/Application/MacOSX/MacOSXApplicationInitializer.h"
+
+namespace Swift {
+ class ApplicationMessageDisplay;
+
+ class MacOSXApplication : public Application {
+ public:
+ MacOSXApplication(const String& name);
+
+ virtual boost::filesystem::path getHomeDir() const;
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay();
+ boost::filesystem::path getSettingsDir() const;
+
+ private:
+ MacOSXApplicationInitializer initializer_;
+ 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/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/SConscript b/Swiften/Application/SConscript
new file mode 100644
index 0000000..78b3a34
--- /dev/null
+++ b/Swiften/Application/SConscript
@@ -0,0 +1,16 @@
+Import("swiften_env")
+
+sources = [
+ "Application.cpp",
+ "ApplicationMessageDisplay.cpp",
+ ]
+
+if swiften_env["PLATFORM"] == "darwin" and swiften_env["target"] == "native" :
+ sources += [
+ "MacOSX/MacOSXApplication.cpp",
+ "MacOSX/MacOSXApplicationMessageDisplay.mm",
+ "MacOSX/MacOSXApplicationInitializer.mm",
+ ]
+
+objects = swiften_env.StaticObject(sources)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/Application/UnitTest/ApplicationTest.cpp b/Swiften/Application/UnitTest/ApplicationTest.cpp
new file mode 100644
index 0000000..0196755
--- /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/Unix/UnixApplication.h b/Swiften/Application/Unix/UnixApplication.h
new file mode 100644
index 0000000..c2671d8
--- /dev/null
+++ b/Swiften/Application/Unix/UnixApplication.h
@@ -0,0 +1,32 @@
+#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) {
+ }
+
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay() {
+ return &messageDisplay_;
+ }
+
+ virtual boost::filesystem::path getHomeDir() const {
+ return boost::filesystem::path(getenv("HOME"));
+ }
+
+ boost::filesystem::path getSettingsDir() const {
+ boost::filesystem::path result(getHomeDir() / ("." + getName().getLowerCase().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..365157b
--- /dev/null
+++ b/Swiften/Application/Windows/WindowsApplication.h
@@ -0,0 +1,36 @@
+#ifndef SWIFTEN_WindowsApplication_H
+#define SWIFTEN_WindowsApplication_H
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/NullApplicationMessageDisplay.h"
+
+namespace Swift {
+ class WindowsApplication : public Application {
+ public:
+ WindowsApplication(const String& name) : Application(name) {
+ }
+
+ virtual ApplicationMessageDisplay* getApplicationMessageDisplay() {
+ return &messageDisplay_;
+ }
+
+ boost::filesystem::path getSettingsDir() const {
+ char* appDirRaw = getenv("APPDATA");
+ boost::filesystem::path result(boost::filesystem::path(appDirRaw) / getName().getUTF8String());
+ boost::filesystem::create_directory(result);
+ return result;
+ }
+
+ boost::filesystem::path getHomeDir() const {
+ //FIXME: This should be My Documents
+
+ char* homeDirRaw = getenv("USERPROFILE");
+ return boost::filesystem::path(homeDirRaw);
+ }
+
+ private:
+ NullApplicationMessageDisplay messageDisplay_;
+ };
+}
+
+#endif
diff --git a/Swiften/Avatars/AvatarFileStorage.cpp b/Swiften/Avatars/AvatarFileStorage.cpp
new file mode 100644
index 0000000..1348018
--- /dev/null
+++ b/Swiften/Avatars/AvatarFileStorage.cpp
@@ -0,0 +1,26 @@
+#include "Swiften/Avatars/AvatarFileStorage.h"
+
+#include <iostream>
+#include <boost/filesystem/fstream.hpp>
+
+namespace Swift {
+
+AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& path) : path_(path) {
+ boost::filesystem::create_directory(path_);
+}
+
+bool AvatarFileStorage::hasAvatar(const String& hash) const {
+ return boost::filesystem::exists(getAvatarPath(hash));
+}
+
+void AvatarFileStorage::addAvatar(const String& hash, const ByteArray& avatar) {
+ boost::filesystem::ofstream file(getAvatarPath(hash), boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out);
+ file.write(avatar.getData(), avatar.getSize());
+ file.close();
+}
+
+boost::filesystem::path AvatarFileStorage::getAvatarPath(const String& hash) const {
+ return path_ / hash.getUTF8String();
+}
+
+}
diff --git a/Swiften/Avatars/AvatarFileStorage.h b/Swiften/Avatars/AvatarFileStorage.h
new file mode 100644
index 0000000..1afa703
--- /dev/null
+++ b/Swiften/Avatars/AvatarFileStorage.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <map>
+#include <boost/filesystem.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Avatars/AvatarStorage.h"
+
+namespace Swift {
+ class AvatarFileStorage : public AvatarStorage {
+ public:
+ AvatarFileStorage(const boost::filesystem::path& path);
+
+ virtual bool hasAvatar(const String& hash) const;
+ virtual void addAvatar(const String& hash, const ByteArray& avatar);
+
+ virtual boost::filesystem::path getAvatarPath(const String& hash) const;
+
+ private:
+ boost::filesystem::path path_;
+ };
+
+}
diff --git a/Swiften/Avatars/AvatarManager.cpp b/Swiften/Avatars/AvatarManager.cpp
new file mode 100644
index 0000000..3825ffd
--- /dev/null
+++ b/Swiften/Avatars/AvatarManager.cpp
@@ -0,0 +1,108 @@
+#include "Swiften/Avatars/AvatarManager.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Elements/VCardUpdate.h"
+#include "Swiften/Queries/Requests/GetVCardRequest.h"
+#include "Swiften/StringCodecs/SHA1.h"
+#include "Swiften/StringCodecs/Hexify.h"
+#include "Swiften/Avatars/AvatarStorage.h"
+#include "Swiften/MUC/MUCRegistry.h"
+
+namespace Swift {
+
+AvatarManager::AvatarManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), avatarStorage_(avatarStorage), mucRegistry_(mucRegistry) {
+ stanzaChannel->onPresenceReceived.connect(boost::bind(&AvatarManager::handlePresenceReceived, this, _1));
+}
+
+AvatarManager::AvatarManager() {
+ stanzaChannel_ = NULL;
+ iqRouter_ = NULL;
+ avatarStorage_ = NULL;
+ mucRegistry_ = NULL;
+}
+
+AvatarManager::~AvatarManager() {
+
+}
+
+void AvatarManager::setMUCRegistry(MUCRegistry* mucRegistry) {
+ mucRegistry_ = mucRegistry;
+}
+
+void AvatarManager::handlePresenceReceived(boost::shared_ptr<Presence> presence) {
+ boost::shared_ptr<VCardUpdate> update = presence->getPayload<VCardUpdate>();
+ if (!update) {
+ return;
+ }
+ JID from = getAvatarJID(presence->getFrom());
+ String& hash = avatarHashes_[from];
+ if (hash != update->getPhotoHash()) {
+ String newHash = update->getPhotoHash();
+ if (avatarStorage_->hasAvatar(newHash)) {
+ setAvatarHash(from, newHash);
+ }
+ else {
+ boost::shared_ptr<GetVCardRequest> request(new GetVCardRequest(from, iqRouter_));
+ request->onResponse.connect(boost::bind(&AvatarManager::handleVCardReceived, this, from, newHash, _1, _2));
+ request->send();
+ }
+ }
+}
+
+void AvatarManager::handleVCardReceived(const JID& from, const String& promisedHash, boost::shared_ptr<VCard> vCard, const boost::optional<ErrorPayload>& error) {
+ if (error) {
+ // FIXME: What to do here?
+ std::cerr << "Warning: " << from << ": Could not get vCard" << std::endl;
+ return;
+ }
+ if (!vCard) {
+ std::cerr << "Warning: " << from << ": null vcard payload" << std::endl;
+ //FIXME: Why could this happen?
+ return;
+ }
+ String realHash = Hexify::hexify(SHA1::getHash(vCard->getPhoto()));
+ if (promisedHash != realHash) {
+ std::cerr << "Warning: " << from << ": Got different vCard photo hash (" << promisedHash << " != " << realHash << ")" << std::endl;
+ }
+ avatarStorage_->addAvatar(realHash, vCard->getPhoto());
+ setAvatarHash(from, realHash);
+}
+
+void AvatarManager::setAvatar(const JID& jid, const ByteArray& avatar) {
+ String hash = Hexify::hexify(SHA1::getHash(avatar));
+ avatarStorage_->addAvatar(hash, avatar);
+ setAvatarHash(getAvatarJID(jid), hash);
+}
+
+void AvatarManager::setAvatarHash(const JID& from, const String& hash) {
+ avatarHashes_[from] = hash;
+ onAvatarChanged(from, hash);
+}
+
+String AvatarManager::getAvatarHash(const JID& jid) const {
+ std::map<JID, String>::const_iterator i = avatarHashes_.find(getAvatarJID(jid));
+ if (i != avatarHashes_.end()) {
+ return i->second;
+ }
+ else {
+ return "";
+ }
+}
+
+boost::filesystem::path AvatarManager::getAvatarPath(const JID& jid) const {
+ String hash = getAvatarHash(jid);
+ if (!hash.isEmpty()) {
+ return avatarStorage_->getAvatarPath(hash);
+ }
+ return boost::filesystem::path();
+}
+
+JID AvatarManager::getAvatarJID(const JID& jid) const {
+ JID bareFrom = jid.toBare();
+ return (mucRegistry_ && mucRegistry_->isMUC(bareFrom)) ? jid : bareFrom;
+}
+
+
+}
diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h
new file mode 100644
index 0000000..fd308d9
--- /dev/null
+++ b/Swiften/Avatars/AvatarManager.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <boost/filesystem.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <boost/signal.hpp>
+#include <map>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/VCard.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ class MUCRegistry;
+ class AvatarStorage;
+ class StanzaChannel;
+ class IQRouter;
+
+ class AvatarManager {
+ public:
+ AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*, MUCRegistry* = NULL);
+ virtual ~AvatarManager();
+
+ virtual void setMUCRegistry(MUCRegistry*);
+
+ virtual String getAvatarHash(const JID&) const;
+ virtual boost::filesystem::path getAvatarPath(const JID&) const;
+ virtual void setAvatar(const JID&, const ByteArray& avatar);
+
+ public:
+ boost::signal<void (const JID&, const String&)> onAvatarChanged;
+
+ protected:
+ /** Used only for testing. Leads to a non-functional object. */
+ AvatarManager();
+
+ private:
+ void handlePresenceReceived(boost::shared_ptr<Presence>);
+ void handleVCardReceived(const JID& from, const String& hash, boost::shared_ptr<VCard>, const boost::optional<ErrorPayload>&);
+ void setAvatarHash(const JID& from, const String& hash);
+ JID getAvatarJID(const JID& o) const;
+
+ private:
+ StanzaChannel* stanzaChannel_;
+ IQRouter* iqRouter_;
+ AvatarStorage* avatarStorage_;
+ MUCRegistry* mucRegistry_;
+ std::map<JID, String> avatarHashes_;
+ };
+}
diff --git a/Swiften/Avatars/AvatarStorage.cpp b/Swiften/Avatars/AvatarStorage.cpp
new file mode 100644
index 0000000..4c98314
--- /dev/null
+++ b/Swiften/Avatars/AvatarStorage.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Avatars/AvatarStorage.h"
+
+namespace Swift {
+
+AvatarStorage::~AvatarStorage() {
+}
+
+}
diff --git a/Swiften/Avatars/AvatarStorage.h b/Swiften/Avatars/AvatarStorage.h
new file mode 100644
index 0000000..b5c0f32
--- /dev/null
+++ b/Swiften/Avatars/AvatarStorage.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/filesystem.hpp>
+
+namespace Swift {
+ class String;
+ class ByteArray;
+
+ class AvatarStorage {
+ public:
+ virtual ~AvatarStorage();
+
+ virtual bool hasAvatar(const String& hash) const = 0;
+ virtual void addAvatar(const String& hash, const ByteArray& avatar) = 0;
+ virtual boost::filesystem::path getAvatarPath(const String& hash) const = 0;
+ };
+
+}
diff --git a/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp b/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp
new file mode 100644
index 0000000..3b4c5f9
--- /dev/null
+++ b/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp
@@ -0,0 +1,110 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/VCardUpdate.h"
+#include "Swiften/Avatars/AvatarManager.h"
+#include "Swiften/Avatars/AvatarStorage.h"
+#include "Swiften/MUC/MUCRegistry.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+
+using namespace Swift;
+
+class AvatarManagerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(AvatarManagerTest);
+ CPPUNIT_TEST(testUpdate_UpdateNewHash);
+ CPPUNIT_TEST(testUpdate_UpdateNewHashAlreadyHaveAvatar);
+ CPPUNIT_TEST(testUpdate_UpdateNewHashFromMUC);
+ CPPUNIT_TEST(testUpdate_UpdateSameHash);
+ CPPUNIT_TEST(testUpdate_UpdateNewHashSameThanOtherUser);
+ CPPUNIT_TEST(testReceiveVCard);
+ CPPUNIT_TEST(testGetAvatarPath);
+ CPPUNIT_TEST(testGetAvatarPathFromMUC);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ AvatarManagerTest() {}
+
+ void setUp() {
+ stanzaChannel_ = new DummyStanzaChannel();
+ iqRouter_ = new IQRouter(stanzaChannel_);
+ mucRegistry_ = new DummyMUCRegistry();
+ avatarStorage_ = new DummyAvatarStorage();
+ }
+
+ void tearDown() {
+ delete avatarStorage_;
+ delete mucRegistry_;
+ delete iqRouter_;
+ delete stanzaChannel_;
+ }
+
+ void testUpdate_UpdateNewHash() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ stanzaChannel_->onPresenceReceived(createPresenceWithPhotoHash());
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel_->sentStanzas_.size()));
+ IQ*
+ CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<VCardUpdate>(0, JID("foo@bar.com"), IQ::Get));
+ }
+
+ void testUpdate_UpdateNewHashAlreadyHaveAvatar() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ void testUpdate_UpdateNewHashFromMUC() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ void testUpdate_UpdateSameHash() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ void testUpdate_UpdateNewHashSameThanOtherUser() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ void testReceiveVCard() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ void testGetAvatarPath() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ void testGetAvatarPathFromMUC() {
+ std::auto_ptr<AvatarManager> testling = createManager();
+ }
+
+ private:
+ std::auto_ptr<AvatarManager> createManager() {
+ return std::auto_ptr<AvatarManager>(new AvatarManager(stanzaChannel_, iqRouter_, avatarStorage_, mucRegistry_));
+ }
+
+ boost::shared_ptr<Presence> createPresenceWithPhotoHash() {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->setFrom(JID("foo@bar.com/baz"));
+ presence->addPayload(boost::shared_ptr<VCardUpdate>(new VCardUpdate("aef56135bcce35eb24a43fcd684005b4ca286497")));
+ return presence;
+ }
+
+ private:
+ struct DummyMUCRegistry : public MUCRegistry {
+ bool isMUC(const JID& jid) const { return std::find(mucs_.begin(), mucs_.end(), jid) != mucs_.end(); }
+ std::vector<JID> mucs_;
+ };
+ struct DummyAvatarStorage : public AvatarStorage {
+ virtual bool hasAvatar(const String& hash) const { return avatars.find(hash) != avatars.end(); }
+ virtual void addAvatar(const String& hash, const ByteArray& avatar) { avatars[hash] = avatar; }
+ virtual boost::filesystem::path getAvatarPath(const String& hash) const {
+ return boost::filesystem::path("/avatars") / hash.getUTF8String();
+ }
+ std::map<String, ByteArray> avatars;
+ };
+ DummyStanzaChannel* stanzaChannel_;
+ IQRouter* iqRouter_;
+ DummyMUCRegistry* mucRegistry_;
+ DummyAvatarStorage* avatarStorage_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(AvatarManagerTest);
diff --git a/Swiften/Avatars/UnitTest/MockAvatarManager.cpp b/Swiften/Avatars/UnitTest/MockAvatarManager.cpp
new file mode 100644
index 0000000..2d3d48a
--- /dev/null
+++ b/Swiften/Avatars/UnitTest/MockAvatarManager.cpp
@@ -0,0 +1,26 @@
+#include "Swiften/Avatars/UnitTest/MockAvatarManager.h"
+
+namespace Swift {
+
+MockAvatarManager::MockAvatarManager() {
+
+}
+
+MockAvatarManager::~MockAvatarManager() {
+
+}
+
+String MockAvatarManager::getAvatarHash(const JID& jid) const {
+ return jid.toBare();
+}
+
+boost::filesystem::path MockAvatarManager::getAvatarPath(const JID& jid) const {
+ return jid.getResource().getUTF8String();
+}
+
+void MockAvatarManager::setAvatar(const JID&, const ByteArray&) {
+
+}
+
+}
+
diff --git a/Swiften/Avatars/UnitTest/MockAvatarManager.h b/Swiften/Avatars/UnitTest/MockAvatarManager.h
new file mode 100644
index 0000000..416d921
--- /dev/null
+++ b/Swiften/Avatars/UnitTest/MockAvatarManager.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Swiften/Avatars/AvatarManager.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+
+namespace Swift {
+ class MockAvatarManager : public AvatarManager {
+ public:
+ MockAvatarManager();
+ virtual ~MockAvatarManager();
+ virtual String getAvatarHash(const JID&) const;
+ virtual boost::filesystem::path getAvatarPath(const JID&) const;
+ virtual void setAvatar(const JID&, const ByteArray& avatar);
+ private:
+ DummyStanzaChannel channel_;
+ };
+}
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..ab256a4
--- /dev/null
+++ b/Swiften/Base/ByteArray.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#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) {
+ if (n > 0) {
+ data_.resize(n);
+ memcpy(&data_[0], c, n);
+ }
+ }
+
+ const char* getData() const {
+ return data_.empty() ? NULL : &data_[0];
+ }
+
+ char* getData() {
+ return data_.empty() ? NULL : &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 ByteArray operator+(const ByteArray& a, char b) {
+ ByteArray x;
+ x.resize(1);
+ x[0] = b;
+ return a + x;
+ }
+
+ ByteArray& operator+=(const ByteArray& b) {
+ data_.insert(data_.end(), b.data_.begin(), b.data_.end());
+ return *this;
+ }
+
+ 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);
diff --git a/Swiften/Base/Error.cpp b/Swiften/Base/Error.cpp
new file mode 100644
index 0000000..597c155
--- /dev/null
+++ b/Swiften/Base/Error.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Base/Error.h"
+
+namespace Swift {
+
+Error::~Error() {
+}
+
+}
diff --git a/Swiften/Base/Error.h b/Swiften/Base/Error.h
new file mode 100644
index 0000000..4c729ff
--- /dev/null
+++ b/Swiften/Base/Error.h
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace Swift {
+ class Error {
+ public:
+ virtual ~Error();
+ };
+};
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/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/SConscript b/Swiften/Base/SConscript
new file mode 100644
index 0000000..a0984e5
--- /dev/null
+++ b/Swiften/Base/SConscript
@@ -0,0 +1,10 @@
+Import("swiften_env")
+
+objects = swiften_env.StaticObject([
+ "ByteArray.cpp",
+ "Error.cpp",
+ "IDGenerator.cpp",
+ "String.cpp",
+ "sleep.cpp",
+ ])
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/Base/String.cpp b/Swiften/Base/String.cpp
new file mode 100644
index 0000000..cc989f6
--- /dev/null
+++ b/Swiften/Base/String.cpp
@@ -0,0 +1,116 @@
+#include <cassert>
+#include <algorithm>
+
+#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();
+ }
+}
+
+String String::getLowerCase() const {
+ std::string lower(data_);
+ std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
+ return String(lower);
+}
+
+std::vector<String> String::split(char c) const {
+ assert((c & 0x80) == 0);
+ std::vector<String> result;
+ String accumulator;
+ for (size_t i = 0; i < data_.size(); ++i) {
+ if (data_[i] == c) {
+ result.push_back(accumulator);
+ accumulator = "";
+ }
+ else {
+ accumulator += data_[i];
+ }
+ }
+ result.push_back(accumulator);
+ return result;
+}
+
+}
diff --git a/Swiften/Base/String.h b/Swiften/Base/String.h
new file mode 100644
index 0000000..7a6a9cc
--- /dev/null
+++ b/Swiften/Base/String.h
@@ -0,0 +1,131 @@
+#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;
+
+ std::vector<String> split(char c) const;
+
+ size_t getLength() const;
+ String getLowerCase() 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;
+ }
+
+ String& operator=(const String& o) {
+ data_ = o.data_;
+ return *this;
+ }
+
+ bool contains(const String& o) {
+ return data_.find(o.data_) != std::string::npos;
+ }
+
+ char operator[](size_t i) const {
+ return data_[i];
+ }
+
+ 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/ByteArrayTest.cpp b/Swiften/Base/UnitTest/ByteArrayTest.cpp
new file mode 100644
index 0000000..bf893bd
--- /dev/null
+++ b/Swiften/Base/UnitTest/ByteArrayTest.cpp
@@ -0,0 +1,21 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/ByteArray.h"
+
+using namespace Swift;
+
+class ByteArrayTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ByteArrayTest);
+ CPPUNIT_TEST(testGetData_NoData);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetData_NoData() {
+ ByteArray testling;
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<const char*>(NULL), static_cast<const char*>(testling.getData()));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ByteArrayTest);
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/StringTest.cpp b/Swiften/Base/UnitTest/StringTest.cpp
new file mode 100644
index 0000000..87e1a99
--- /dev/null
+++ b/Swiften/Base/UnitTest/StringTest.cpp
@@ -0,0 +1,179 @@
+#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(testGetLowerCase);
+ CPPUNIT_TEST(testSplit);
+ CPPUNIT_TEST(testContains);
+ CPPUNIT_TEST(testContainsFalse);
+ CPPUNIT_TEST(testContainsExact);
+ 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);
+ }
+
+ void testGetLowerCase() {
+ String testling("aBcD e");
+
+ CPPUNIT_ASSERT_EQUAL(String("abcd e"), testling.getLowerCase());
+ }
+
+ void testSplit() {
+ std::vector<String> result = String("abc def ghi").split(' ');
+
+ CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(result.size()));
+ CPPUNIT_ASSERT_EQUAL(String("abc"), result[0]);
+ CPPUNIT_ASSERT_EQUAL(String("def"), result[1]);
+ CPPUNIT_ASSERT_EQUAL(String("ghi"), result[2]);
+ }
+
+ void testContains() {
+ CPPUNIT_ASSERT(String("abcde").contains(String("bcd")));
+ }
+
+ void testContainsFalse() {
+ CPPUNIT_ASSERT(!String("abcde").contains(String("abcdef")));
+ }
+
+ void testContainsExact() {
+ CPPUNIT_ASSERT(String("abcde").contains(String("abcde")));
+ }
+
+};
+
+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..99d0fe6
--- /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..bcebc4c
--- /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/Chat/ChatStateActionProvider.h b/Swiften/Chat/ChatStateActionProvider.h
new file mode 100644
index 0000000..82bed3f
--- /dev/null
+++ b/Swiften/Chat/ChatStateActionProvider.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Swift {
+ class ChatState {
+
+ };
+}
diff --git a/Swiften/Chat/ChatStateMessageSender.cpp b/Swiften/Chat/ChatStateMessageSender.cpp
new file mode 100644
index 0000000..ad1495f
--- /dev/null
+++ b/Swiften/Chat/ChatStateMessageSender.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/Chat/ChatStateMessageSender.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+ChatStateMessageSender::ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact) : contact_(contact) {
+ notifier_ = notifier;
+ stanzaChannel_ = stanzaChannel;
+ notifier_->onChatStateChanged.connect(boost::bind(&ChatStateMessageSender::handleChatStateChanged, this, _1));
+}
+
+void ChatStateMessageSender::handleChatStateChanged(ChatState::ChatStateType state) {
+ boost::shared_ptr<Message> message(new Message());
+ message->setTo(contact_);
+ message->addPayload(boost::shared_ptr<Payload>(new ChatState(state)));
+ stanzaChannel_->sendMessage(message);
+}
+
+}
diff --git a/Swiften/Chat/ChatStateMessageSender.h b/Swiften/Chat/ChatStateMessageSender.h
new file mode 100644
index 0000000..aff0791
--- /dev/null
+++ b/Swiften/Chat/ChatStateMessageSender.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Chat/ChatStateNotifier.h"
+#include "Swiften/Elements/ChatState.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class StanzaChannel;
+ class ChatStateMessageSender {
+ public:
+ ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact);
+ void setContact(const JID& contact) {contact_ = contact;};
+
+ private:
+ void handleChatStateChanged(ChatState::ChatStateType state);
+ ChatStateNotifier* notifier_;
+ StanzaChannel* stanzaChannel_;
+ JID contact_;
+ };
+}
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp
new file mode 100644
index 0000000..7c6560f
--- /dev/null
+++ b/Swiften/Chat/ChatStateNotifier.cpp
@@ -0,0 +1,45 @@
+#include "Swiften/Chat/ChatStateNotifier.h"
+
+namespace Swift {
+
+ChatStateNotifier::ChatStateNotifier() {
+ contactHas85Caps_ = false;
+ isInConversation_ = false;
+ contactHasSentActive_ = false;
+ userIsTyping_ = false;
+}
+
+void ChatStateNotifier::setContactHas85Caps(bool hasCaps) {
+ contactHas85Caps_ = hasCaps;
+}
+
+void ChatStateNotifier::setUserIsTyping() {
+ if (contactShouldReceiveStates() && !userIsTyping_) {
+ userIsTyping_ = true;
+ onChatStateChanged(ChatState::Composing);
+ }
+}
+
+void ChatStateNotifier::userSentMessage() {
+ userIsTyping_ = false;
+}
+
+void ChatStateNotifier::userCancelledNewMessage() {
+ if (userIsTyping_) {
+ userIsTyping_ = false;
+ onChatStateChanged(ChatState::Active);
+ }
+}
+
+void ChatStateNotifier::receivedMessageFromContact(bool hasActiveElement) {
+ isInConversation_ = true;
+ contactHasSentActive_ = hasActiveElement;
+}
+
+bool ChatStateNotifier::contactShouldReceiveStates() {
+ /* So, yes, the XEP says to look at caps, but it also says that once you've
+ heard from the contact, the active state overrides this.*/
+ return contactHasSentActive_ || (contactHas85Caps_ && !isInConversation_);;
+}
+
+}
diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h
new file mode 100644
index 0000000..0ef4255
--- /dev/null
+++ b/Swiften/Chat/ChatStateNotifier.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/ChatState.h"
+
+namespace Swift {
+ class ChatStateNotifier {
+ public:
+ ChatStateNotifier();
+ void setContactHas85Caps(bool hasCaps);
+ void setUserIsTyping();
+ void userSentMessage();
+ void userCancelledNewMessage();
+ void receivedMessageFromContact(bool hasActiveElement);
+ bool contactShouldReceiveStates();
+
+ boost::signal<void (ChatState::ChatStateType)> onChatStateChanged;
+ private:
+ bool contactHas85Caps_;
+ bool isInConversation_;
+ bool contactHasSentActive_;
+ bool userIsTyping_;
+ };
+}
diff --git a/Swiften/Chat/ChatStateTracker.cpp b/Swiften/Chat/ChatStateTracker.cpp
new file mode 100644
index 0000000..3076845
--- /dev/null
+++ b/Swiften/Chat/ChatStateTracker.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Chat/ChatStateTracker.h"
+
+namespace Swift {
+ChatStateTracker::ChatStateTracker() {
+ currentState_ = ChatState::Gone;
+}
+
+void ChatStateTracker::handleMessageReceived(boost::shared_ptr<Message> message) {
+ boost::shared_ptr<ChatState> statePayload = message->getPayload<ChatState>();
+ if (statePayload) {
+ changeState(statePayload->getChatState());;
+ }
+}
+
+void ChatStateTracker::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence>) {
+ if (newPresence->getType() == Presence::Unavailable) {
+ onChatStateChange(ChatState::Gone);
+ }
+}
+
+void ChatStateTracker::changeState(ChatState::ChatStateType state) {
+ if (state != currentState_) {
+ currentState_ = state;
+ onChatStateChange(state);
+ }
+}
+
+}
diff --git a/Swiften/Chat/ChatStateTracker.h b/Swiften/Chat/ChatStateTracker.h
new file mode 100644
index 0000000..e66bbae
--- /dev/null
+++ b/Swiften/Chat/ChatStateTracker.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/ChatState.h"
+
+namespace Swift {
+ class ChatStateTracker {
+ public:
+ ChatStateTracker();
+ void handleMessageReceived(boost::shared_ptr<Message> message);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> oldPresence);
+ boost::signal<void (ChatState::ChatStateType)> onChatStateChange;
+ private:
+ void changeState(ChatState::ChatStateType state);
+ ChatState::ChatStateType currentState_;
+ };
+}
diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
new file mode 100644
index 0000000..44ec9d8
--- /dev/null
+++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
@@ -0,0 +1,132 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+
+#include "Swiften/Chat/ChatStateNotifier.h"
+
+using namespace Swift;
+
+
+class ChatStateMonitor {
+public:
+ ChatStateMonitor(ChatStateNotifier* notifier) {
+ notifier_ = notifier;
+ composingCallCount = 0;
+ activeCallCount = 0;
+ notifier->onChatStateChanged.connect(boost::bind(&ChatStateMonitor::handleChatStateChanged, this, _1));
+ };
+
+ int composingCallCount;
+ int activeCallCount;
+ ChatState::ChatStateType currentState;
+
+private:
+ void handleChatStateChanged(ChatState::ChatStateType newState) {
+ switch (newState) {
+ case ChatState::Composing:
+ composingCallCount++;
+ break;
+ case ChatState::Active:
+ activeCallCount++;
+ break;
+ default:
+ break;
+ }
+ currentState = newState;
+ };
+
+ ChatStateNotifier* notifier_;
+};
+
+class ChatStateNotifierTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ChatStateNotifierTest);
+ CPPUNIT_TEST(testStartTypingReply_CapsNotIncluded);
+ CPPUNIT_TEST(testStartTypingReply_CapsIncluded);
+ CPPUNIT_TEST(testCancelledNewMessage);
+ CPPUNIT_TEST(testContinueTypingReply_CapsIncluded);
+ CPPUNIT_TEST(testContactShouldReceiveStates_CapsOnly);
+ CPPUNIT_TEST(testContactShouldReceiveStates_CapsNorActive);
+ CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOn);
+ CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOff);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ ChatStateNotifier* notifier_;
+ ChatStateMonitor* monitor_;
+
+public:
+ void setUp() {
+ notifier_ = new ChatStateNotifier();
+ monitor_ = new ChatStateMonitor(notifier_);
+ }
+
+ void tearDown() {
+ delete notifier_;
+ delete monitor_;
+ }
+
+ void testStartTypingReply_CapsNotIncluded() {
+ notifier_->setContactHas85Caps(false);
+ notifier_->setUserIsTyping();
+ CPPUNIT_ASSERT_EQUAL(0, monitor_->composingCallCount);
+ }
+
+ void testSendTwoMessages() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ notifier_->userSentMessage();
+ notifier_->setUserIsTyping();
+ notifier_->userSentMessage();
+ CPPUNIT_ASSERT_EQUAL(2, monitor_->composingCallCount);
+ }
+
+ void testCancelledNewMessage() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ notifier_->userCancelledNewMessage();
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->activeCallCount);
+ CPPUNIT_ASSERT_EQUAL(ChatState::Active, monitor_->currentState);
+ }
+
+
+ void testContactShouldReceiveStates_CapsOnly() {
+ notifier_->setContactHas85Caps(true);
+ CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+ }
+
+ void testContactShouldReceiveStates_CapsNorActive() {
+ CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates());
+ }
+
+ void testContactShouldReceiveStates_ActiveOverrideOn() {
+ notifier_->setContactHas85Caps(false);
+ notifier_->receivedMessageFromContact(true);
+ CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+ }
+
+ void testContactShouldReceiveStates_ActiveOverrideOff() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->receivedMessageFromContact(false);
+ CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates());
+ }
+
+
+ void testStartTypingReply_CapsIncluded() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+ }
+
+ void testContinueTypingReply_CapsIncluded() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ notifier_->setUserIsTyping();
+ notifier_->setUserIsTyping();
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+ }
+
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ChatStateNotifierTest);
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
new file mode 100644
index 0000000..c704248
--- /dev/null
+++ b/Swiften/Client/Client.cpp
@@ -0,0 +1,212 @@
+#include "Swiften/Client/Client.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Network/MainBoostIOServiceThread.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/Client/ClientSession.h"
+#include "Swiften/StreamStack/PlatformTLSLayerFactory.h"
+#include "Swiften/Network/Connector.h"
+#include "Swiften/Network/BoostConnectionFactory.h"
+#include "Swiften/Network/BoostTimerFactory.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+#include "Swiften/Session/BasicSessionStream.h"
+
+namespace Swift {
+
+Client::Client(const JID& jid, const String& password) :
+ IQRouter(this), jid_(jid), password_(password) {
+ connectionFactory_ = new BoostConnectionFactory(&MainBoostIOServiceThread::getInstance().getIOService());
+ timerFactory_ = new BoostTimerFactory(&MainBoostIOServiceThread::getInstance().getIOService());
+ tlsLayerFactory_ = new PlatformTLSLayerFactory();
+}
+
+Client::~Client() {
+ if (session_ || connection_) {
+ std::cerr << "Warning: Client not disconnected properly" << std::endl;
+ }
+ delete tlsLayerFactory_;
+ delete timerFactory_;
+ delete connectionFactory_;
+}
+
+bool Client::isAvailable() {
+ return session_;
+}
+
+void Client::connect() {
+ assert(!connector_);
+ connector_ = boost::shared_ptr<Connector>(new Connector(jid_.getDomain(), &resolver_, connectionFactory_, timerFactory_));
+ connector_->onConnectFinished.connect(boost::bind(&Client::handleConnectorFinished, this, _1));
+ connector_->setTimeoutMilliseconds(60*1000);
+ connector_->start();
+}
+
+void Client::handleConnectorFinished(boost::shared_ptr<Connection> connection) {
+ // TODO: Add domain name resolver error
+ connector_.reset();
+ if (!connection) {
+ onError(ClientError::ConnectionError);
+ }
+ else {
+ assert(!connection_);
+ connection_ = connection;
+
+ assert(!sessionStream_);
+ sessionStream_ = boost::shared_ptr<BasicSessionStream>(new BasicSessionStream(connection_, &payloadParserFactories_, &payloadSerializers_, tlsLayerFactory_, timerFactory_));
+ if (!certificate_.isEmpty()) {
+ sessionStream_->setTLSCertificate(PKCS12Certificate(certificate_, password_));
+ }
+ sessionStream_->onDataRead.connect(boost::bind(&Client::handleDataRead, this, _1));
+ sessionStream_->onDataWritten.connect(boost::bind(&Client::handleDataWritten, this, _1));
+ sessionStream_->initialize();
+
+ session_ = ClientSession::create(jid_, sessionStream_);
+ session_->onInitialized.connect(boost::bind(boost::ref(onConnected)));
+ session_->onFinished.connect(boost::bind(&Client::handleSessionFinished, this, _1));
+ session_->onNeedCredentials.connect(boost::bind(&Client::handleNeedCredentials, this));
+ session_->onElementReceived.connect(boost::bind(&Client::handleElement, this, _1));
+ session_->start();
+ }
+}
+
+void Client::disconnect() {
+ if (session_) {
+ session_->finish();
+ }
+ else {
+ closeConnection();
+ }
+}
+
+void Client::closeConnection() {
+ if (sessionStream_) {
+ sessionStream_.reset();
+ }
+ if (connection_) {
+ connection_->disconnect();
+ connection_.reset();
+ }
+}
+
+void Client::send(boost::shared_ptr<Stanza> stanza) {
+ if (!isAvailable()) {
+ std::cerr << "Warning: Client: Trying to send a stanza while disconnected." << std::endl;
+ return;
+ }
+ 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::handleSessionFinished(boost::shared_ptr<Error> error) {
+ session_.reset();
+ closeConnection();
+ if (error) {
+ ClientError clientError;
+ if (boost::shared_ptr<ClientSession::Error> actualError = boost::dynamic_pointer_cast<ClientSession::Error>(error)) {
+ switch(actualError->type) {
+ case ClientSession::Error::AuthenticationFailedError:
+ clientError = ClientError(ClientError::AuthenticationFailedError);
+ break;
+ case ClientSession::Error::CompressionFailedError:
+ clientError = ClientError(ClientError::CompressionFailedError);
+ break;
+ case ClientSession::Error::ServerVerificationFailedError:
+ clientError = ClientError(ClientError::ServerVerificationFailedError);
+ break;
+ case ClientSession::Error::NoSupportedAuthMechanismsError:
+ clientError = ClientError(ClientError::NoSupportedAuthMechanismsError);
+ break;
+ case ClientSession::Error::UnexpectedElementError:
+ clientError = ClientError(ClientError::UnexpectedElementError);
+ break;
+ case ClientSession::Error::ResourceBindError:
+ clientError = ClientError(ClientError::ResourceBindError);
+ break;
+ case ClientSession::Error::SessionStartError:
+ clientError = ClientError(ClientError::SessionStartError);
+ break;
+ case ClientSession::Error::TLSError:
+ clientError = ClientError(ClientError::TLSError);
+ break;
+ case ClientSession::Error::TLSClientCertificateError:
+ clientError = ClientError(ClientError::ClientCertificateError);
+ break;
+ }
+ }
+ else if (boost::shared_ptr<SessionStream::Error> actualError = boost::dynamic_pointer_cast<SessionStream::Error>(error)) {
+ switch(actualError->type) {
+ case SessionStream::Error::ParseError:
+ clientError = ClientError(ClientError::XMLError);
+ break;
+ case SessionStream::Error::TLSError:
+ clientError = ClientError(ClientError::TLSError);
+ break;
+ case SessionStream::Error::InvalidTLSCertificateError:
+ clientError = ClientError(ClientError::ClientCertificateLoadError);
+ break;
+ case SessionStream::Error::ConnectionReadError:
+ clientError = ClientError(ClientError::ConnectionReadError);
+ break;
+ case SessionStream::Error::ConnectionWriteError:
+ clientError = ClientError(ClientError::ConnectionWriteError);
+ break;
+ }
+ }
+ onError(clientError);
+ }
+}
+
+void Client::handleNeedCredentials() {
+ assert(session_);
+ session_->sendCredentials(password_);
+}
+
+void Client::handleDataRead(const String& data) {
+ onDataRead(data);
+}
+
+void Client::handleDataWritten(const String& data) {
+ onDataWritten(data);
+}
+
+}
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
new file mode 100644
index 0000000..444c136
--- /dev/null
+++ b/Swiften/Client/Client.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Network/PlatformDomainNameResolver.h"
+#include "Swiften/Base/Error.h"
+#include "Swiften/Client/ClientSession.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 TimerFactory;
+ class ClientSession;
+ class BasicSessionStream;
+ class Connector;
+
+ class Client : public StanzaChannel, public IQRouter, public boost::bsignals::trackable {
+ public:
+ Client(const JID& jid, const String& password);
+ ~Client();
+
+ void setCertificate(const String& certificate);
+
+ void connect();
+ void disconnect();
+
+ bool isAvailable();
+
+ 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 (const ClientError&)> onError;
+ boost::signal<void ()> onConnected;
+ boost::signal<void (const String&)> onDataRead;
+ boost::signal<void (const String&)> onDataWritten;
+
+ private:
+ void handleConnectorFinished(boost::shared_ptr<Connection>);
+ void send(boost::shared_ptr<Stanza>);
+ virtual String getNewIQID();
+ void handleElement(boost::shared_ptr<Element>);
+ void handleSessionFinished(boost::shared_ptr<Error>);
+ void handleNeedCredentials();
+ void handleDataRead(const String&);
+ void handleDataWritten(const String&);
+
+ void closeConnection();
+
+ private:
+ PlatformDomainNameResolver resolver_;
+ JID jid_;
+ String password_;
+ IDGenerator idGenerator_;
+ boost::shared_ptr<Connector> connector_;
+ ConnectionFactory* connectionFactory_;
+ TimerFactory* timerFactory_;
+ TLSLayerFactory* tlsLayerFactory_;
+ FullPayloadParserFactoryCollection payloadParserFactories_;
+ FullPayloadSerializerCollection payloadSerializers_;
+ boost::shared_ptr<Connection> connection_;
+ boost::shared_ptr<BasicSessionStream> sessionStream_;
+ boost::shared_ptr<ClientSession> session_;
+ String certificate_;
+ };
+}
diff --git a/Swiften/Client/ClientError.h b/Swiften/Client/ClientError.h
new file mode 100644
index 0000000..a0557d4
--- /dev/null
+++ b/Swiften/Client/ClientError.h
@@ -0,0 +1,32 @@
+#pragma once
+
+namespace Swift {
+ class ClientError {
+ public:
+ enum Type {
+ UnknownError,
+ DomainNameResolveError,
+ ConnectionError,
+ ConnectionReadError,
+ ConnectionWriteError,
+ XMLError,
+ AuthenticationFailedError,
+ CompressionFailedError,
+ ServerVerificationFailedError,
+ NoSupportedAuthMechanismsError,
+ UnexpectedElementError,
+ ResourceBindError,
+ SessionStartError,
+ TLSError,
+ ClientCertificateLoadError,
+ ClientCertificateError
+ };
+
+ ClientError(Type type = UnknownError) : type_(type) {}
+
+ Type getType() const { return type_; }
+
+ private:
+ Type type_;
+ };
+}
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
new file mode 100644
index 0000000..16bda40
--- /dev/null
+++ b/Swiften/Client/ClientSession.cpp
@@ -0,0 +1,276 @@
+#include "Swiften/Client/ClientSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Elements/ProtocolHeader.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/AuthChallenge.h"
+#include "Swiften/Elements/AuthResponse.h"
+#include "Swiften/Elements/Compressed.h"
+#include "Swiften/Elements/CompressFailure.h"
+#include "Swiften/Elements/CompressRequest.h"
+#include "Swiften/Elements/StartSession.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/ResourceBind.h"
+#include "Swiften/SASL/PLAINClientAuthenticator.h"
+#include "Swiften/SASL/SCRAMSHA1ClientAuthenticator.h"
+#include "Swiften/Session/SessionStream.h"
+
+namespace Swift {
+
+ClientSession::ClientSession(
+ const JID& jid,
+ boost::shared_ptr<SessionStream> stream) :
+ localJID(jid),
+ state(Initial),
+ stream(stream),
+ needSessionStart(false),
+ authenticator(NULL) {
+}
+
+ClientSession::~ClientSession() {
+}
+
+void ClientSession::start() {
+ stream->onStreamStartReceived.connect(boost::bind(&ClientSession::handleStreamStart, shared_from_this(), _1));
+ stream->onElementReceived.connect(boost::bind(&ClientSession::handleElement, shared_from_this(), _1));
+ stream->onError.connect(boost::bind(&ClientSession::handleStreamError, shared_from_this(), _1));
+ stream->onTLSEncrypted.connect(boost::bind(&ClientSession::handleTLSEncrypted, shared_from_this()));
+
+ assert(state == Initial);
+ state = WaitingForStreamStart;
+ sendStreamHeader();
+}
+
+void ClientSession::sendStreamHeader() {
+ ProtocolHeader header;
+ header.setTo(getRemoteJID());
+ stream->writeHeader(header);
+}
+
+void ClientSession::sendElement(boost::shared_ptr<Element> element) {
+ stream->writeElement(element);
+}
+
+void ClientSession::handleStreamStart(const ProtocolHeader&) {
+ checkState(WaitingForStreamStart);
+ state = Negotiating;
+}
+
+void ClientSession::handleElement(boost::shared_ptr<Element> element) {
+ if (getState() == Initialized) {
+ onElementReceived(element);
+ }
+ else if (StreamFeatures* streamFeatures = dynamic_cast<StreamFeatures*>(element.get())) {
+ if (!checkState(Negotiating)) {
+ return;
+ }
+
+ if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption()) {
+ state = WaitingForEncrypt;
+ stream->writeElement(boost::shared_ptr<StartTLSRequest>(new StartTLSRequest()));
+ }
+ else if (streamFeatures->hasCompressionMethod("zlib")) {
+ state = Compressing;
+ stream->writeElement(boost::shared_ptr<CompressRequest>(new CompressRequest("zlib")));
+ }
+ else if (streamFeatures->hasAuthenticationMechanisms()) {
+ if (stream->hasTLSCertificate()) {
+ if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) {
+ state = Authenticating;
+ stream->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", "")));
+ }
+ else {
+ finishSession(Error::TLSClientCertificateError);
+ }
+ }
+ else if (streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1")) {
+ // FIXME: Use a real nonce
+ authenticator = new SCRAMSHA1ClientAuthenticator("ClientNonce");
+ state = WaitingForCredentials;
+ onNeedCredentials();
+ }
+ else if (streamFeatures->hasAuthenticationMechanism("PLAIN")) {
+ authenticator = new PLAINClientAuthenticator();
+ state = WaitingForCredentials;
+ onNeedCredentials();
+ }
+ else {
+ finishSession(Error::NoSupportedAuthMechanismsError);
+ }
+ }
+ else {
+ // Start the session
+ stream->setWhitespacePingEnabled(true);
+
+ if (streamFeatures->hasSession()) {
+ needSessionStart = true;
+ }
+
+ if (streamFeatures->hasResourceBind()) {
+ state = BindingResource;
+ boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind());
+ if (!localJID.getResource().isEmpty()) {
+ resourceBind->setResource(localJID.getResource());
+ }
+ stream->writeElement(IQ::createRequest(IQ::Set, JID(), "session-bind", resourceBind));
+ }
+ else if (needSessionStart) {
+ sendSessionStart();
+ }
+ else {
+ state = Initialized;
+ onInitialized();
+ }
+ }
+ }
+ else if (boost::dynamic_pointer_cast<Compressed>(element)) {
+ checkState(Compressing);
+ state = WaitingForStreamStart;
+ stream->addZLibCompression();
+ stream->resetXMPPParser();
+ sendStreamHeader();
+ }
+ else if (boost::dynamic_pointer_cast<CompressFailure>(element)) {
+ finishSession(Error::CompressionFailedError);
+ }
+ else if (AuthChallenge* challenge = dynamic_cast<AuthChallenge*>(element.get())) {
+ checkState(Authenticating);
+ assert(authenticator);
+ if (authenticator->setChallenge(challenge->getValue())) {
+ stream->writeElement(boost::shared_ptr<AuthResponse>(new AuthResponse(authenticator->getResponse())));
+ }
+ else {
+ finishSession(Error::AuthenticationFailedError);
+ }
+ }
+ else if (AuthSuccess* authSuccess = dynamic_cast<AuthSuccess*>(element.get())) {
+ checkState(Authenticating);
+ if (authenticator && !authenticator->setChallenge(authSuccess->getValue())) {
+ finishSession(Error::ServerVerificationFailedError);
+ }
+ else {
+ state = WaitingForStreamStart;
+ delete authenticator;
+ authenticator = NULL;
+ stream->resetXMPPParser();
+ sendStreamHeader();
+ }
+ }
+ else if (dynamic_cast<AuthFailure*>(element.get())) {
+ delete authenticator;
+ authenticator = NULL;
+ finishSession(Error::AuthenticationFailedError);
+ }
+ else if (dynamic_cast<TLSProceed*>(element.get())) {
+ checkState(WaitingForEncrypt);
+ state = Encrypting;
+ stream->addTLSEncryption();
+ }
+ else if (dynamic_cast<StartTLSFailure*>(element.get())) {
+ finishSession(Error::TLSError);
+ }
+ else if (IQ* iq = dynamic_cast<IQ*>(element.get())) {
+ if (state == BindingResource) {
+ boost::shared_ptr<ResourceBind> resourceBind(iq->getPayload<ResourceBind>());
+ if (iq->getType() == IQ::Error && iq->getID() == "session-bind") {
+ finishSession(Error::ResourceBindError);
+ }
+ else if (!resourceBind) {
+ finishSession(Error::UnexpectedElementError);
+ }
+ else if (iq->getType() == IQ::Result) {
+ localJID = resourceBind->getJID();
+ if (!localJID.isValid()) {
+ finishSession(Error::ResourceBindError);
+ }
+ if (needSessionStart) {
+ sendSessionStart();
+ }
+ else {
+ state = Initialized;
+ }
+ }
+ else {
+ finishSession(Error::UnexpectedElementError);
+ }
+ }
+ else if (state == StartingSession) {
+ if (iq->getType() == IQ::Result) {
+ state = Initialized;
+ onInitialized();
+ }
+ else if (iq->getType() == IQ::Error) {
+ finishSession(Error::SessionStartError);
+ }
+ else {
+ finishSession(Error::UnexpectedElementError);
+ }
+ }
+ else {
+ finishSession(Error::UnexpectedElementError);
+ }
+ }
+ else {
+ // FIXME Not correct?
+ state = Initialized;
+ onInitialized();
+ }
+}
+
+void ClientSession::sendSessionStart() {
+ state = StartingSession;
+ stream->writeElement(IQ::createRequest(IQ::Set, JID(), "session-start", boost::shared_ptr<StartSession>(new StartSession())));
+}
+
+bool ClientSession::checkState(State state) {
+ if (state != state) {
+ finishSession(Error::UnexpectedElementError);
+ return false;
+ }
+ return true;
+}
+
+void ClientSession::sendCredentials(const String& password) {
+ assert(WaitingForCredentials);
+ state = Authenticating;
+ authenticator->setCredentials(localJID.getNode(), password);
+ stream->writeElement(boost::shared_ptr<AuthRequest>(new AuthRequest(authenticator->getName(), authenticator->getResponse())));
+}
+
+void ClientSession::handleTLSEncrypted() {
+ checkState(WaitingForEncrypt);
+ state = WaitingForStreamStart;
+ stream->resetXMPPParser();
+ sendStreamHeader();
+}
+
+void ClientSession::handleStreamError(boost::shared_ptr<Swift::Error> error) {
+ finishSession(error);
+}
+
+void ClientSession::finish() {
+ if (stream->isAvailable()) {
+ stream->writeFooter();
+ }
+ finishSession(boost::shared_ptr<Error>());
+}
+
+void ClientSession::finishSession(Error::Type error) {
+ finishSession(boost::shared_ptr<Swift::ClientSession::Error>(new Swift::ClientSession::Error(error)));
+}
+
+void ClientSession::finishSession(boost::shared_ptr<Swift::Error> error) {
+ state = Finished;
+ stream->setWhitespacePingEnabled(false);
+ onFinished(error);
+}
+
+
+}
diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h
new file mode 100644
index 0000000..685672e
--- /dev/null
+++ b/Swiften/Client/ClientSession.h
@@ -0,0 +1,100 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Base/Error.h"
+#include "Swiften/Session/SessionStream.h"
+#include "Swiften/Session/BasicSessionStream.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class ClientAuthenticator;
+
+ class ClientSession : public boost::enable_shared_from_this<ClientSession> {
+ public:
+ enum State {
+ Initial,
+ WaitingForStreamStart,
+ Negotiating,
+ Compressing,
+ WaitingForEncrypt,
+ Encrypting,
+ WaitingForCredentials,
+ Authenticating,
+ BindingResource,
+ StartingSession,
+ Initialized,
+ Finished
+ };
+
+ struct Error : public Swift::Error {
+ enum Type {
+ AuthenticationFailedError,
+ CompressionFailedError,
+ ServerVerificationFailedError,
+ NoSupportedAuthMechanismsError,
+ UnexpectedElementError,
+ ResourceBindError,
+ SessionStartError,
+ TLSClientCertificateError,
+ TLSError,
+ } type;
+ Error(Type type) : type(type) {}
+ };
+
+ ~ClientSession();
+ static boost::shared_ptr<ClientSession> create(const JID& jid, boost::shared_ptr<SessionStream> stream) {
+ return boost::shared_ptr<ClientSession>(new ClientSession(jid, stream));
+ }
+
+ State getState() const {
+ return state;
+ }
+
+ void start();
+ void finish();
+
+ void sendCredentials(const String& password);
+ void sendElement(boost::shared_ptr<Element> element);
+
+ public:
+ boost::signal<void ()> onNeedCredentials;
+ boost::signal<void ()> onInitialized;
+ boost::signal<void (boost::shared_ptr<Swift::Error>)> onFinished;
+ boost::signal<void (boost::shared_ptr<Element>)> onElementReceived;
+
+ private:
+ ClientSession(
+ const JID& jid,
+ boost::shared_ptr<SessionStream>);
+
+ void finishSession(Error::Type error);
+ void finishSession(boost::shared_ptr<Swift::Error> error);
+
+ JID getRemoteJID() const {
+ return JID("", localJID.getDomain());
+ }
+
+ void sendStreamHeader();
+ void sendSessionStart();
+
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamStart(const ProtocolHeader&);
+ void handleStreamError(boost::shared_ptr<Swift::Error>);
+
+ void handleTLSEncrypted();
+
+ bool checkState(State);
+
+ private:
+ JID localJID;
+ State state;
+ boost::shared_ptr<SessionStream> stream;
+ bool needSessionStart;
+ ClientAuthenticator* authenticator;
+ };
+}
diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h
new file mode 100644
index 0000000..85224c8
--- /dev/null
+++ b/Swiften/Client/ClientXMLTracer.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Swiften/Client/Client.h"
+
+namespace Swift {
+ class ClientXMLTracer {
+ public:
+ ClientXMLTracer(Client* client) {
+ client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, '<', _1));
+ client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, '>', _1));
+ }
+
+ private:
+ static void printData(char direction, const String& data) {
+ printLine(direction);
+ std::cerr << data << std::endl;
+ }
+
+ static void printLine(char c) {
+ for (unsigned int i = 0; i < 80; ++i) {
+ std::cerr << c;
+ }
+ std::cerr << std::endl;
+ }
+ };
+}
diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h
new file mode 100644
index 0000000..d618167
--- /dev/null
+++ b/Swiften/Client/DummyStanzaChannel.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <vector>
+
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+ class DummyStanzaChannel : public StanzaChannel {
+ public:
+ DummyStanzaChannel() {}
+
+ virtual void sendStanza(boost::shared_ptr<Stanza> stanza) {
+ sentStanzas.push_back(stanza);
+ }
+
+ virtual void sendIQ(boost::shared_ptr<IQ> iq) {
+ sentStanzas.push_back(iq);
+ }
+
+ virtual void sendMessage(boost::shared_ptr<Message> message) {
+ sentStanzas.push_back(message);
+ }
+
+ virtual void sendPresence(boost::shared_ptr<Presence> presence) {
+ sentStanzas.push_back(presence);
+ }
+
+ virtual String getNewIQID() {
+ return "test-id";
+ }
+
+ virtual bool isAvailable() {
+ return true;
+ }
+
+ std::vector<boost::shared_ptr<Stanza> > sentStanzas;
+ };
+}
diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h
new file mode 100644
index 0000000..a0e291d
--- /dev/null
+++ b/Swiften/Client/StanzaChannel.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#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;
+ virtual bool isAvailable() = 0;
+
+ boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;
+ boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived;
+ };
+}
diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp
new file mode 100644
index 0000000..e035ba3
--- /dev/null
+++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp
@@ -0,0 +1,498 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <deque>
+#include <boost/bind.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Session/SessionStream.h"
+#include "Swiften/Client/ClientSession.h"
+#include "Swiften/Elements/StartTLSRequest.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/TLSProceed.h"
+#include "Swiften/Elements/StartTLSFailure.h"
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Elements/AuthFailure.h"
+
+using namespace Swift;
+
+class ClientSessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ClientSessionTest);
+ CPPUNIT_TEST(testStart_Error);
+ CPPUNIT_TEST(testStartTLS);
+ CPPUNIT_TEST(testStartTLS_ServerError);
+ CPPUNIT_TEST(testStartTLS_ConnectError);
+ 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:
+ void setUp() {
+ server = boost::shared_ptr<MockSessionStream>(new MockSessionStream());
+ sessionFinishedReceived = false;
+ needCredentials = false;
+ }
+
+ void testStart_Error() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->breakConnection();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
+ CPPUNIT_ASSERT(sessionFinishedReceived);
+ CPPUNIT_ASSERT(sessionFinishedError);
+ }
+
+ void testStartTLS() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ server->sendStreamFeaturesWithStartTLS();
+ server->receiveStartTLS();
+ CPPUNIT_ASSERT(!server->tlsEncrypted);
+ server->sendTLSProceed();
+ CPPUNIT_ASSERT(server->tlsEncrypted);
+ server->onTLSEncrypted();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ }
+
+ void testStartTLS_ServerError() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ server->sendStreamFeaturesWithStartTLS();
+ server->receiveStartTLS();
+ server->sendTLSFailure();
+
+ CPPUNIT_ASSERT(!server->tlsEncrypted);
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
+ CPPUNIT_ASSERT(sessionFinishedReceived);
+ CPPUNIT_ASSERT(sessionFinishedError);
+ }
+
+ void testStartTLS_ConnectError() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ server->sendStreamFeaturesWithStartTLS();
+ server->receiveStartTLS();
+ server->sendTLSProceed();
+ server->breakTLS();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
+ CPPUNIT_ASSERT(sessionFinishedReceived);
+ CPPUNIT_ASSERT(sessionFinishedError);
+ }
+
+ void testAuthenticate() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ server->sendStreamFeaturesWithPLAINAuthentication();
+ CPPUNIT_ASSERT(needCredentials);
+ CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState());
+ session->sendCredentials("mypass");
+ server->receiveAuthRequest("PLAIN");
+ server->sendAuthSuccess();
+ server->receiveStreamStart();
+ }
+
+ void testAuthenticate_Unauthorized() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ server->sendStreamFeaturesWithPLAINAuthentication();
+ CPPUNIT_ASSERT(needCredentials);
+ CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState());
+ session->sendCredentials("mypass");
+ server->receiveAuthRequest("PLAIN");
+ server->sendAuthFailure();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
+ CPPUNIT_ASSERT(sessionFinishedReceived);
+ CPPUNIT_ASSERT(sessionFinishedError);
+ }
+
+ void testAuthenticate_NoValidAuthMechanisms() {
+ boost::shared_ptr<ClientSession> session(createSession());
+ session->start();
+ server->receiveStreamStart();
+ server->sendStreamStart();
+ server->sendStreamFeaturesWithUnknownAuthentication();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
+ CPPUNIT_ASSERT(sessionFinishedReceived);
+ CPPUNIT_ASSERT(sessionFinishedError);
+ }
+
+ private:
+ boost::shared_ptr<ClientSession> createSession() {
+ boost::shared_ptr<ClientSession> session = ClientSession::create(JID("me@foo.com"), server);
+ session->onFinished.connect(boost::bind(&ClientSessionTest::handleSessionFinished, this, _1));
+ session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::handleSessionNeedCredentials, this));
+ return session;
+ }
+
+ void handleSessionFinished(boost::shared_ptr<Error> error) {
+ sessionFinishedReceived = true;
+ sessionFinishedError = error;
+ }
+
+ void handleSessionNeedCredentials() {
+ needCredentials = true;
+ }
+
+ class MockSessionStream : public SessionStream {
+ public:
+ struct Event {
+ Event(boost::shared_ptr<Element> element) : element(element), footer(false) {}
+ Event(const ProtocolHeader& header) : header(header), footer(false) {}
+ Event() : footer(true) {}
+
+ boost::shared_ptr<Element> element;
+ boost::optional<ProtocolHeader> header;
+ bool footer;
+ };
+
+ MockSessionStream() : available(true), canTLSEncrypt(true), tlsEncrypted(false), compressed(false), whitespacePingEnabled(false), resetCount(0) {
+ }
+
+ virtual bool isAvailable() {
+ return available;
+ }
+
+ virtual void writeHeader(const ProtocolHeader& header) {
+ receivedEvents.push_back(Event(header));
+ }
+
+ virtual void writeFooter() {
+ receivedEvents.push_back(Event());
+ }
+
+ virtual void writeElement(boost::shared_ptr<Element> element) {
+ receivedEvents.push_back(Event(element));
+ }
+
+ virtual bool supportsTLSEncryption() {
+ return canTLSEncrypt;
+ }
+
+ virtual void addTLSEncryption() {
+ tlsEncrypted = true;
+ }
+
+ virtual void addZLibCompression() {
+ compressed = true;
+ }
+
+ virtual void setWhitespacePingEnabled(bool enabled) {
+ whitespacePingEnabled = enabled;
+ }
+
+ virtual void resetXMPPParser() {
+ resetCount++;
+ }
+
+ void breakConnection() {
+ onError(boost::shared_ptr<SessionStream::Error>(new SessionStream::Error(SessionStream::Error::ConnectionReadError)));
+ }
+
+ void breakTLS() {
+ onError(boost::shared_ptr<SessionStream::Error>(new SessionStream::Error(SessionStream::Error::TLSError)));
+ }
+
+
+ void sendStreamStart() {
+ ProtocolHeader header;
+ header.setTo("foo.com");
+ return onStreamStartReceived(header);
+ }
+
+ void sendStreamFeaturesWithStartTLS() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->setHasStartTLS();
+ onElementReceived(streamFeatures);
+ }
+
+ void sendTLSProceed() {
+ onElementReceived(boost::shared_ptr<TLSProceed>(new TLSProceed()));
+ }
+
+ void sendTLSFailure() {
+ onElementReceived(boost::shared_ptr<StartTLSFailure>(new StartTLSFailure()));
+ }
+
+ void sendStreamFeaturesWithPLAINAuthentication() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->addAuthenticationMechanism("PLAIN");
+ onElementReceived(streamFeatures);
+ }
+
+ void sendStreamFeaturesWithUnknownAuthentication() {
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->addAuthenticationMechanism("UNKNOWN");
+ onElementReceived(streamFeatures);
+ }
+
+ void sendAuthSuccess() {
+ onElementReceived(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
+ }
+
+ void sendAuthFailure() {
+ onElementReceived(boost::shared_ptr<AuthFailure>(new AuthFailure()));
+ }
+
+ void receiveStreamStart() {
+ Event event = popEvent();
+ CPPUNIT_ASSERT(event.header);
+ }
+
+ void receiveStartTLS() {
+ Event event = popEvent();
+ CPPUNIT_ASSERT(event.element);
+ CPPUNIT_ASSERT(boost::dynamic_pointer_cast<StartTLSRequest>(event.element));
+ }
+
+ void receiveAuthRequest(const String& mech) {
+ Event event = popEvent();
+ CPPUNIT_ASSERT(event.element);
+ boost::shared_ptr<AuthRequest> request(boost::dynamic_pointer_cast<AuthRequest>(event.element));
+ CPPUNIT_ASSERT(request);
+ CPPUNIT_ASSERT_EQUAL(mech, request->getMechanism());
+ }
+
+ Event popEvent() {
+ CPPUNIT_ASSERT(receivedEvents.size() > 0);
+ Event event = receivedEvents.front();
+ receivedEvents.pop_front();
+ return event;
+ }
+
+ bool available;
+ bool canTLSEncrypt;
+ bool tlsEncrypted;
+ bool compressed;
+ bool whitespacePingEnabled;
+ int resetCount;
+ std::deque<Event> receivedEvents;
+ };
+
+ boost::shared_ptr<MockSessionStream> server;
+ bool sessionFinishedReceived;
+ bool needCredentials;
+ boost::shared_ptr<Error> sessionFinishedError;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ClientSessionTest);
+
+#if 0
+ void testAuthenticate() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::setNeedCredentials, this));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithAuthentication();
+ session->startSession();
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState());
+ CPPUNIT_ASSERT(needCredentials_);
+
+ getMockServer()->expectAuth("me", "mypass");
+ getMockServer()->sendAuthSuccess();
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ session->sendCredentials("mypass");
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Authenticating, session->getState());
+ processEvents();
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Negotiating, session->getState());
+ }
+
+ void testAuthenticate_Unauthorized() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithAuthentication();
+ session->startSession();
+ processEvents();
+
+ getMockServer()->expectAuth("me", "mypass");
+ getMockServer()->sendAuthFailure();
+ session->sendCredentials("mypass");
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(ClientSession::AuthenticationFailedError, *session->getError());
+ }
+
+ void testAuthenticate_NoValidAuthMechanisms() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithUnsupportedAuthentication();
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(ClientSession::NoSupportedAuthMechanismsError, *session->getError());
+ }
+
+ void testResourceBind() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("Bar", "session-bind");
+ // FIXME: Check CPPUNIT_ASSERT_EQUAL(ClientSession::BindingResource, session->getState());
+ getMockServer()->sendResourceBindResponse("me@foo.com/Bar", "session-bind");
+ session->startSession();
+
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState());
+ CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/Bar"), session->getLocalJID());
+ }
+
+ void testResourceBind_ChangeResource() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("Bar", "session-bind");
+ getMockServer()->sendResourceBindResponse("me@foo.com/Bar123", "session-bind");
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState());
+ CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/Bar123"), session->getLocalJID());
+ }
+
+ void testResourceBind_EmptyResource() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("", "session-bind");
+ getMockServer()->sendResourceBindResponse("me@foo.com/NewResource", "session-bind");
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState());
+ CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/NewResource"), session->getLocalJID());
+ }
+
+ void testResourceBind_Error() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithResourceBind();
+ getMockServer()->expectResourceBind("", "session-bind");
+ getMockServer()->sendError("session-bind");
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(ClientSession::ResourceBindError, *session->getError());
+ }
+
+ void testSessionStart() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onSessionStarted.connect(boost::bind(&ClientSessionTest::setSessionStarted, this));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithSession();
+ getMockServer()->expectSessionStart("session-start");
+ // FIXME: Check CPPUNIT_ASSERT_EQUAL(ClientSession::StartingSession, session->getState());
+ getMockServer()->sendSessionStartResponse("session-start");
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState());
+ CPPUNIT_ASSERT(sessionStarted_);
+ }
+
+ void testSessionStart_Error() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeaturesWithSession();
+ getMockServer()->expectSessionStart("session-start");
+ getMockServer()->sendError("session-start");
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState());
+ CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStartError, *session->getError());
+ }
+
+ void testSessionStart_AfterResourceBind() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onSessionStarted.connect(boost::bind(&ClientSessionTest::setSessionStarted, this));
+ 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");
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState());
+ CPPUNIT_ASSERT(sessionStarted_);
+ }
+
+ void testWhitespacePing() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeatures();
+ session->startSession();
+ processEvents();
+ CPPUNIT_ASSERT(session->getWhitespacePingLayer());
+ }
+
+ void testReceiveElementAfterSessionStarted() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeatures();
+ session->startSession();
+ processEvents();
+
+ getMockServer()->expectMessage();
+ session->sendElement(boost::shared_ptr<Message>(new Message()));
+ }
+
+ void testSendElement() {
+ boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
+ session->onElementReceived.connect(boost::bind(&ClientSessionTest::addReceivedElement, this, _1));
+ getMockServer()->expectStreamStart();
+ getMockServer()->sendStreamStart();
+ getMockServer()->sendStreamFeatures();
+ getMockServer()->sendMessage();
+ session->startSession();
+ processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(receivedElements_.size()));
+ CPPUNIT_ASSERT(boost::dynamic_pointer_cast<Message>(receivedElements_[0]));
+ }
+#endif
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/Disco/CapsInfoGenerator.cpp b/Swiften/Disco/CapsInfoGenerator.cpp
new file mode 100644
index 0000000..8d9e407
--- /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::getHash(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/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/Elements/AuthChallenge.h b/Swiften/Elements/AuthChallenge.h
new file mode 100644
index 0000000..b8a9297
--- /dev/null
+++ b/Swiften/Elements/AuthChallenge.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class AuthChallenge : public Element {
+ public:
+ AuthChallenge(const ByteArray& value = "") : value(value) {
+ }
+
+ const ByteArray& getValue() const {
+ return value;
+ }
+
+ void setValue(const ByteArray& value) {
+ this->value = value;
+ }
+
+ private:
+ ByteArray value;
+ };
+}
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/AuthResponse.h b/Swiften/Elements/AuthResponse.h
new file mode 100644
index 0000000..8a0679f
--- /dev/null
+++ b/Swiften/Elements/AuthResponse.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class AuthResponse : public Element {
+ public:
+ AuthResponse(const ByteArray& value = "") : value(value) {
+ }
+
+ const ByteArray& getValue() const {
+ return value;
+ }
+
+ void setValue(const ByteArray& value) {
+ this->value = value;
+ }
+
+ private:
+ ByteArray value;
+ };
+}
diff --git a/Swiften/Elements/AuthSuccess.h b/Swiften/Elements/AuthSuccess.h
new file mode 100644
index 0000000..1db1729
--- /dev/null
+++ b/Swiften/Elements/AuthSuccess.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class AuthSuccess : public Element {
+ public:
+ AuthSuccess() {}
+
+ const ByteArray& getValue() const {
+ return value;
+ }
+
+ void setValue(const ByteArray& value) {
+ this->value = value;
+ }
+
+ private:
+ ByteArray value;
+ };
+}
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/ChatState.h b/Swiften/Elements/ChatState.h
new file mode 100644
index 0000000..3378cc3
--- /dev/null
+++ b/Swiften/Elements/ChatState.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class ChatState : public Payload {
+ public:
+ enum ChatStateType {Active, Composing, Paused, Inactive, Gone};
+ ChatState(ChatStateType state = Active) {
+ state_ = state;
+ }
+
+ ChatStateType getChatState() { return state_; }
+ void setChatState(ChatStateType state) {state_ = state;}
+
+ private:
+ ChatStateType state_;
+ };
+}
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/ErrorPayload.h b/Swiften/Elements/ErrorPayload.h
new file mode 100644
index 0000000..32fd067
--- /dev/null
+++ b/Swiften/Elements/ErrorPayload.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class ErrorPayload : 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
+ };
+
+ ErrorPayload(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_;
+ };
+}
diff --git a/Swiften/Elements/IQ.cpp b/Swiften/Elements/IQ.cpp
new file mode 100644
index 0000000..53dec53
--- /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, ErrorPayload::Condition condition, ErrorPayload::Type type) {
+ boost::shared_ptr<IQ> iq(new IQ(IQ::Error));
+ iq->setTo(to);
+ iq->setID(id);
+ iq->addPayload(boost::shared_ptr<Swift::ErrorPayload>(new Swift::ErrorPayload(condition, type)));
+ return iq;
+}
+
+}
diff --git a/Swiften/Elements/IQ.h b/Swiften/Elements/IQ.h
new file mode 100644
index 0000000..80c2913
--- /dev/null
+++ b/Swiften/Elements/IQ.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Elements/ErrorPayload.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,
+ ErrorPayload::Condition condition,
+ ErrorPayload::Type type);
+
+ private:
+ Type type_;
+ };
+}
diff --git a/Swiften/Elements/MUCPayload.h b/Swiften/Elements/MUCPayload.h
new file mode 100644
index 0000000..97932a1
--- /dev/null
+++ b/Swiften/Elements/MUCPayload.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class MUCPayload : public Payload {
+ public:
+ MUCPayload() {
+ }
+ };
+}
diff --git a/Swiften/Elements/Message.h b/Swiften/Elements/Message.h
new file mode 100644
index 0000000..6d9171f
--- /dev/null
+++ b/Swiften/Elements/Message.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Body.h"
+#include "Swiften/Elements/ErrorPayload.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::ErrorPayload> error(getPayload<Swift::ErrorPayload>());
+ return getType() == Message::Error || error;
+ }
+
+ Type getType() const { return type_; }
+ void setType(Type type) { type_ = type; }
+
+ private:
+ String body_;
+ Type type_;
+ };
+}
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..f748e44
--- /dev/null
+++ b/Swiften/Elements/Presence.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#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)*/ {}
+ Presence(const String& status) : type_(Available) {
+ setStatus(status);
+ }
+
+ 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)));
+ }
+
+ boost::shared_ptr<Presence> clone() const {
+ return boost::shared_ptr<Presence>(new Presence(*this));
+ }
+
+ private:
+ Presence::Type type_;
+ };
+}
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/PrivateStorage.h b/Swiften/Elements/PrivateStorage.h
new file mode 100644
index 0000000..93f1cfe
--- /dev/null
+++ b/Swiften/Elements/PrivateStorage.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class PrivateStorage : public Payload {
+ public:
+ PrivateStorage(boost::shared_ptr<Payload> payload = boost::shared_ptr<Payload>()) : payload(payload) {
+ }
+
+ boost::shared_ptr<Payload> getPayload() const {
+ return payload;
+ }
+
+ void setPayload(boost::shared_ptr<Payload> p) {
+ payload = p;
+ }
+
+ private:
+ boost::shared_ptr<Payload> payload;
+ };
+}
diff --git a/Swiften/Elements/ProtocolHeader.h b/Swiften/Elements/ProtocolHeader.h
new file mode 100644
index 0000000..da64811
--- /dev/null
+++ b/Swiften/Elements/ProtocolHeader.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class ProtocolHeader {
+ public:
+ ProtocolHeader() : version("1.0") {}
+
+ const String& getTo() const { return to; }
+ void setTo(const String& a) {
+ to = a;
+ }
+
+ const String& getFrom() const { return from; }
+ void setFrom(const String& a) {
+ from = a;
+ }
+
+ const String& getVersion() const { return version; }
+ void setVersion(const String& a) {
+ version = a;
+ }
+
+ const String& getID() const { return id; }
+ void setID(const String& a) {
+ id = a;
+ }
+
+ private:
+ String to;
+ String from;
+ String id;
+ String version;
+ };
+}
diff --git a/Swiften/Elements/RawXMLPayload.h b/Swiften/Elements/RawXMLPayload.h
new file mode 100644
index 0000000..c2ee439
--- /dev/null
+++ b/Swiften/Elements/RawXMLPayload.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class RawXMLPayload : public Payload {
+ public:
+ RawXMLPayload() {}
+
+ void setRawXML(const String& data) {
+ rawXML_ = data;
+ }
+
+ const String& getRawXML() const {
+ return rawXML_;
+ }
+
+ private:
+ String rawXML_;
+ };
+}
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..1aecde9
--- /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().equals(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/Storage.h b/Swiften/Elements/Storage.h
new file mode 100644
index 0000000..1458836
--- /dev/null
+++ b/Swiften/Elements/Storage.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <vector>
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class Storage : public Payload {
+ public:
+ struct Conference {
+ Conference() : autoJoin(false) {}
+
+ String name;
+ JID jid;
+ bool autoJoin;
+ String nick;
+ String password;
+ };
+
+ Storage() {
+ }
+
+ const std::vector<Conference>& getConferences() const {
+ return conferences;
+ }
+
+ void addConference(const Conference& conference) {
+ conferences.push_back(conference);
+ }
+
+ private:
+ std::vector<Conference> conferences;
+ };
+}
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..a5e6dc8
--- /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", ErrorPayload::BadRequest, ErrorPayload::Modify));
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar/fum"), iq->getTo());
+ CPPUNIT_ASSERT_EQUAL(String("myid"), iq->getID());
+ boost::shared_ptr<ErrorPayload> error(iq->getPayload<ErrorPayload>());
+ CPPUNIT_ASSERT(error);
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::BadRequest, error->getCondition());
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::Modify, error->getType());
+ }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IQTest);
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..3d2c219
--- /dev/null
+++ b/Swiften/Elements/UnknownElement.h
@@ -0,0 +1,13 @@
+#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/VCard.h b/Swiften/Elements/VCard.h
new file mode 100644
index 0000000..a42d7e3
--- /dev/null
+++ b/Swiften/Elements/VCard.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class VCard : public Payload {
+ public:
+ VCard() {}
+
+ void setFullName(const String& fullName) { fullName_ = fullName; }
+ const String& getFullName() const { return fullName_; }
+
+ void setFamilyName(const String& familyName) { familyName_ = familyName; }
+ const String& getFamilyName() const { return familyName_; }
+
+ void setGivenName(const String& givenName) { givenName_ = givenName; }
+ const String& getGivenName() const { return givenName_; }
+
+ void setEMail(const String& email) { email_ = email; }
+ const String& getEMail() const { return email_; }
+
+ void setNickname(const String& nick) { nick_ = nick; }
+ const String& getNickname() const { return nick_; }
+
+ void setPhoto(const ByteArray& photo) { photo_ = photo; }
+ const ByteArray& getPhoto() { return photo_; }
+
+ void setPhotoType(const String& photoType) { photoType_ = photoType; }
+ const String& getPhotoType() { return photoType_; }
+
+ private:
+ String fullName_;
+ String familyName_;
+ String givenName_;
+ String email_;
+ ByteArray photo_;
+ String photoType_;
+ String nick_;
+ };
+}
diff --git a/Swiften/Elements/VCardUpdate.h b/Swiften/Elements/VCardUpdate.h
new file mode 100644
index 0000000..6bb79cc
--- /dev/null
+++ b/Swiften/Elements/VCardUpdate.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class VCardUpdate : public Payload {
+ public:
+ VCardUpdate(const String& photoHash = "") : photoHash_(photoHash) {}
+
+ void setPhotoHash(const String& photoHash) { photoHash_ = photoHash; }
+ const String& getPhotoHash() { return photoHash_; }
+
+ private:
+ String photoHash_;
+ };
+}
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/Cocoa/CocoaEvent.h b/Swiften/EventLoop/Cocoa/CocoaEvent.h
new file mode 100644
index 0000000..0ff4453
--- /dev/null
+++ b/Swiften/EventLoop/Cocoa/CocoaEvent.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <Cocoa/Cocoa.h>
+
+namespace Swift {
+ class Event;
+ class CocoaEventLoop;
+}
+
+@interface CocoaEvent : NSObject {
+ Swift::Event* event;
+ Swift::CocoaEventLoop* eventLoop;
+}
+
+// Takes ownership of event
+- (id) initWithEvent: (Swift::Event*) e eventLoop: (Swift::CocoaEventLoop*) el;
+- (void) process;
+- (void) dealloc;
+
+@end
diff --git a/Swiften/EventLoop/Cocoa/CocoaEvent.mm b/Swiften/EventLoop/Cocoa/CocoaEvent.mm
new file mode 100644
index 0000000..8a90983
--- /dev/null
+++ b/Swiften/EventLoop/Cocoa/CocoaEvent.mm
@@ -0,0 +1,25 @@
+#include "Swiften/EventLoop/Cocoa/CocoaEvent.h"
+#include "Swiften/EventLoop/Event.h"
+#include "Swiften/EventLoop/Cocoa/CocoaEventLoop.h"
+
+@implementation CocoaEvent
+
+- (id) initWithEvent: (Swift::Event*) e eventLoop: (Swift::CocoaEventLoop*) el {
+ self = [super init];
+ if (self != nil) {
+ event = e;
+ eventLoop = el;
+ }
+ return self;
+}
+
+- (void) process {
+ eventLoop->handleEvent(*event);
+}
+
+- (void) dealloc {
+ delete event;
+ [super dealloc];
+}
+
+@end
diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.h b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h
new file mode 100644
index 0000000..ad8e456
--- /dev/null
+++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Swiften/EventLoop/EventLoop.h"
+
+namespace Swift {
+ class CocoaEventLoop : public EventLoop {
+ public:
+ CocoaEventLoop();
+
+ virtual void post(const Event& event);
+
+ using EventLoop::handleEvent;
+ };
+}
diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm
new file mode 100644
index 0000000..b90f3c6
--- /dev/null
+++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm
@@ -0,0 +1,21 @@
+#include "Swiften/EventLoop/Cocoa/CocoaEventLoop.h"
+#include "Swiften/EventLoop/Cocoa/CocoaEvent.h"
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+namespace Swift {
+
+CocoaEventLoop::CocoaEventLoop() {
+}
+
+void CocoaEventLoop::post(const Event& event) {
+ Event* eventCopy = new Event(event);
+ CocoaEvent* cocoaEvent = [[CocoaEvent alloc] initWithEvent: eventCopy eventLoop: this];
+ [cocoaEvent
+ performSelectorOnMainThread:@selector(process)
+ withObject: nil
+ waitUntilDone: NO];
+ [cocoaEvent release];
+}
+
+}
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..7766bd4
--- /dev/null
+++ b/Swiften/EventLoop/DummyEventLoop.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <deque>
+#include <iostream>
+#include <boost/function.hpp>
+
+#include "Swiften/EventLoop/EventLoop.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+ class DummyEventLoop : public EventLoop {
+ public:
+ DummyEventLoop() {
+ }
+
+ ~DummyEventLoop() {
+ if (!events_.empty()) {
+ std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl;
+ }
+ events_.clear();
+ }
+
+ 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_;
+ };
+}
diff --git a/Swiften/EventLoop/Event.h b/Swiften/EventLoop/Event.h
new file mode 100644
index 0000000..edd35f4
--- /dev/null
+++ b/Swiften/EventLoop/Event.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+ struct Event {
+ Event(boost::shared_ptr<EventOwner> owner, const boost::function<void()>& callback) :
+ owner(owner), callback(callback) {
+ }
+
+ bool operator==(const Event& o) const {
+ return o.id == id;
+ }
+
+ unsigned int id;
+ boost::shared_ptr<EventOwner> owner;
+ boost::function<void()> callback;
+ };
+}
diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp
new file mode 100644
index 0000000..3c3c356
--- /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, boost::shared_ptr<EventOwner> 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(boost::shared_ptr<EventOwner> 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..2b45288
--- /dev/null
+++ b/Swiften/EventLoop/EventLoop.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <boost/function.hpp>
+#include <boost/thread/mutex.hpp>
+#include <list>
+
+#include "Swiften/EventLoop/Event.h"
+
+namespace Swift {
+ class EventOwner;
+ class EventLoop {
+ public:
+ EventLoop();
+ virtual ~EventLoop();
+
+ void postEvent(boost::function<void ()> event, boost::shared_ptr<EventOwner> owner = boost::shared_ptr<EventOwner>());
+ void removeEventsFromOwner(boost::shared_ptr<EventOwner> owner);
+
+ protected:
+ /**
+ * 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(boost::shared_ptr<EventOwner> owner) : owner(owner) {}
+ bool operator()(const Event& event) { return event.owner == owner; }
+ boost::shared_ptr<EventOwner> owner;
+ };
+ boost::mutex eventsMutex_;
+ unsigned int nextEventID_;
+ std::list<Event> events_;
+ };
+}
diff --git a/Swiften/EventLoop/EventOwner.cpp b/Swiften/EventLoop/EventOwner.cpp
new file mode 100644
index 0000000..4818b3c
--- /dev/null
+++ b/Swiften/EventLoop/EventOwner.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+
+EventOwner::~EventOwner() {
+}
+
+}
diff --git a/Swiften/EventLoop/EventOwner.h b/Swiften/EventLoop/EventOwner.h
new file mode 100644
index 0000000..8da95e0
--- /dev/null
+++ b/Swiften/EventLoop/EventOwner.h
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace Swift {
+ class EventOwner {
+ public:
+ virtual ~EventOwner();
+ };
+}
diff --git a/Swiften/EventLoop/MainEventLoop.cpp b/Swiften/EventLoop/MainEventLoop.cpp
new file mode 100644
index 0000000..d7de6c6
--- /dev/null
+++ b/Swiften/EventLoop/MainEventLoop.cpp
@@ -0,0 +1,36 @@
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+#include <iostream>
+#include <typeinfo>
+
+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, boost::shared_ptr<EventOwner> owner) {
+ getInstance()->postEvent(event, owner);
+}
+
+void MainEventLoop::removeEventsFromOwner(boost::shared_ptr<EventOwner> 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..fbb7079
--- /dev/null
+++ b/Swiften/EventLoop/MainEventLoop.h
@@ -0,0 +1,41 @@
+#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 EventOwner;
+
+ 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, boost::shared_ptr<EventOwner> owner = boost::shared_ptr<EventOwner>());
+
+ static void removeEventsFromOwner(boost::shared_ptr<EventOwner> 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/Qt/QtEventLoop.h b/Swiften/EventLoop/Qt/QtEventLoop.h
new file mode 100644
index 0000000..40e927e
--- /dev/null
+++ b/Swiften/EventLoop/Qt/QtEventLoop.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <QObject>
+#include <QEvent>
+#include <QCoreApplication>
+
+#include "Swiften/EventLoop/EventLoop.h"
+
+class QtEventLoop : public QObject, public Swift::EventLoop {
+ public:
+ QtEventLoop() {}
+
+ virtual void post(const Swift::Event& event) {
+ QCoreApplication::postEvent(this, new Event(event));
+ }
+
+ virtual bool event(QEvent* qevent) {
+ Event* event = dynamic_cast<Event*>(qevent);
+ if (event) {
+ handleEvent(event->event_);
+ //event->deleteLater(); FIXME: Leak?
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ struct Event : public QEvent {
+ Event(const Swift::Event& event) :
+ QEvent(QEvent::User), event_(event) {
+ }
+
+ Swift::Event event_;
+ };
+};
diff --git a/Swiften/EventLoop/SConscript b/Swiften/EventLoop/SConscript
new file mode 100644
index 0000000..5d1c3cb
--- /dev/null
+++ b/Swiften/EventLoop/SConscript
@@ -0,0 +1,17 @@
+Import("swiften_env")
+
+sources = [
+ "EventLoop.cpp",
+ "EventOwner.cpp",
+ "MainEventLoop.cpp",
+ "SimpleEventLoop.cpp",
+ ]
+
+if swiften_env["PLATFORM"] == "darwin" and swiften_env["target"] == "native":
+ sources += [
+ "Cocoa/CocoaEventLoop.mm",
+ "Cocoa/CocoaEvent.mm"
+ ]
+
+objects = swiften_env.StaticObject(sources)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/EventLoop/SimpleEventLoop.cpp b/Swiften/EventLoop/SimpleEventLoop.cpp
new file mode 100644
index 0000000..7c46ed3
--- /dev/null
+++ b/Swiften/EventLoop/SimpleEventLoop.cpp
@@ -0,0 +1,54 @@
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+
+
+namespace Swift {
+
+void nop() {}
+
+SimpleEventLoop::SimpleEventLoop() : isRunning_(true) {
+}
+
+SimpleEventLoop::~SimpleEventLoop() {
+ if (!events_.empty()) {
+ std::cerr << "Warning: Pending events in SimpleEventLoop at destruction time" << std::endl;
+ }
+}
+
+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));
+}
+
+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..bd0a07f
--- /dev/null
+++ b/Swiften/EventLoop/SimpleEventLoop.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#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();
+ ~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_;
+ };
+}
diff --git a/Swiften/EventLoop/UnitTest/EventLoopTest.cpp b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp
new file mode 100644
index 0000000..9475ac9
--- /dev/null
+++ b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp
@@ -0,0 +1,67 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+#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));
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2));
+ 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;
+ boost::shared_ptr<MyEventOwner> eventOwner1(new MyEventOwner());
+ boost::shared_ptr<MyEventOwner> eventOwner2(new MyEventOwner());
+
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), eventOwner1);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), eventOwner2);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 3), eventOwner1);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 4), eventOwner2);
+ testling.removeEventsFromOwner(eventOwner2);
+ 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:
+ struct MyEventOwner : public EventOwner {};
+ void logEvent(int i) {
+ events_.push_back(i);
+ }
+
+ private:
+ std::vector<int> events_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(EventLoopTest);
diff --git a/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp
new file mode 100644
index 0000000..14f24c7
--- /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));
+ 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));
+ }
+ loop->stop();
+ }
+
+ void incrementCounter() {
+ counter_++;
+ }
+
+ void incrementCounterAndStop(SimpleEventLoop* loop) {
+ counter_++;
+ loop->stop();
+ }
+
+ int counter_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SimpleEventLoopTest);
diff --git a/Swiften/Events/MessageEvent.h b/Swiften/Events/MessageEvent.h
new file mode 100644
index 0000000..43174a1
--- /dev/null
+++ b/Swiften/Events/MessageEvent.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_MessageEvent_H
+#define SWIFTEN_MessageEvent_H
+
+#include <cassert>
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Events/StanzaEvent.h"
+#include "Swiften/Elements/Message.h"
+
+namespace Swift {
+ class MessageEvent : public StanzaEvent {
+ public:
+ MessageEvent(boost::shared_ptr<Message> stanza) : stanza_(stanza){};
+ virtual ~MessageEvent(){};
+ boost::shared_ptr<Message> getStanza() {return stanza_;}
+
+ bool isReadable() {
+ return getStanza()->isError() || !getStanza()->getBody().isEmpty();
+ }
+
+ void read() {
+ assert (isReadable());
+ conclude();
+ }
+
+ private:
+ boost::shared_ptr<Message> stanza_;
+ };
+}
+
+#endif
diff --git a/Swiften/Events/StanzaEvent.h b/Swiften/Events/StanzaEvent.h
new file mode 100644
index 0000000..b1dc537
--- /dev/null
+++ b/Swiften/Events/StanzaEvent.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class StanzaEvent {
+ public:
+ StanzaEvent(){concluded_ = false;};
+ virtual ~StanzaEvent() {};
+ void conclude() {concluded_ = true; onConclusion();};
+ /** Do not call this directly from outside the class */
+ boost::signal<void()> onConclusion;
+ bool getConcluded() {return concluded_;};
+ private:
+ bool concluded_;
+ };
+}
diff --git a/Swiften/Events/SubscriptionRequestEvent.h b/Swiften/Events/SubscriptionRequestEvent.h
new file mode 100644
index 0000000..ed063d7
--- /dev/null
+++ b/Swiften/Events/SubscriptionRequestEvent.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <cassert>
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Events/StanzaEvent.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class SubscriptionRequestEvent : public StanzaEvent {
+ public:
+ SubscriptionRequestEvent(const JID& jid, const String& reason) : jid_(jid), reason_(reason){};
+ virtual ~SubscriptionRequestEvent(){};
+ const JID& getJID() const {return jid_;};
+ const String& getReason() const {return reason_;};
+ boost::signal<void()> onAccept;
+ boost::signal<void()> onDecline;
+ void accept() {
+ onAccept();
+ conclude();
+ };
+
+ void decline() {
+ onDecline();
+ conclude();
+ };
+
+ private:
+ JID jid_;
+ String reason_;
+ };
+}
+
diff --git a/Swiften/Examples/EchoBot/.gitignore b/Swiften/Examples/EchoBot/.gitignore
new file mode 100644
index 0000000..9200f42
--- /dev/null
+++ b/Swiften/Examples/EchoBot/.gitignore
@@ -0,0 +1 @@
+EchoBot
diff --git a/Swiften/Examples/EchoBot/EchoBot.cpp b/Swiften/Examples/EchoBot/EchoBot.cpp
new file mode 100644
index 0000000..872d901
--- /dev/null
+++ b/Swiften/Examples/EchoBot/EchoBot.cpp
@@ -0,0 +1,56 @@
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/Client/ClientXMLTracer.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+
+using namespace Swift;
+using namespace boost;
+
+class EchoBot {
+ public:
+ EchoBot(const JID& jid, const String& pass) : tracer(0) {
+ client = new Client(jid, pass);
+ tracer = new ClientXMLTracer(client);
+ client->onConnected.connect(bind(&EchoBot::handleConnected, this));
+ client->onMessageReceived.connect(bind(&EchoBot::handleMessageReceived, this, _1));
+ client->connect();
+ }
+
+ ~EchoBot() {
+ delete tracer;
+ delete client;
+ }
+
+ private:
+ void handleConnected() {
+ shared_ptr<GetRosterRequest> rosterRequest(new GetRosterRequest(client));
+ rosterRequest->onResponse.connect(bind(&EchoBot::handleRosterReceived, this, _2));
+ rosterRequest->send();
+ }
+
+ void handleRosterReceived(const optional<Error>& error) {
+ if (error) {
+ std::cerr << "Error receiving roster. Continuing anyway.";
+ }
+ client->sendPresence(shared_ptr<Presence>(new Presence("Send me a message")));
+ }
+
+ void handleMessageReceived(shared_ptr<Message> message) {
+ message->setTo(message->getFrom());
+ message->setFrom(JID());
+ client->sendMessage(message);
+ }
+
+ private:
+ Client* client;
+ ClientXMLTracer* tracer;
+};
+
+int main(int, char**) {
+ SimpleEventLoop eventLoop;
+ EchoBot bot(JID("echobot@wonderland.lit"), "mypass");
+ eventLoop.run();
+ return 0;
+}
diff --git a/Swiften/Examples/SConscript b/Swiften/Examples/SConscript
new file mode 100644
index 0000000..a4d5998
--- /dev/null
+++ b/Swiften/Examples/SConscript
@@ -0,0 +1,8 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+
+if myenv["target"] == "native":
+ SConscript(dirs = [
+ "SendMessage"
+ ])
diff --git a/Swiften/Examples/SendMessage/.gitignore b/Swiften/Examples/SendMessage/.gitignore
new file mode 100644
index 0000000..3b8b4d2
--- /dev/null
+++ b/Swiften/Examples/SendMessage/.gitignore
@@ -0,0 +1 @@
+SendMessage
diff --git a/Swiften/Examples/SendMessage/SConscript b/Swiften/Examples/SendMessage/SConscript
new file mode 100644
index 0000000..0e0197e
--- /dev/null
+++ b/Swiften/Examples/SendMessage/SConscript
@@ -0,0 +1,13 @@
+Import("env")
+
+myenv = env.Clone()
+myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
+myenv.MergeFlags(myenv["CPPUNIT_FLAGS"])
+myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
+myenv.MergeFlags(myenv["BOOST_FLAGS"])
+myenv.MergeFlags(myenv["SQLITE_FLAGS"])
+myenv.MergeFlags(myenv["ZLIB_FLAGS"])
+myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
+myenv.MergeFlags(myenv.get("LIBXML_FLAGS", ""))
+myenv.MergeFlags(myenv.get("EXPAT_FLAGS", ""))
+tester = myenv.Program("SendMessage", ["SendMessage.cpp"])
diff --git a/Swiften/Examples/SendMessage/SendMessage.cpp b/Swiften/Examples/SendMessage/SendMessage.cpp
new file mode 100644
index 0000000..b7a80dd
--- /dev/null
+++ b/Swiften/Examples/SendMessage/SendMessage.cpp
@@ -0,0 +1,53 @@
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/Network/BoostTimer.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Client/ClientXMLTracer.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/Network/MainBoostIOServiceThread.h"
+
+using namespace Swift;
+
+SimpleEventLoop eventLoop;
+
+Client* client = 0;
+JID recipient;
+std::string messageBody;
+
+void handleConnected() {
+ boost::shared_ptr<Message> message(new Message());
+ message->setBody(messageBody);
+ message->setTo(recipient);
+ client->sendMessage(message);
+ client->disconnect();
+ eventLoop.stop();
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 5) {
+ std::cerr << "Usage: " << argv[0] << " <jid> <password> <recipient> <message>" << std::endl;
+ return -1;
+ }
+
+ recipient = JID(argv[3]);
+ messageBody = std::string(argv[4]);
+
+ client = new Swift::Client(JID(argv[1]), String(argv[2]));
+ ClientXMLTracer* tracer = new ClientXMLTracer(client);
+ client->onConnected.connect(&handleConnected);
+ client->connect();
+
+ {
+ boost::shared_ptr<BoostTimer> timer(new BoostTimer(30000, &MainBoostIOServiceThread::getInstance().getIOService()));
+ timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
+ timer->start();
+
+ eventLoop.run();
+ }
+
+ delete tracer;
+ delete client;
+}
diff --git a/Swiften/History/HistoryManager.cpp b/Swiften/History/HistoryManager.cpp
new file mode 100644
index 0000000..3d67c27
--- /dev/null
+++ b/Swiften/History/HistoryManager.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/History/HistoryManager.h"
+
+namespace Swift {
+
+HistoryManager::~HistoryManager() {
+}
+
+}
diff --git a/Swiften/History/HistoryManager.h b/Swiften/History/HistoryManager.h
new file mode 100644
index 0000000..2a7c040
--- /dev/null
+++ b/Swiften/History/HistoryManager.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/History/HistoryMessage.h"
+
+namespace Swift {
+ class HistoryManager {
+ public:
+ virtual ~HistoryManager();
+
+ virtual void addMessage(const HistoryMessage& message) = 0;
+
+ virtual std::vector<HistoryMessage> getMessages() const = 0;
+ };
+}
diff --git a/Swiften/History/HistoryMessage.h b/Swiften/History/HistoryMessage.h
new file mode 100644
index 0000000..3fda3e2
--- /dev/null
+++ b/Swiften/History/HistoryMessage.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+namespace Swift {
+ class HistoryMessage {
+ public:
+ HistoryMessage(const String& message, const JID& from, const JID& to, const boost::posix_time::ptime time) : message_(message), from_(from), to_(to), time_(time) {
+ }
+
+ const String& getMessage() const {
+ return message_;
+ }
+
+ const JID& getFrom() const {
+ return from_;
+ }
+
+ const JID& getTo() const {
+ return to_;
+ }
+
+ boost::posix_time::ptime getTime() const {
+ return time_;
+ }
+
+ bool operator==(const HistoryMessage& o) const {
+ return message_ == o.message_ && from_ == o.from_ && to_ == o.to_ && time_ == o.time_;
+ }
+
+ private:
+ String message_;
+ JID from_;
+ JID to_;
+ boost::posix_time::ptime time_;
+ };
+}
diff --git a/Swiften/History/SConscript b/Swiften/History/SConscript
new file mode 100644
index 0000000..bc80780
--- /dev/null
+++ b/Swiften/History/SConscript
@@ -0,0 +1,11 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+if myenv["target"] == "native":
+ myenv.MergeFlags(swiften_env["SQLITE_FLAGS"])
+
+objects = myenv.StaticObject([
+ "HistoryManager.cpp",
+ "SQLiteHistoryManager.cpp",
+ ])
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/History/SQLiteHistoryManager.cpp b/Swiften/History/SQLiteHistoryManager.cpp
new file mode 100644
index 0000000..865abba
--- /dev/null
+++ b/Swiften/History/SQLiteHistoryManager.cpp
@@ -0,0 +1,134 @@
+#include <iostream>
+#include <boost/lexical_cast.hpp>
+
+#include "sqlite3.h"
+#include "Swiften/History/SQLiteHistoryManager.h"
+
+namespace {
+
+inline Swift::String getEscapedString(const Swift::String& s) {
+ Swift::String result(s);
+ result.replaceAll('\'', Swift::String("\\'"));
+ return result;
+}
+
+}
+
+
+namespace Swift {
+
+SQLiteHistoryManager::SQLiteHistoryManager(const String& file) : db_(0) {
+ sqlite3_open(file.getUTF8Data(), &db_);
+ if (!db_) {
+ std::cerr << "Error opening database " << file << std::endl; // FIXME
+ }
+
+ char* errorMessage;
+ int result = sqlite3_exec(db_, "CREATE TABLE IF NOT EXISTS messages('from' INTEGER, 'to' INTEGER, 'message' STRING, 'time' INTEGER)", 0, 0, &errorMessage);
+ if (result != SQLITE_OK) {
+ std::cerr << "SQL Error: " << errorMessage << std::endl;
+ sqlite3_free(errorMessage);
+ }
+
+ result = sqlite3_exec(db_, "CREATE TABLE IF NOT EXISTS jids('id' INTEGER PRIMARY KEY ASC AUTOINCREMENT, 'jid' STRING UNIQUE NOT NULL)", 0, 0, &errorMessage);
+ if (result != SQLITE_OK) {
+ std::cerr << "SQL Error: " << errorMessage << std::endl;
+ sqlite3_free(errorMessage);
+ }
+}
+
+SQLiteHistoryManager::~SQLiteHistoryManager() {
+ sqlite3_close(db_);
+}
+
+void SQLiteHistoryManager::addMessage(const HistoryMessage& message) {
+ int secondsSinceEpoch = (message.getTime() - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds();
+ String statement = String("INSERT INTO messages('from', 'to', 'message', 'time') VALUES(") + boost::lexical_cast<std::string>(getIDForJID(message.getFrom())) + ", " + boost::lexical_cast<std::string>(getIDForJID(message.getTo())) + ", '" + getEscapedString(message.getMessage()) + "', " + boost::lexical_cast<std::string>(secondsSinceEpoch) + ")";
+ char* errorMessage;
+ int result = sqlite3_exec(db_, statement.getUTF8Data(), 0, 0, &errorMessage);
+ if (result != SQLITE_OK) {
+ std::cerr << "SQL Error: " << errorMessage << std::endl;
+ sqlite3_free(errorMessage);
+ }
+}
+
+std::vector<HistoryMessage> SQLiteHistoryManager::getMessages() const {
+ std::vector<HistoryMessage> result;
+ sqlite3_stmt* selectStatement;
+ String selectQuery("SELECT messages.'from', messages.'to', messages.'message', messages.'time' FROM messages");
+ int r = sqlite3_prepare(db_, selectQuery.getUTF8Data(), selectQuery.getUTF8Size(), &selectStatement, NULL);
+ if (r != SQLITE_OK) {
+ std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl;
+ }
+ r = sqlite3_step(selectStatement);
+ while (r == SQLITE_ROW) {
+ boost::optional<JID> from(getJIDFromID(sqlite3_column_int(selectStatement, 0)));
+ boost::optional<JID> to(getJIDFromID(sqlite3_column_int(selectStatement, 1)));
+ String message(reinterpret_cast<const char*>(sqlite3_column_text(selectStatement, 2)));
+ int secondsSinceEpoch(sqlite3_column_int(selectStatement, 3));
+ boost::posix_time::ptime time(boost::gregorian::date(1970, 1, 1), boost::posix_time::seconds(secondsSinceEpoch));
+
+ result.push_back(HistoryMessage(message, (from ? *from : JID()), (to ? *to : JID()), time));
+ r = sqlite3_step(selectStatement);
+ }
+ if (r != SQLITE_DONE) {
+ std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl;
+ }
+ sqlite3_finalize(selectStatement);
+ return result;
+}
+
+int SQLiteHistoryManager::getIDForJID(const JID& jid) {
+ boost::optional<int> id = getIDFromJID(jid);
+ if (id) {
+ return *id;
+ }
+ else {
+ return addJID(jid);
+ }
+}
+
+int SQLiteHistoryManager::addJID(const JID& jid) {
+ String statement = String("INSERT INTO jids('jid') VALUES('") + getEscapedString(jid.toString()) + "')";
+ char* errorMessage;
+ int result = sqlite3_exec(db_, statement.getUTF8Data(), 0, 0, &errorMessage);
+ if (result != SQLITE_OK) {
+ std::cerr << "SQL Error: " << errorMessage << std::endl;
+ sqlite3_free(errorMessage);
+ }
+ return sqlite3_last_insert_rowid(db_);
+}
+
+boost::optional<JID> SQLiteHistoryManager::getJIDFromID(int id) const {
+ boost::optional<JID> result;
+ sqlite3_stmt* selectStatement;
+ String selectQuery("SELECT jid FROM jids WHERE id=" + boost::lexical_cast<std::string>(id));
+ int r = sqlite3_prepare(db_, selectQuery.getUTF8Data(), selectQuery.getUTF8Size(), &selectStatement, NULL);
+ if (r != SQLITE_OK) {
+ std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl;
+ }
+ r = sqlite3_step(selectStatement);
+ if (r == SQLITE_ROW) {
+ result = boost::optional<JID>(reinterpret_cast<const char*>(sqlite3_column_text(selectStatement, 0)));
+ }
+ sqlite3_finalize(selectStatement);
+ return result;
+}
+
+boost::optional<int> SQLiteHistoryManager::getIDFromJID(const JID& jid) const {
+ boost::optional<int> result;
+ sqlite3_stmt* selectStatement;
+ String selectQuery("SELECT id FROM jids WHERE jid='" + jid.toString() + "'");
+ int r = sqlite3_prepare(db_, selectQuery.getUTF8Data(), selectQuery.getUTF8Size(), &selectStatement, NULL);
+ if (r != SQLITE_OK) {
+ std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl;
+ }
+ r = sqlite3_step(selectStatement);
+ if (r == SQLITE_ROW) {
+ result = boost::optional<int>(sqlite3_column_int(selectStatement, 0));
+ }
+ sqlite3_finalize(selectStatement);
+ return result;
+}
+
+}
diff --git a/Swiften/History/SQLiteHistoryManager.h b/Swiften/History/SQLiteHistoryManager.h
new file mode 100644
index 0000000..ace42f7
--- /dev/null
+++ b/Swiften/History/SQLiteHistoryManager.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/History/HistoryManager.h"
+
+struct sqlite3;
+
+namespace Swift {
+ class SQLiteHistoryManager : public HistoryManager {
+ public:
+ SQLiteHistoryManager(const String& file);
+ ~SQLiteHistoryManager();
+
+ virtual void addMessage(const HistoryMessage& message);
+ virtual std::vector<HistoryMessage> getMessages() const;
+
+ int getIDForJID(const JID&);
+ int addJID(const JID&);
+
+ boost::optional<JID> getJIDFromID(int id) const;
+ boost::optional<int> getIDFromJID(const JID& jid) const;
+
+ private:
+ sqlite3* db_;
+ };
+}
diff --git a/Swiften/History/UnitTest/SQLiteHistoryManagerTest.cpp b/Swiften/History/UnitTest/SQLiteHistoryManagerTest.cpp
new file mode 100644
index 0000000..e738a6e
--- /dev/null
+++ b/Swiften/History/UnitTest/SQLiteHistoryManagerTest.cpp
@@ -0,0 +1,109 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "Swiften/History/SQLiteHistoryManager.h"
+
+using namespace Swift;
+
+class SQLiteHistoryManagerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SQLiteHistoryManagerTest);
+ CPPUNIT_TEST(testAddMessage);
+ CPPUNIT_TEST(testAddMessage_TwoMessages);
+ CPPUNIT_TEST(testGetIDForJID_SameJID);
+ CPPUNIT_TEST(testGetIDForJID_DifferentJIDs);
+ CPPUNIT_TEST(getJIDFromID);
+ CPPUNIT_TEST(getJIDFromID_UnexistingID);
+ CPPUNIT_TEST(getIDFromJID);
+ CPPUNIT_TEST(getIDFromJID_UnexistingJID);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SQLiteHistoryManagerTest() {}
+
+ void setUp() {
+ }
+
+ void tearDown() {
+ }
+
+ void testAddMessage() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+ HistoryMessage testMessage("Test", JID("foo@bar.com"), JID("fum@baz.org"), boost::posix_time::time_from_string("1980-01-21 22:03"));
+ testling->addMessage(testMessage);
+
+ std::vector<HistoryMessage> messages = testling->getMessages();
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(messages.size()));
+ CPPUNIT_ASSERT(testMessage == messages[0]);
+ }
+
+ void testAddMessage_TwoMessages() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+ HistoryMessage testMessage1("Test1", JID("foo@bar.com"), JID("fum@baz.org"), boost::posix_time::time_from_string("1980-01-21 22:03"));
+ testling->addMessage(testMessage1);
+ HistoryMessage testMessage2("Test2", JID("fum@baz.org"), JID("foo@bar.com"), boost::posix_time::time_from_string("1975-03-09 22:04"));
+ testling->addMessage(testMessage2);
+
+ std::vector<HistoryMessage> messages = testling->getMessages();
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(messages.size()));
+ CPPUNIT_ASSERT(testMessage1 == messages[0]);
+ CPPUNIT_ASSERT(testMessage2 == messages[1]);
+ }
+
+ void testGetIDForJID_SameJID() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+ int id1 = testling->getIDForJID(JID("foo@bar.com"));
+ int id2 = testling->getIDForJID(JID("foo@bar.com"));
+
+ CPPUNIT_ASSERT_EQUAL(id1, id2);
+ }
+
+ void testGetIDForJID_DifferentJIDs() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+ int id1 = testling->getIDForJID(JID("foo@bar.com"));
+ int id2 = testling->getIDForJID(JID("foo@baz.com"));
+
+ CPPUNIT_ASSERT(id1 != id2);
+ }
+
+ void getJIDFromID() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+ int id = testling->addJID(JID("foo@bar.com"));
+
+ boost::optional<JID> result(testling->getJIDFromID(id));
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), *result);
+ }
+
+ void getJIDFromID_UnexistingID() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+
+ boost::optional<JID> result(testling->getJIDFromID(1));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void getIDFromJID() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+ int id = testling->addJID(JID("foo@bar.com"));
+
+ boost::optional<int> result(testling->getIDFromJID(JID("foo@bar.com")));
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(id, *result);
+ }
+
+ void getIDFromJID_UnexistingJID() {
+ std::auto_ptr<SQLiteHistoryManager> testling(createHistoryManager());
+
+ boost::optional<int> result(testling->getIDFromJID(JID("foo@bar.com")));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ private:
+ SQLiteHistoryManager* createHistoryManager() {
+ return new SQLiteHistoryManager(":memory:");
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SQLiteHistoryManagerTest);
diff --git a/Swiften/JID/JID.cpp b/Swiften/JID/JID.cpp
new file mode 100644
index 0000000..3be8386
--- /dev/null
+++ b/Swiften/JID/JID.cpp
@@ -0,0 +1,86 @@
+#include <stringprep.h>
+#include <vector>
+#include <iostream>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/StringPrep/StringPrep.h"
+
+namespace Swift {
+
+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_ = StringPrep::getPrepared(node, StringPrep::NamePrep);
+ domain_ = StringPrep::getPrepared(domain, StringPrep::XMPPNodePrep);
+ resource_ = StringPrep::getPrepared(resource, StringPrep::XMPPResourcePrep);
+}
+
+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..0bfb858
--- /dev/null
+++ b/Swiften/JID/JID.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#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_;
+ };
+}
diff --git a/Swiften/JID/SConscript b/Swiften/JID/SConscript
new file mode 100644
index 0000000..d48fbb0
--- /dev/null
+++ b/Swiften/JID/SConscript
@@ -0,0 +1,9 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"])
+
+objects = myenv.StaticObject([
+ "JID.cpp",
+ ])
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
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/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h
new file mode 100644
index 0000000..229f97e
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <boost/bind.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class AvahiQuerier;
+
+ class AvahiBrowseQuery : public DNSSDBrowseQuery, public AvahiQuery {
+ public:
+ AvahiBrowseQuery(boost::shared_ptr<AvahiQuerier> q) : AvahiQuery(q) {
+ }
+
+ void startBrowsing() {
+ std::cout << "Start browsing" << std::endl;
+ avahi_threaded_poll_lock(querier->getThreadedPoll());
+ std::cout << "Creating browser" << std::endl;
+ AvahiServiceBrowser* browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, (AvahiLookupFlags) 0, &handleServiceDiscoveredStatic, this);
+ if (!browser) {
+ std::cout << "Error" << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+ }
+ std::cout << "Unlocking" << std::endl;
+ avahi_threaded_poll_unlock(querier->getThreadedPoll());
+ std::cout << "Browse started" << std::endl;
+ }
+
+ void stopBrowsing() {
+ // TODO
+ }
+
+ private:
+ static void handleServiceDiscoveredStatic(AvahiServiceBrowser *b, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* context) {
+ static_cast<AvahiBrowseQuery*>(context)->handleServiceDiscovered(b, interfaceIndex, protocol, event, name, type, domain, flags);
+ }
+
+ void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) {
+ switch (event) {
+ case AVAHI_BROWSER_FAILURE:
+ std::cout << "Service browse error" << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+ break;
+ case AVAHI_BROWSER_NEW: {
+ DNSSDServiceID service(name, domain, type, interfaceIndex);
+ std::cout << "Service discovered " << name << " " << type << " " << domain << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this());
+ break;
+ }
+ case AVAHI_BROWSER_REMOVE: {
+ std::cout << "Service went away " << name << " " << type << " " << domain << std::endl;
+ DNSSDServiceID service(name, domain, type, interfaceIndex);
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this());
+ break;
+ }
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ break;
+ }
+ }
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp
new file mode 100644
index 0000000..c4bfcb4
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp
@@ -0,0 +1,60 @@
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h"
+
+#include <iostream>
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h"
+
+namespace Swift {
+
+AvahiQuerier::AvahiQuerier() : client(NULL), threadedPoll(NULL) {
+}
+
+AvahiQuerier::~AvahiQuerier() {
+}
+
+boost::shared_ptr<DNSSDBrowseQuery> AvahiQuerier::createBrowseQuery() {
+ return boost::shared_ptr<DNSSDBrowseQuery>(new AvahiBrowseQuery(shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDRegisterQuery> AvahiQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) {
+ return boost::shared_ptr<DNSSDRegisterQuery>(new AvahiRegisterQuery(name, port, info, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveServiceQuery> AvahiQuerier::createResolveServiceQuery(const DNSSDServiceID& service) {
+ return boost::shared_ptr<DNSSDResolveServiceQuery>(new AvahiResolveServiceQuery(service, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveHostnameQuery> AvahiQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) {
+ return boost::shared_ptr<DNSSDResolveHostnameQuery>(new AvahiResolveHostnameQuery(hostname, interfaceIndex, shared_from_this()));
+}
+
+void AvahiQuerier::start() {
+ std::cout << "Starrting querier" << std::endl;
+ assert(!threadedPoll);
+ threadedPoll = avahi_threaded_poll_new();
+ int error;
+ assert(!client);
+ client = avahi_client_new(
+ avahi_threaded_poll_get(threadedPoll),
+ static_cast<AvahiClientFlags>(0), NULL, this, &error); // TODO
+ if (!client) {
+ // TODO
+ std::cerr << "Avahi Error: " << avahi_strerror(error) << std::endl;
+ return;
+ }
+ std::cout << "Starrting event loop" << std::endl;
+ avahi_threaded_poll_start(threadedPoll);
+}
+
+void AvahiQuerier::stop() {
+ assert(threadedPoll);
+ avahi_threaded_poll_stop(threadedPoll);
+ assert(client);
+ avahi_client_free(client);
+ avahi_threaded_poll_free(threadedPoll);
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h
new file mode 100644
index 0000000..ffb8441
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+
+namespace Swift {
+ class ByteArray;
+
+ class AvahiQuerier :
+ public DNSSDQuerier,
+ public boost::enable_shared_from_this<AvahiQuerier> {
+ public:
+ AvahiQuerier();
+ ~AvahiQuerier();
+
+ boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery();
+ boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
+ const String& name, int port, const ByteArray& info);
+ boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery(
+ const DNSSDServiceID&);
+ boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery(
+ const String& hostname, int interfaceIndex);
+
+ void start();
+ void stop();
+
+ AvahiThreadedPoll* getThreadedPoll() const {
+ return threadedPoll;
+ }
+
+ AvahiClient* getClient() const {
+ return client;
+ }
+
+ private:
+ AvahiClient* client;
+ AvahiThreadedPoll* threadedPoll;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp
new file mode 100644
index 0000000..6b7c7f9
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp
@@ -0,0 +1,13 @@
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h"
+
+namespace Swift {
+
+AvahiQuery::AvahiQuery(boost::shared_ptr<AvahiQuerier> q) : querier(q) {
+}
+
+AvahiQuery::~AvahiQuery() {
+}
+
+}
+
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h
new file mode 100644
index 0000000..847f3f7
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+ class AvahiQuerier;
+
+ class AvahiQuery :
+ public EventOwner,
+ public boost::enable_shared_from_this<AvahiQuery> {
+ public:
+ AvahiQuery(boost::shared_ptr<AvahiQuerier>);
+ virtual ~AvahiQuery();
+
+ protected:
+ boost::shared_ptr<AvahiQuerier> querier;
+ };
+}
+
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h
new file mode 100644
index 0000000..f42950e
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#include <avahi-client/publish.h>
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class AvahiQuerier;
+
+ class AvahiRegisterQuery : public DNSSDRegisterQuery, public AvahiQuery {
+ public:
+ AvahiRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), name(name), port(port), txtRecord(txtRecord), group(0) {
+ }
+
+ void registerService() {
+ std::cout << "Registering service " << name << ":" << port << std::endl;
+ avahi_threaded_poll_lock(querier->getThreadedPoll());
+ if (!group) {
+ std::cout << "Creating entry group" << std::endl;
+ group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this);
+ if (!group) {
+ std::cout << "Error ceating entry group" << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+ }
+
+ doRegisterService();
+ avahi_threaded_poll_unlock(querier->getThreadedPoll());
+ }
+
+ void unregisterService() {
+ }
+
+ void updateServiceInfo(const ByteArray& txtRecord) {
+ this->txtRecord = txtRecord;
+ avahi_threaded_poll_lock(querier->getThreadedPoll());
+ assert(group);
+ avahi_entry_group_reset(group);
+ doRegisterService();
+ avahi_threaded_poll_unlock(querier->getThreadedPoll());
+ }
+
+ private:
+ void doRegisterService() {
+ AvahiStringList* txtList;
+ avahi_string_list_parse(txtRecord.getData(), txtRecord.getSize(), &txtList);
+
+ int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, txtList);
+ if (result < 0) {
+ std::cout << "Error registering service: " << avahi_strerror(result) << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+ result = avahi_entry_group_commit(group);
+ if (result < 0) {
+ std::cout << "Error registering service: " << avahi_strerror(result) << std::endl;
+ }
+ }
+
+ static void handleEntryGroupChange(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+ static_cast<AvahiRegisterQuery*>(userdata)->handleEntryGroupChange(g, state);
+ }
+
+ void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) {
+ std::cout << "ENtry group callback: " << state << std::endl;
+ switch (state) {
+ case AVAHI_ENTRY_GROUP_ESTABLISHED :
+ // Domain is a hack!
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this());
+ std::cout << "Entry group established" << std::endl;
+ break;
+ case AVAHI_ENTRY_GROUP_COLLISION : {
+ std::cout << "Entry group collision" << std::endl;
+ /*char *n;
+ n = avahi_alternative_service_name(name);
+ avahi_free(name);
+ name = n;*/
+ break;
+ }
+
+ case AVAHI_ENTRY_GROUP_FAILURE :
+ std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl;
+ break;
+
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ ;
+
+ /*
+ DNSServiceErrorType result = DNSServiceRegister(
+ &sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port,
+ txtRecord.getSize(), txtRecord.getData(),
+ &AvahiRegisterQuery::handleServiceRegisteredStatic, this);
+ if (result != kDNSServiceErr_NoError) {
+ sdRef = NULL;
+ }*/
+ //MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+ }
+
+/*
+ static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) {
+ static_cast<AvahiRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain);
+ }
+
+ void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) {
+ if (errorCode != kDNSServiceErr_NoError) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+ else {
+ }
+ }
+ */
+
+ private:
+ String name;
+ int port;
+ ByteArray txtRecord;
+ AvahiEntryGroup* group;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h
new file mode 100644
index 0000000..ee0e837
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Network/HostAddress.h"
+
+#include <netinet/in.h>
+
+namespace Swift {
+ class AvahiQuerier;
+
+ class AvahiResolveHostnameQuery : public DNSSDResolveHostnameQuery, public AvahiQuery {
+ public:
+ AvahiResolveHostnameQuery(const String& hostname, int, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), hostname(hostname) {
+ std::cout << "Resolving hostname " << hostname << std::endl;
+ }
+
+ void run() {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>(HostAddress(hostname))), shared_from_this());
+ }
+
+ void finish() {
+ }
+
+ private:
+ HostAddress hostAddress;
+ String hostname;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h
new file mode 100644
index 0000000..8577837
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class AvahiQuerier;
+
+ class AvahiResolveServiceQuery : public DNSSDResolveServiceQuery, public AvahiQuery {
+ public:
+ AvahiResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), service(service), resolver(NULL) {
+ }
+
+ void start() {
+ std::cout << "Resolving " << service.getName() << std::endl;
+ avahi_threaded_poll_lock(querier->getThreadedPoll());
+ assert(!resolver);
+ resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().getUTF8Data(), service.getType().getUTF8Data(), service.getDomain().getUTF8Data(), AVAHI_PROTO_UNSPEC, (AvahiLookupFlags) 0, handleServiceResolvedStatic, this);
+ if (!resolver) {
+ std::cout << "Error starting resolver" << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+ }
+ avahi_threaded_poll_unlock(querier->getThreadedPoll());
+ }
+
+ void stop() {
+ avahi_threaded_poll_lock(querier->getThreadedPoll());
+ avahi_service_resolver_free(resolver);
+ resolver = NULL;
+ avahi_threaded_poll_unlock(querier->getThreadedPoll());
+ }
+
+ private:
+ static void handleServiceResolvedStatic(AvahiServiceResolver* resolver, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* context) {
+ static_cast<AvahiResolveServiceQuery*>(context)->handleServiceResolved(resolver, interfaceIndex, protocol, event, name, type, domain, host_name, address, port, txt, flags);
+ }
+
+ void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) {
+ std::cout << "Resolve finished" << std::endl;
+ switch(event) {
+ case AVAHI_RESOLVER_FAILURE:
+ std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+ break;
+ case AVAHI_RESOLVER_FOUND: {
+ char a[AVAHI_ADDRESS_STR_MAX];
+ avahi_address_snprint(a, sizeof(a), address);
+
+ ByteArray txtRecord;
+ txtRecord.resize(1024);
+ avahi_string_list_serialize(txt, txtRecord.getData(), txtRecord.getSize());
+
+ // FIXME: Probably not accurate
+ String fullname = String(name) + "." + String(type) + "." + String(domain) + ".";
+ std::cout << "Result: " << fullname << "->" << String(a) << ":" << port << std::endl;
+ MainEventLoop::postEvent(
+ boost::bind(
+ boost::ref(onServiceResolved),
+ Result(fullname, String(a), port, txtRecord)),
+ shared_from_this());
+ break;
+ }
+ }
+ }
+
+ private:
+ DNSSDServiceID service;
+ AvahiServiceResolver* resolver;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
new file mode 100644
index 0000000..2dec2fb
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class BonjourQuerier;
+
+ class BonjourBrowseQuery : public DNSSDBrowseQuery, public BonjourQuery {
+ public:
+ BonjourBrowseQuery(boost::shared_ptr<BonjourQuerier> q) : BonjourQuery(q) {
+ DNSServiceErrorType result = DNSServiceBrowse(
+ &sdRef, 0, 0, "_presence._tcp", 0,
+ &BonjourBrowseQuery::handleServiceDiscoveredStatic, this);
+ if (result != kDNSServiceErr_NoError) {
+ sdRef = NULL;
+ }
+ }
+
+ void startBrowsing() {
+ if (!sdRef) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+ }
+ else {
+ run();
+ }
+ }
+
+ void stopBrowsing() {
+ finish();
+ }
+
+ private:
+ static void handleServiceDiscoveredStatic(DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain, void *context) {
+ static_cast<BonjourBrowseQuery*>(context)->handleServiceDiscovered(flags, interfaceIndex, errorCode, name, type, domain);
+ }
+
+ void handleServiceDiscovered(DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain) {
+ if (errorCode != kDNSServiceErr_NoError) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this());
+ }
+ else {
+ //std::cout << "Discovered service: name:" << name << " domain:" << domain << " type: " << type << std::endl;
+ DNSSDServiceID service(name, domain, type, interfaceIndex);
+ if (flags & kDNSServiceFlagsAdd) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this());
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this());
+ }
+ }
+ }
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp
new file mode 100644
index 0000000..9c9e64e
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp
@@ -0,0 +1,129 @@
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h"
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+BonjourQuerier::BonjourQuerier() : stopRequested(false), thread(0) {
+ int fds[2];
+ int result = pipe(fds);
+ assert(result == 0);
+ interruptSelectReadSocket = fds[0];
+ fcntl(interruptSelectReadSocket, F_SETFL, fcntl(interruptSelectReadSocket, F_GETFL)|O_NONBLOCK);
+ interruptSelectWriteSocket = fds[1];
+}
+
+BonjourQuerier::~BonjourQuerier() {
+ assert(!thread);
+}
+
+boost::shared_ptr<DNSSDBrowseQuery> BonjourQuerier::createBrowseQuery() {
+ return boost::shared_ptr<DNSSDBrowseQuery>(new BonjourBrowseQuery(shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDRegisterQuery> BonjourQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) {
+ return boost::shared_ptr<DNSSDRegisterQuery>(new BonjourRegisterQuery(name, port, info, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveServiceQuery> BonjourQuerier::createResolveServiceQuery(const DNSSDServiceID& service) {
+ return boost::shared_ptr<DNSSDResolveServiceQuery>(new BonjourResolveServiceQuery(service, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveHostnameQuery> BonjourQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) {
+ return boost::shared_ptr<DNSSDResolveHostnameQuery>(new BonjourResolveHostnameQuery(hostname, interfaceIndex, shared_from_this()));
+}
+
+void BonjourQuerier::addRunningQuery(boost::shared_ptr<BonjourQuery> query) {
+ {
+ boost::lock_guard<boost::mutex> lock(runningQueriesMutex);
+ runningQueries.push_back(query);
+ }
+ runningQueriesAvailableEvent.notify_one();
+ interruptSelect();
+}
+
+void BonjourQuerier::removeRunningQuery(boost::shared_ptr<BonjourQuery> query) {
+ {
+ boost::lock_guard<boost::mutex> lock(runningQueriesMutex);
+ runningQueries.erase(std::remove(
+ runningQueries.begin(), runningQueries.end(), query), runningQueries.end());
+ }
+}
+
+void BonjourQuerier::interruptSelect() {
+ char c = 0;
+ write(interruptSelectWriteSocket, &c, 1);
+}
+
+void BonjourQuerier::start() {
+ assert(!thread);
+ thread = new boost::thread(boost::bind(&BonjourQuerier::run, shared_from_this()));
+}
+
+void BonjourQuerier::stop() {
+ if (thread) {
+ stopRequested = true;
+ assert(runningQueries.empty());
+ runningQueriesAvailableEvent.notify_one();
+ interruptSelect();
+ thread->join();
+ delete thread;
+ thread = NULL;
+ stopRequested = false;
+ }
+}
+
+void BonjourQuerier::run() {
+ while (!stopRequested) {
+ fd_set fdSet;
+ int maxSocket;
+ {
+ boost::unique_lock<boost::mutex> lock(runningQueriesMutex);
+ if (runningQueries.empty()) {
+ runningQueriesAvailableEvent.wait(lock);
+ if (runningQueries.empty()) {
+ continue;
+ }
+ }
+
+ // Run all running queries
+ FD_ZERO(&fdSet);
+ maxSocket = interruptSelectReadSocket;
+ FD_SET(interruptSelectReadSocket, &fdSet);
+
+ foreach(const boost::shared_ptr<BonjourQuery>& query, runningQueries) {
+ int socketID = query->getSocketID();
+ maxSocket = std::max(maxSocket, socketID);
+ FD_SET(socketID, &fdSet);
+ }
+ }
+
+ if (select(maxSocket+1, &fdSet, NULL, NULL, 0) <= 0) {
+ continue;
+ }
+
+ if (FD_ISSET(interruptSelectReadSocket, &fdSet)) {
+ char dummy;
+ while (read(interruptSelectReadSocket, &dummy, 1) > 0) {}
+ }
+
+ {
+ boost::lock_guard<boost::mutex> lock(runningQueriesMutex);
+ foreach(boost::shared_ptr<BonjourQuery> query, runningQueries) {
+ if (FD_ISSET(query->getSocketID(), &fdSet)) {
+ query->processResult();
+ }
+ }
+ }
+ }
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h
new file mode 100644
index 0000000..d12f94f
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <list>
+#include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h"
+
+namespace Swift {
+ class ByteArray;
+
+ class BonjourQuerier :
+ public DNSSDQuerier,
+ public boost::enable_shared_from_this<BonjourQuerier> {
+ public:
+ BonjourQuerier();
+ ~BonjourQuerier();
+
+ boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery();
+ boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
+ const String& name, int port, const ByteArray& info);
+ boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery(
+ const DNSSDServiceID&);
+ boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery(
+ const String& hostname, int interfaceIndex);
+
+ void start();
+ void stop();
+
+ private:
+ friend class BonjourQuery;
+
+ void addRunningQuery(boost::shared_ptr<BonjourQuery>);
+ void removeRunningQuery(boost::shared_ptr<BonjourQuery>);
+ void interruptSelect();
+ void run();
+
+ private:
+ bool stopRequested;
+ boost::thread* thread;
+ boost::mutex runningQueriesMutex;
+ std::list< boost::shared_ptr<BonjourQuery> > runningQueries;
+ int interruptSelectReadSocket;
+ int interruptSelectWriteSocket;
+ boost::condition_variable runningQueriesAvailableEvent;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp
new file mode 100644
index 0000000..c1c481b
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp
@@ -0,0 +1,31 @@
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h"
+
+namespace Swift {
+
+BonjourQuery::BonjourQuery(boost::shared_ptr<BonjourQuerier> q) : querier(q), sdRef(0) {
+}
+
+BonjourQuery::~BonjourQuery() {
+ DNSServiceRefDeallocate(sdRef);
+}
+
+void BonjourQuery::processResult() {
+ boost::lock_guard<boost::mutex> lock(sdRefMutex);
+ DNSServiceProcessResult(sdRef);
+}
+
+int BonjourQuery::getSocketID() const {
+ boost::lock_guard<boost::mutex> lock(sdRefMutex);
+ return DNSServiceRefSockFD(sdRef);
+}
+
+void BonjourQuery::run() {
+ querier->addRunningQuery(shared_from_this());
+}
+
+void BonjourQuery::finish() {
+ querier->removeRunningQuery(shared_from_this());
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h
new file mode 100644
index 0000000..bdb91a4
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <dns_sd.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+ class BonjourQuerier;
+
+ class BonjourQuery :
+ public EventOwner,
+ public boost::enable_shared_from_this<BonjourQuery> {
+ public:
+ BonjourQuery(boost::shared_ptr<BonjourQuerier>);
+ virtual ~BonjourQuery();
+
+ void processResult();
+ int getSocketID() const;
+
+ protected:
+ void run();
+ void finish();
+
+ protected:
+ boost::shared_ptr<BonjourQuerier> querier;
+ mutable boost::mutex sdRefMutex;
+ DNSServiceRef sdRef;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h
new file mode 100644
index 0000000..ddc2788
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class BonjourQuerier;
+
+ class BonjourRegisterQuery : public DNSSDRegisterQuery, public BonjourQuery {
+ public:
+ BonjourRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) {
+ DNSServiceErrorType result = DNSServiceRegister(
+ &sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port,
+ txtRecord.getSize(), txtRecord.getData(),
+ &BonjourRegisterQuery::handleServiceRegisteredStatic, this);
+ if (result != kDNSServiceErr_NoError) {
+ sdRef = NULL;
+ }
+ }
+
+ void registerService() {
+ if (sdRef) {
+ run();
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+ }
+
+ void unregisterService() {
+ finish();
+ }
+
+ void updateServiceInfo(const ByteArray& txtRecord) {
+ boost::lock_guard<boost::mutex> lock(sdRefMutex);
+ DNSServiceUpdateRecord(sdRef, NULL, NULL, txtRecord.getSize(), txtRecord.getData(), 0);
+ }
+
+ private:
+ static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) {
+ static_cast<BonjourRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain);
+ }
+
+ void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) {
+ if (errorCode != kDNSServiceErr_NoError) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, domain, regtype, 0))), shared_from_this());
+ }
+ }
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h
new file mode 100644
index 0000000..7b5f19a
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Network/HostAddress.h"
+
+#include <netinet/in.h>
+
+namespace Swift {
+ class BonjourQuerier;
+
+ class BonjourResolveHostnameQuery : public DNSSDResolveHostnameQuery, public BonjourQuery {
+ public:
+ BonjourResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) {
+ DNSServiceErrorType result = DNSServiceGetAddrInfo(
+ &sdRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4,
+ hostname.getUTF8Data(),
+ &BonjourResolveHostnameQuery::handleHostnameResolvedStatic, this);
+ if (result != kDNSServiceErr_NoError) {
+ sdRef = NULL;
+ }
+ }
+
+ //void DNSSDResolveHostnameQuery::run() {
+ void run() {
+ if (sdRef) {
+ BonjourQuery::run();
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>()), shared_from_this());
+ }
+ }
+
+ void finish() {
+ BonjourQuery::finish();
+ }
+
+ private:
+ static void handleHostnameResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char*, const struct sockaddr *address, uint32_t, void *context) {
+ static_cast<BonjourResolveHostnameQuery*>(context)->handleHostnameResolved(errorCode, address);
+ }
+
+ void handleHostnameResolved(DNSServiceErrorType errorCode, const struct sockaddr *rawAddress) {
+ if (errorCode) {
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onHostnameResolved),
+ boost::optional<HostAddress>()),
+ shared_from_this());
+ }
+ else {
+ assert(rawAddress->sa_family == AF_INET);
+ const sockaddr_in* sa = reinterpret_cast<const sockaddr_in*>(rawAddress);
+ uint32_t address = ntohl(sa->sin_addr.s_addr);
+ MainEventLoop::postEvent(boost::bind(
+ boost::ref(onHostnameResolved),
+ HostAddress(reinterpret_cast<unsigned char*>(&address), 4)),
+ shared_from_this());
+ }
+ }
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
new file mode 100644
index 0000000..1c38179
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class BonjourQuerier;
+
+ class BonjourResolveServiceQuery : public DNSSDResolveServiceQuery, public BonjourQuery {
+ public:
+ BonjourResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) {
+ DNSServiceErrorType result = DNSServiceResolve(
+ &sdRef, 0, service.getNetworkInterfaceID(),
+ service.getName().getUTF8Data(), service.getType().getUTF8Data(),
+ service.getDomain().getUTF8Data(),
+ &BonjourResolveServiceQuery::handleServiceResolvedStatic, this);
+ if (result != kDNSServiceErr_NoError) {
+ sdRef = NULL;
+ }
+ }
+
+ void start() {
+ if (sdRef) {
+ run();
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+ }
+ }
+
+ void stop() {
+ finish();
+ }
+
+ private:
+ static void handleServiceResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) {
+ static_cast<BonjourResolveServiceQuery*>(context)->handleServiceResolved(errorCode, fullname, hosttarget, port, txtLen, txtRecord);
+ }
+
+ void handleServiceResolved(DNSServiceErrorType errorCode, const char* fullName, const char* host, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) {
+ if (errorCode != kDNSServiceErr_NoError) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this());
+ }
+ else {
+ //std::cout << "Service resolved: name:" << fullName << " host:" << host << " port:" << port << std::endl;
+ MainEventLoop::postEvent(
+ boost::bind(
+ boost::ref(onServiceResolved),
+ Result(String(fullName), String(host), port,
+ ByteArray(reinterpret_cast<const char*>(txtRecord), txtLen))),
+ shared_from_this());
+ }
+ }
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp
new file mode 100644
index 0000000..1dbff2c
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+
+namespace Swift {
+
+DNSSDBrowseQuery::~DNSSDBrowseQuery() {
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h
new file mode 100644
index 0000000..9b30858
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <boost/signal.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+
+namespace Swift {
+ class DNSSDBrowseQuery {
+ public:
+ virtual ~DNSSDBrowseQuery();
+
+ virtual void startBrowsing() = 0;
+ virtual void stopBrowsing() = 0;
+
+ boost::signal<void (const DNSSDServiceID&)> onServiceAdded;
+ boost::signal<void (const DNSSDServiceID&)> onServiceRemoved;
+ boost::signal<void ()> onError;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp
new file mode 100644
index 0000000..cc8d6ef
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+
+namespace Swift {
+
+DNSSDQuerier::~DNSSDQuerier() {
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h
new file mode 100644
index 0000000..efcc140
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class String;
+ class ByteArray;
+ class DNSSDServiceID;
+ class DNSSDBrowseQuery;
+ class DNSSDRegisterQuery;
+ class DNSSDResolveServiceQuery;
+ class DNSSDResolveHostnameQuery;
+
+ class DNSSDQuerier {
+ public:
+ virtual ~DNSSDQuerier();
+
+ virtual void start() = 0;
+ virtual void stop() = 0;
+
+ virtual boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery() = 0;
+ virtual boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
+ const String& name, int port, const ByteArray& info) = 0;
+ virtual boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery(
+ const DNSSDServiceID&) = 0;
+ virtual boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery(
+ const String& hostname, int interfaceIndex) = 0;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp
new file mode 100644
index 0000000..bbb8692
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+
+namespace Swift {
+
+DNSSDRegisterQuery::~DNSSDRegisterQuery() {
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h
new file mode 100644
index 0000000..4a04fa9
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+
+namespace Swift {
+ class ByteArray;
+
+ class DNSSDRegisterQuery {
+ public:
+ virtual ~DNSSDRegisterQuery();
+
+ virtual void registerService() = 0;
+ virtual void unregisterService() = 0;
+ virtual void updateServiceInfo(const ByteArray& info) = 0;
+
+ boost::signal<void (boost::optional<DNSSDServiceID>)> onRegisterFinished;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp
new file mode 100644
index 0000000..e247e39
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+
+namespace Swift {
+
+DNSSDResolveHostnameQuery::~DNSSDResolveHostnameQuery() {
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h
new file mode 100644
index 0000000..1b9f291
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+ class DNSSDResolveHostnameQuery {
+ public:
+ virtual ~DNSSDResolveHostnameQuery();
+
+ virtual void run() = 0;
+ virtual void finish() = 0;
+
+ boost::signal<void (const boost::optional<HostAddress>&)> onHostnameResolved;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp
new file mode 100644
index 0000000..67a5d66
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+
+namespace Swift {
+
+DNSSDResolveServiceQuery::~DNSSDResolveServiceQuery() {
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h
new file mode 100644
index 0000000..217e396
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class DNSSDResolveServiceQuery {
+ public:
+ struct Result {
+ Result(const String& fullName, const String& host, int port, const ByteArray& info) :
+ fullName(fullName), host(host), port(port), info(info) {}
+ String fullName;
+ String host;
+ int port;
+ ByteArray info;
+ };
+
+ virtual ~DNSSDResolveServiceQuery();
+
+ virtual void start() = 0;
+ virtual void stop() = 0;
+
+ boost::signal<void (const boost::optional<Result>&)> onServiceResolved;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp
new file mode 100644
index 0000000..b360ee5
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp
@@ -0,0 +1,7 @@
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+
+namespace Swift {
+
+const char* DNSSDServiceID::PresenceServiceType = "_presence._tcp";
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h
new file mode 100644
index 0000000..ba7828b
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class DNSSDServiceID {
+ public:
+ static const char* PresenceServiceType;
+
+ DNSSDServiceID(
+ const String& name,
+ const String& domain,
+ const String& type = PresenceServiceType,
+ int networkInterface = 0) :
+ name(name),
+ domain(domain),
+ type(type),
+ networkInterface(networkInterface) {
+ }
+
+ bool operator==(const DNSSDServiceID& o) const {
+ return name == o.name && domain == o.domain && type == o.type && (networkInterface != 0 && o.networkInterface != 0 ? networkInterface == o.networkInterface : true);
+ }
+
+ bool operator<(const DNSSDServiceID& o) const {
+ if (o.name == name) {
+ if (o.domain == domain) {
+ if (o.type == type) {
+ return networkInterface < o.networkInterface;
+ }
+ else {
+ return type < o.type;
+ }
+ }
+ else {
+ return domain < o.domain;
+ }
+ }
+ else {
+ return o.name < name;
+ }
+ }
+
+ const String& getName() const {
+ return name;
+ }
+
+ const String& getDomain() const {
+ return domain;
+ }
+
+ const String& getType() const {
+ return type;
+ }
+
+ int getNetworkInterfaceID() const {
+ return networkInterface;
+ }
+
+ private:
+ String name;
+ String domain;
+ String type;
+ int networkInterface;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h
new file mode 100644
index 0000000..5a0b93b
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+
+namespace Swift {
+ class FakeDNSSDQuerier;
+
+ class FakeDNSSDBrowseQuery : public DNSSDBrowseQuery, public FakeDNSSDQuery {
+ public:
+ FakeDNSSDBrowseQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier) {
+ }
+
+ void startBrowsing() {
+ run();
+ }
+
+ void stopBrowsing() {
+ finish();
+ }
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp
new file mode 100644
index 0000000..c26f8ee
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp
@@ -0,0 +1,130 @@
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+FakeDNSSDQuerier::FakeDNSSDQuerier(const String& domain) : domain(domain) {
+}
+
+FakeDNSSDQuerier::~FakeDNSSDQuerier() {
+ if (!runningQueries.empty()) {
+ std::cerr << "FakeDNSSDQuerier: Running queries not empty at destruction time" << std::endl;
+ }
+}
+
+boost::shared_ptr<DNSSDBrowseQuery> FakeDNSSDQuerier::createBrowseQuery() {
+ return boost::shared_ptr<DNSSDBrowseQuery>(new FakeDNSSDBrowseQuery(shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDRegisterQuery> FakeDNSSDQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) {
+ return boost::shared_ptr<DNSSDRegisterQuery>(new FakeDNSSDRegisterQuery(name, port, info, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveServiceQuery> FakeDNSSDQuerier::createResolveServiceQuery(const DNSSDServiceID& service) {
+ return boost::shared_ptr<DNSSDResolveServiceQuery>(new FakeDNSSDResolveServiceQuery(service, shared_from_this()));
+}
+
+boost::shared_ptr<DNSSDResolveHostnameQuery> FakeDNSSDQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) {
+ return boost::shared_ptr<DNSSDResolveHostnameQuery>(new FakeDNSSDResolveHostnameQuery(hostname, interfaceIndex, shared_from_this()));
+}
+
+void FakeDNSSDQuerier::addRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) {
+ runningQueries.push_back(query);
+ if (boost::shared_ptr<FakeDNSSDBrowseQuery> browseQuery = boost::dynamic_pointer_cast<FakeDNSSDBrowseQuery>(query)) {
+ foreach(const DNSSDServiceID& service, services) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(browseQuery->onServiceAdded), service), shared_from_this());
+ }
+ }
+ else if (boost::shared_ptr<FakeDNSSDResolveServiceQuery> resolveQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveServiceQuery>(query)) {
+ for(ServiceInfoMap::const_iterator i = serviceInfo.begin(); i != serviceInfo.end(); ++i) {
+ if (i->first == resolveQuery->service) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(resolveQuery->onServiceResolved), i->second), shared_from_this());
+ }
+ }
+ }
+ else if (boost::shared_ptr<FakeDNSSDRegisterQuery> registerQuery = boost::dynamic_pointer_cast<FakeDNSSDRegisterQuery>(query)) {
+ DNSSDServiceID service(registerQuery->name, domain);
+ MainEventLoop::postEvent(boost::bind(boost::ref(registerQuery->onRegisterFinished), service), shared_from_this());
+ }
+ else if (boost::shared_ptr<FakeDNSSDResolveHostnameQuery> resolveHostnameQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveHostnameQuery>(query)) {
+ std::map<String,boost::optional<HostAddress> >::const_iterator i = addresses.find(resolveHostnameQuery->hostname);
+ if (i != addresses.end()) {
+ MainEventLoop::postEvent(
+ boost::bind(
+ boost::ref(resolveHostnameQuery->onHostnameResolved), i->second),
+ shared_from_this());
+ }
+ }
+}
+
+void FakeDNSSDQuerier::removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) {
+ runningQueries.erase(std::remove(
+ runningQueries.begin(), runningQueries.end(), query), runningQueries.end());
+}
+
+void FakeDNSSDQuerier::addService(const DNSSDServiceID& id) {
+ services.insert(id);
+ foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceAdded), id), shared_from_this());
+ }
+}
+
+void FakeDNSSDQuerier::removeService(const DNSSDServiceID& id) {
+ services.erase(id);
+ serviceInfo.erase(id);
+ foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceRemoved), id), shared_from_this());
+ }
+}
+
+void FakeDNSSDQuerier::setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info) {
+ std::pair<ServiceInfoMap::iterator, bool> r = serviceInfo.insert(std::make_pair(id, info));
+ if (!r.second) {
+ r.first->second = info;
+ }
+ foreach(const boost::shared_ptr<FakeDNSSDResolveServiceQuery>& query, getQueries<FakeDNSSDResolveServiceQuery>()) {
+ if (query->service == id) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceResolved), info), shared_from_this());
+ }
+ }
+}
+
+bool FakeDNSSDQuerier::isServiceRegistered(const String& name, int port, const ByteArray& info) {
+ foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) {
+ if (query->name == name && query->port == port && query->info == info) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void FakeDNSSDQuerier::setBrowseError() {
+ foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) {
+ MainEventLoop::postEvent(boost::ref(query->onError), shared_from_this());
+ }
+}
+
+void FakeDNSSDQuerier::setRegisterError() {
+ foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(query->onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
+ }
+}
+
+void FakeDNSSDQuerier::setAddress(const String& hostname, boost::optional<HostAddress> address) {
+ addresses[hostname] = address;
+ foreach(const boost::shared_ptr<FakeDNSSDResolveHostnameQuery>& query, getQueries<FakeDNSSDResolveHostnameQuery>()) {
+ if (query->hostname == hostname) {
+ MainEventLoop::postEvent(boost::bind(
+ boost::ref(query->onHostnameResolved), address), shared_from_this());
+ }
+ }
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
new file mode 100644
index 0000000..22bca0c
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <list>
+#include <set>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/EventLoop/EventOwner.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+ class ByteArray;
+ class FakeDNSSDQuery;
+ class FakeDNSSDBrowseQuery;
+
+ class FakeDNSSDQuerier :
+ public DNSSDQuerier,
+ public EventOwner,
+ public boost::enable_shared_from_this<FakeDNSSDQuerier> {
+ public:
+ FakeDNSSDQuerier(const String& domain);
+ ~FakeDNSSDQuerier();
+
+ void start() {}
+ void stop() {}
+
+ boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery();
+ boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery(
+ const String& name, int port, const ByteArray& info);
+ boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery(
+ const DNSSDServiceID&);
+ boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery(
+ const String& hostname, int interfaceIndex);
+
+ void addRunningQuery(boost::shared_ptr<FakeDNSSDQuery>);
+ void removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery>);
+
+ void addService(const DNSSDServiceID& id);
+ void removeService(const DNSSDServiceID& id);
+ void setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info);
+ bool isServiceRegistered(const String& name, int port, const ByteArray& info);
+ void setAddress(const String& hostname, boost::optional<HostAddress> address);
+
+ void setBrowseError();
+ void setRegisterError();
+
+ private:
+ template<typename T>
+ std::vector< boost::shared_ptr<T> > getQueries() const {
+ std::vector< boost::shared_ptr<T> > result;
+ foreach(const boost::shared_ptr<FakeDNSSDQuery>& query, runningQueries) {
+ if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(query)) {
+ result.push_back(resultQuery);
+ }
+ }
+ return result;
+ }
+
+ private:
+ String domain;
+ std::list< boost::shared_ptr<FakeDNSSDQuery> > runningQueries;
+ std::set<DNSSDServiceID> services;
+ typedef std::map<DNSSDServiceID,DNSSDResolveServiceQuery::Result> ServiceInfoMap;
+ ServiceInfoMap serviceInfo;
+ std::map<String, boost::optional<HostAddress> > addresses;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp
new file mode 100644
index 0000000..ced7850
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp
@@ -0,0 +1,20 @@
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
+
+namespace Swift {
+
+FakeDNSSDQuery::FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : querier(querier) {
+}
+
+FakeDNSSDQuery::~FakeDNSSDQuery() {
+}
+
+void FakeDNSSDQuery::run() {
+ querier->addRunningQuery(shared_from_this());
+}
+
+void FakeDNSSDQuery::finish() {
+ querier->removeRunningQuery(shared_from_this());
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h
new file mode 100644
index 0000000..9fca1d4
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+ class FakeDNSSDQuerier;
+
+ class FakeDNSSDQuery :
+ public EventOwner,
+ public boost::enable_shared_from_this<FakeDNSSDQuery> {
+ public:
+ FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier>);
+ virtual ~FakeDNSSDQuery();
+
+ protected:
+ void run();
+ void finish();
+
+ protected:
+ boost::shared_ptr<FakeDNSSDQuerier> querier;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h
new file mode 100644
index 0000000..82ec623
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class FakeDNSSDQuerier;
+
+ class FakeDNSSDRegisterQuery : public DNSSDRegisterQuery, public FakeDNSSDQuery {
+ public:
+ FakeDNSSDRegisterQuery(const String& name, int port, const ByteArray& info, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), name(name), port(port), info(info) {
+ }
+
+ void registerService() {
+ run();
+ }
+
+ void updateServiceInfo(const ByteArray& i) {
+ info = i;
+ }
+
+ void unregisterService() {
+ finish();
+ }
+
+ String name;
+ int port;
+ ByteArray info;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h
new file mode 100644
index 0000000..5ed42af
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+ class FakeDNSSDQuerier;
+
+ class FakeDNSSDResolveHostnameQuery : public DNSSDResolveHostnameQuery, public FakeDNSSDQuery {
+ public:
+ FakeDNSSDResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), hostname(hostname), interfaceIndex(interfaceIndex) {
+ }
+
+ void run() {
+ FakeDNSSDQuery::run();
+ }
+
+ void finish() {
+ FakeDNSSDQuery::finish();
+ }
+
+ String hostname;
+ int interfaceIndex;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h
new file mode 100644
index 0000000..60d35e5
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+
+namespace Swift {
+ class FakeDNSSDQuerier;
+
+ class FakeDNSSDResolveServiceQuery : public DNSSDResolveServiceQuery, public FakeDNSSDQuery {
+ public:
+ FakeDNSSDResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), service(service) {
+ }
+
+ void start() {
+ run();
+ }
+
+ void stop() {
+ finish();
+ }
+
+ DNSSDServiceID service;
+ };
+}
diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp
new file mode 100644
index 0000000..7b06350
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h"
+
+#if defined(HAVE_BONJOUR)
+#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h"
+#elif defined(HAVE_AVAHI)
+#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h"
+#endif
+
+
+namespace Swift {
+
+boost::shared_ptr<DNSSDQuerier> PlatformDNSSDQuerierFactory::createQuerier() {
+#if defined(HAVE_BONJOUR)
+ return boost::shared_ptr<DNSSDQuerier>(new BonjourQuerier());
+#elif defined(HAVE_AVAHI)
+ return boost::shared_ptr<DNSSDQuerier>(new AvahiQuerier());
+#else
+ return boost::shared_ptr<DNSSDQuerier>();
+#endif
+}
+
+}
diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h
new file mode 100644
index 0000000..b52814b
--- /dev/null
+++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class DNSSDQuerier;
+
+ class PlatformDNSSDQuerierFactory {
+ public:
+ boost::shared_ptr<DNSSDQuerier> createQuerier();
+ };
+}
diff --git a/Swiften/LinkLocal/IncomingLinkLocalSession.cpp b/Swiften/LinkLocal/IncomingLinkLocalSession.cpp
new file mode 100644
index 0000000..77910e6
--- /dev/null
+++ b/Swiften/LinkLocal/IncomingLinkLocalSession.cpp
@@ -0,0 +1,62 @@
+#include "Swiften/LinkLocal/IncomingLinkLocalSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/StreamStack/StreamStack.h"
+#include "Swiften/StreamStack/ConnectionLayer.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+
+IncomingLinkLocalSession::IncomingLinkLocalSession(
+ const JID& localJID,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers) :
+ Session(connection, payloadParserFactories, payloadSerializers),
+ initialized(false) {
+ setLocalJID(localJID);
+}
+
+void IncomingLinkLocalSession::handleStreamStart(const ProtocolHeader& incomingHeader) {
+ setRemoteJID(JID(incomingHeader.getFrom()));
+ if (!getRemoteJID().isValid()) {
+ finishSession();
+ return;
+ }
+
+ ProtocolHeader header;
+ header.setFrom(getLocalJID());
+ getXMPPLayer()->writeHeader(header);
+
+ if (incomingHeader.getVersion() == "1.0") {
+ getXMPPLayer()->writeElement(boost::shared_ptr<StreamFeatures>(new StreamFeatures()));
+ }
+ else {
+ setInitialized();
+ }
+}
+
+void IncomingLinkLocalSession::handleElement(boost::shared_ptr<Element> element) {
+ boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element);
+ // If we get our first stanza before streamfeatures, our session is implicitly
+ // initialized
+ if (stanza && !isInitialized()) {
+ setInitialized();
+ }
+
+ onElementReceived(element);
+}
+
+void IncomingLinkLocalSession::setInitialized() {
+ initialized = true;
+ onSessionStarted();
+}
+
+
+
+}
diff --git a/Swiften/LinkLocal/IncomingLinkLocalSession.h b/Swiften/LinkLocal/IncomingLinkLocalSession.h
new file mode 100644
index 0000000..e3f0460
--- /dev/null
+++ b/Swiften/LinkLocal/IncomingLinkLocalSession.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Session/Session.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Network/Connection.h"
+
+namespace Swift {
+ class ProtocolHeader;
+ class String;
+ class Element;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+
+ class IncomingLinkLocalSession : public Session {
+ public:
+ IncomingLinkLocalSession(
+ const JID& localJID,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers);
+
+ boost::signal<void ()> onSessionStarted;
+
+ private:
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamStart(const ProtocolHeader&);
+ void setInitialized();
+ bool isInitialized() const {
+ return initialized;
+ }
+
+ bool initialized;
+ };
+}
diff --git a/Swiften/LinkLocal/LinkLocalConnector.cpp b/Swiften/LinkLocal/LinkLocalConnector.cpp
new file mode 100644
index 0000000..fba4a4e
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalConnector.cpp
@@ -0,0 +1,68 @@
+#include "Swiften/LinkLocal/LinkLocalConnector.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/Network/HostAddress.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+
+namespace Swift {
+
+LinkLocalConnector::LinkLocalConnector(
+ const LinkLocalService& service,
+ boost::shared_ptr<DNSSDQuerier> querier,
+ boost::shared_ptr<Connection> connection) :
+ service(service),
+ querier(querier),
+ connection(connection) {
+}
+
+LinkLocalConnector::~LinkLocalConnector() {
+ assert(!resolveQuery);
+}
+
+void LinkLocalConnector::connect() {
+ resolveQuery = querier->createResolveHostnameQuery(
+ service.getHostname(),
+ service.getID().getNetworkInterfaceID());
+ resolveQuery->onHostnameResolved.connect(boost::bind(
+ &LinkLocalConnector::handleHostnameResolved,
+ boost::dynamic_pointer_cast<LinkLocalConnector>(shared_from_this()),
+ _1));
+ resolveQuery->run();
+}
+
+void LinkLocalConnector::cancel() {
+ if (resolveQuery) {
+ resolveQuery->finish();
+ }
+ resolveQuery.reset();
+ connection->disconnect();
+}
+
+void LinkLocalConnector::handleHostnameResolved(const boost::optional<HostAddress>& address) {
+ resolveQuery->finish();
+ resolveQuery.reset();
+ if (address) {
+ connection->onConnectFinished.connect(
+ boost::bind(boost::ref(onConnectFinished), _1));
+ connection->connect(HostAddressPort(*address, service.getPort()));
+ }
+ else {
+ onConnectFinished(true);
+ }
+}
+
+void LinkLocalConnector::handleConnected(bool error) {
+ onConnectFinished(error);
+}
+
+void LinkLocalConnector::queueElement(boost::shared_ptr<Element> element) {
+ queuedElements.push_back(element);
+}
+
+
+}
diff --git a/Swiften/LinkLocal/LinkLocalConnector.h b/Swiften/LinkLocal/LinkLocalConnector.h
new file mode 100644
index 0000000..0b6baef
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalConnector.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <vector>
+
+#include "Swiften/Network/Connection.h"
+#include "Swiften/LinkLocal/LinkLocalService.h"
+
+namespace Swift {
+ class ConnectionFactory;
+ class HostAddress;
+ class Element;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+ class DNSSDQuerier;
+ class DNSSDResolveHostnameQuery;
+
+ class LinkLocalConnector : public boost::enable_shared_from_this<LinkLocalConnector> {
+ public:
+ LinkLocalConnector(
+ const LinkLocalService& service,
+ boost::shared_ptr<DNSSDQuerier> querier,
+ boost::shared_ptr<Connection> connection);
+ ~LinkLocalConnector();
+
+ const LinkLocalService& getService() const {
+ return service;
+ }
+
+ void connect();
+ void cancel();
+ void queueElement(boost::shared_ptr<Element> element);
+
+ const std::vector<boost::shared_ptr<Element> >& getQueuedElements() const {
+ return queuedElements;
+ }
+
+ boost::shared_ptr<Connection> getConnection() const {
+ return connection;
+ }
+
+ boost::signal<void (bool)> onConnectFinished;
+
+ private:
+ void handleHostnameResolved(const boost::optional<HostAddress>& address);
+ void handleConnected(bool error);
+
+ private:
+ LinkLocalService service;
+ boost::shared_ptr<DNSSDQuerier> querier;
+ boost::shared_ptr<DNSSDResolveHostnameQuery> resolveQuery;
+ boost::shared_ptr<Connection> connection;
+ std::vector<boost::shared_ptr<Element> > queuedElements;
+ };
+}
diff --git a/Swiften/LinkLocal/LinkLocalService.cpp b/Swiften/LinkLocal/LinkLocalService.cpp
new file mode 100644
index 0000000..f1114ed
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalService.cpp
@@ -0,0 +1,27 @@
+#include "Swiften/LinkLocal/LinkLocalService.h"
+
+namespace Swift {
+
+String LinkLocalService::getDescription() const {
+ LinkLocalServiceInfo info = getInfo();
+ if (!info.getNick().isEmpty()) {
+ return info.getNick();
+ }
+ else if (!info.getFirstName().isEmpty()) {
+ String result = info.getFirstName();
+ if (!info.getLastName().isEmpty()) {
+ result += " " + info.getLastName();
+ }
+ return result;
+ }
+ else if (!info.getLastName().isEmpty()) {
+ return info.getLastName();
+ }
+ return getName();
+}
+
+JID LinkLocalService::getJID() const {
+ return JID(getName());
+}
+
+}
diff --git a/Swiften/LinkLocal/LinkLocalService.h b/Swiften/LinkLocal/LinkLocalService.h
new file mode 100644
index 0000000..8ae593c
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalService.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+
+namespace Swift {
+ class LinkLocalService {
+ public:
+ LinkLocalService(
+ const DNSSDServiceID& id,
+ const DNSSDResolveServiceQuery::Result& info) :
+ id(id),
+ info(info) {}
+
+ const DNSSDServiceID& getID() const {
+ return id;
+ }
+
+ const String& getName() const {
+ return id.getName();
+ }
+
+ int getPort() const {
+ return info.port;
+ }
+
+ const String& getHostname() const {
+ return info.host;
+ }
+
+ LinkLocalServiceInfo getInfo() const {
+ return LinkLocalServiceInfo::createFromTXTRecord(info.info);
+ }
+
+ String getDescription() const;
+
+ JID getJID() const;
+
+ private:
+ DNSSDServiceID id;
+ DNSSDResolveServiceQuery::Result info;
+ };
+}
diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
new file mode 100644
index 0000000..061bf2c
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp
@@ -0,0 +1,147 @@
+#include <boost/bind.hpp>
+#include <iostream>
+
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h"
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+
+LinkLocalServiceBrowser::LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier) : querier(querier), haveError(false) {
+}
+
+LinkLocalServiceBrowser::~LinkLocalServiceBrowser() {
+ if (isRunning()) {
+ std::cerr << "WARNING: LinkLocalServiceBrowser still running on destruction" << std::endl;
+ }
+}
+
+
+void LinkLocalServiceBrowser::start() {
+ assert(!isRunning());
+ haveError = false;
+ browseQuery = querier->createBrowseQuery();
+ browseQuery->onServiceAdded.connect(
+ boost::bind(&LinkLocalServiceBrowser::handleServiceAdded, this, _1));
+ browseQuery->onServiceRemoved.connect(
+ boost::bind(&LinkLocalServiceBrowser::handleServiceRemoved, this, _1));
+ browseQuery->onError.connect(
+ boost::bind(&LinkLocalServiceBrowser::handleBrowseError, this));
+ browseQuery->startBrowsing();
+}
+
+void LinkLocalServiceBrowser::stop() {
+ assert(isRunning());
+ if (isRegistered()) {
+ unregisterService();
+ }
+ for (ResolveQueryMap::const_iterator i = resolveQueries.begin(); i != resolveQueries.end(); ++i) {
+ i->second->stop();
+ }
+ resolveQueries.clear();
+ services.clear();
+ browseQuery->stopBrowsing();
+ browseQuery.reset();
+ onStopped(haveError);
+}
+
+bool LinkLocalServiceBrowser::isRunning() const {
+ return browseQuery;
+}
+
+bool LinkLocalServiceBrowser::hasError() const {
+ return haveError;
+}
+
+bool LinkLocalServiceBrowser::isRegistered() const {
+ return registerQuery;
+}
+
+void LinkLocalServiceBrowser::registerService(const String& name, int port, const LinkLocalServiceInfo& info) {
+ assert(!registerQuery);
+ registerQuery = querier->createRegisterQuery(name, port, info.toTXTRecord());
+ registerQuery->onRegisterFinished.connect(
+ boost::bind(&LinkLocalServiceBrowser::handleRegisterFinished, this, _1));
+ registerQuery->registerService();
+}
+
+void LinkLocalServiceBrowser::updateService(const LinkLocalServiceInfo& info) {
+ assert(registerQuery);
+ registerQuery->updateServiceInfo(info.toTXTRecord());
+}
+
+void LinkLocalServiceBrowser::unregisterService() {
+ assert(registerQuery);
+ registerQuery->unregisterService();
+ registerQuery.reset();
+ selfService.reset();
+}
+
+std::vector<LinkLocalService> LinkLocalServiceBrowser::getServices() const {
+ std::vector<LinkLocalService> result;
+ for (ServiceMap::const_iterator i = services.begin(); i != services.end(); ++i) {
+ result.push_back(LinkLocalService(i->first, i->second));
+ }
+ return result;
+}
+
+void LinkLocalServiceBrowser::handleServiceAdded(const DNSSDServiceID& service) {
+ if (selfService && service == *selfService) {
+ return;
+ }
+ boost::shared_ptr<DNSSDResolveServiceQuery> resolveQuery = querier->createResolveServiceQuery(service);
+ resolveQuery->onServiceResolved.connect(
+ boost::bind(&LinkLocalServiceBrowser::handleServiceResolved, this, service, _1));
+ std::pair<ResolveQueryMap::iterator, bool> r = resolveQueries.insert(std::make_pair(service, resolveQuery));
+ if (!r.second) {
+ r.first->second = resolveQuery;
+ }
+ resolveQuery->start();
+}
+
+void LinkLocalServiceBrowser::handleServiceRemoved(const DNSSDServiceID& service) {
+ ResolveQueryMap::iterator i = resolveQueries.find(service);
+ if (i == resolveQueries.end()) {
+ // Can happen after an unregister(), when getting the old 'self'
+ // service remove notification.
+ return;
+ }
+ i->second->stop();
+ resolveQueries.erase(i);
+ ServiceMap::iterator j = services.find(service);
+ assert(j != services.end());
+ LinkLocalService linkLocalService(j->first, j->second);
+ services.erase(j);
+ onServiceRemoved(linkLocalService);
+}
+
+void LinkLocalServiceBrowser::handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result) {
+ if (result) {
+ std::pair<ServiceMap::iterator, bool> r = services.insert(std::make_pair(service, *result));
+ if (r.second) {
+ onServiceAdded(LinkLocalService(r.first->first, r.first->second));
+ }
+ else {
+ r.first->second = *result;
+ onServiceChanged(LinkLocalService(r.first->first, r.first->second));
+ }
+ }
+}
+
+void LinkLocalServiceBrowser::handleRegisterFinished(const boost::optional<DNSSDServiceID>& result) {
+ if (result) {
+ selfService = result;
+ onServiceRegistered(*result);
+ }
+ else {
+ haveError = true;
+ stop();
+ }
+}
+
+void LinkLocalServiceBrowser::handleBrowseError() {
+ haveError = true;
+ stop();
+}
+
+}
diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.h b/Swiften/LinkLocal/LinkLocalServiceBrowser.h
new file mode 100644
index 0000000..66973d5
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <map>
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+#include "Swiften/LinkLocal/LinkLocalService.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+
+namespace Swift {
+ class LinkLocalServiceBrowser {
+ public:
+ LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier);
+ ~LinkLocalServiceBrowser();
+
+ void start();
+ void stop();
+ bool isRunning() const;
+ bool hasError() const;
+
+ void registerService(
+ const String& name,
+ int port,
+ const LinkLocalServiceInfo& info = LinkLocalServiceInfo());
+ void updateService(
+ const LinkLocalServiceInfo& info = LinkLocalServiceInfo());
+ void unregisterService();
+ bool isRegistered() const;
+
+ std::vector<LinkLocalService> getServices() const;
+
+ // FIXME: Ugly that we need this
+ boost::shared_ptr<DNSSDQuerier> getQuerier() const {
+ return querier;
+ }
+
+ boost::signal<void (const LinkLocalService&)> onServiceAdded;
+ boost::signal<void (const LinkLocalService&)> onServiceChanged;
+ boost::signal<void (const LinkLocalService&)> onServiceRemoved;
+ boost::signal<void (const DNSSDServiceID&)> onServiceRegistered;
+ boost::signal<void (bool)> onStopped;
+
+ private:
+ void handleServiceAdded(const DNSSDServiceID&);
+ void handleServiceRemoved(const DNSSDServiceID&);
+ void handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result);
+ void handleRegisterFinished(const boost::optional<DNSSDServiceID>&);
+ void handleBrowseError();
+
+ private:
+ boost::shared_ptr<DNSSDQuerier> querier;
+ boost::optional<DNSSDServiceID> selfService;
+ boost::shared_ptr<DNSSDBrowseQuery> browseQuery;
+ boost::shared_ptr<DNSSDRegisterQuery> registerQuery;
+ typedef std::map<DNSSDServiceID, boost::shared_ptr<DNSSDResolveServiceQuery> > ResolveQueryMap;
+ ResolveQueryMap resolveQueries;
+ typedef std::map<DNSSDServiceID, DNSSDResolveServiceQuery::Result> ServiceMap;
+ ServiceMap services;
+ bool haveError;
+ };
+}
diff --git a/Swiften/LinkLocal/LinkLocalServiceInfo.cpp b/Swiften/LinkLocal/LinkLocalServiceInfo.cpp
new file mode 100644
index 0000000..8ee7ae0
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalServiceInfo.cpp
@@ -0,0 +1,114 @@
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+
+#include <boost/lexical_cast.hpp>
+
+namespace Swift {
+
+ByteArray LinkLocalServiceInfo::toTXTRecord() const {
+ ByteArray result(getEncoded("txtvers=1"));
+ if (!firstName.isEmpty()) {
+ result += getEncoded("1st=" + firstName);
+ }
+ if (!lastName.isEmpty()) {
+ result += getEncoded("last=" + lastName);
+ }
+ if (!email.isEmpty()) {
+ result += getEncoded("email=" + email);
+ }
+ if (jid.isValid()) {
+ result += getEncoded("jid=" + jid.toString());
+ }
+ if (!message.isEmpty()) {
+ result += getEncoded("msg=" + message);
+ }
+ if (!nick.isEmpty()) {
+ result += getEncoded("nick=" + nick);
+ }
+ if (port) {
+ result += getEncoded("port.p2pj=" + String(boost::lexical_cast<std::string>(*port)));
+ }
+
+ switch (status) {
+ case Available: result += getEncoded("status=avail"); break;
+ case Away: result += getEncoded("status=away"); break;
+ case DND: result += getEncoded("status=dnd"); break;
+ }
+
+ return result;
+}
+
+ByteArray LinkLocalServiceInfo::getEncoded(const String& s) {
+ ByteArray sizeByte;
+ sizeByte.resize(1);
+ assert(s.getLength() < 256);
+ sizeByte[0] = s.getUTF8Size();
+ return sizeByte + ByteArray(s);
+}
+
+LinkLocalServiceInfo LinkLocalServiceInfo::createFromTXTRecord(const ByteArray& record) {
+ LinkLocalServiceInfo info;
+ size_t i = 0;
+ while (i < record.getSize()) {
+ std::pair<String,String> entry = readEntry(record, &i);
+ if (entry.first.isEmpty()) {
+ break;
+ }
+ else if (entry.first == "1st") {
+ info.setFirstName(entry.second);
+ }
+ else if (entry.first == "last") {
+ info.setLastName(entry.second);
+ }
+ else if (entry.first == "email") {
+ info.setEMail(entry.second);
+ }
+ else if (entry.first == "jid") {
+ info.setJID(JID(entry.second));
+ }
+ else if (entry.first == "msg") {
+ info.setMessage(entry.second);
+ }
+ else if (entry.first == "nick") {
+ info.setNick(entry.second);
+ }
+ else if (entry.first == "port.p2pj") {
+ info.setPort(boost::lexical_cast<int>(entry.second));
+ }
+ else if (entry.first == "status") {
+ if (entry.second == "away") {
+ info.setStatus(Away);
+ }
+ else if (entry.second == "dnd") {
+ info.setStatus(DND);
+ }
+ }
+ }
+ return info;
+}
+
+std::pair<String,String> LinkLocalServiceInfo::readEntry(const ByteArray& record, size_t* index) {
+ size_t& i = *index;
+ String key;
+ String value;
+
+ size_t entryEnd = i + 1 + record[i];
+ ++i;
+ bool inKey = true;
+ while (i < entryEnd && i < record.getSize()) {
+ if (inKey) {
+ if (record[i] == '=') {
+ inKey = false;
+ }
+ else {
+ key += record[i];
+ }
+ }
+ else {
+ value += record[i];
+ }
+ ++i;
+ }
+ return std::make_pair(key, value);
+}
+
+}
diff --git a/Swiften/LinkLocal/LinkLocalServiceInfo.h b/Swiften/LinkLocal/LinkLocalServiceInfo.h
new file mode 100644
index 0000000..d78b70c
--- /dev/null
+++ b/Swiften/LinkLocal/LinkLocalServiceInfo.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+
+ class LinkLocalServiceInfo {
+ public:
+ enum Status { Available, Away, DND };
+
+ LinkLocalServiceInfo() : status(Available) {}
+
+ const String& getFirstName() const { return firstName; }
+ void setFirstName(const String& f) { firstName = f; }
+
+ const String& getLastName() const { return lastName; }
+ void setLastName(const String& l) { lastName = l; }
+
+ const String& getEMail() const { return email; }
+ void setEMail(const String& e) { email = e; }
+
+ const JID& getJID() const { return jid; }
+ void setJID(const JID& j) { jid = j; }
+
+ const String& getMessage() const { return message; }
+ void setMessage(const String& m) { message = m; }
+
+ const String& getNick() const { return nick; }
+ void setNick(const String& n) { nick = n; }
+
+ Status getStatus() const { return status; }
+ void setStatus(Status s) { status = s; }
+
+ boost::optional<int> getPort() const { return port; }
+ void setPort(int p) { port = p; }
+
+ ByteArray toTXTRecord() const;
+
+ static LinkLocalServiceInfo createFromTXTRecord(const ByteArray& record);
+
+ private:
+ static ByteArray getEncoded(const String&);
+ static std::pair<String,String> readEntry(const ByteArray&, size_t*);
+
+ private:
+ String firstName;
+ String lastName;
+ String email;
+ JID jid;
+ String message;
+ String nick;
+ Status status;
+ boost::optional<int> port;
+ };
+}
diff --git a/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp b/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp
new file mode 100644
index 0000000..7b71f82
--- /dev/null
+++ b/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp
@@ -0,0 +1,45 @@
+#include "Swiften/LinkLocal/OutgoingLinkLocalSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/IQ.h"
+
+namespace Swift {
+
+OutgoingLinkLocalSession::OutgoingLinkLocalSession(
+ const JID& localJID,
+ const JID& remoteJID,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers) :
+ Session(connection, payloadParserFactories, payloadSerializers) {
+ setLocalJID(localJID);
+ setRemoteJID(remoteJID);
+}
+
+void OutgoingLinkLocalSession::handleSessionStarted() {
+ ProtocolHeader header;
+ header.setFrom(getLocalJID());
+ getXMPPLayer()->writeHeader(header);
+}
+
+void OutgoingLinkLocalSession::handleStreamStart(const ProtocolHeader&) {
+ foreach(const boost::shared_ptr<Element>& stanza, queuedElements_) {
+ sendElement(stanza);
+ }
+ queuedElements_.clear();
+}
+
+void OutgoingLinkLocalSession::handleElement(boost::shared_ptr<Element> element) {
+ onElementReceived(element);
+}
+
+void OutgoingLinkLocalSession::queueElement(boost::shared_ptr<Element> element) {
+ queuedElements_.push_back(element);
+}
+
+
+}
diff --git a/Swiften/LinkLocal/OutgoingLinkLocalSession.h b/Swiften/LinkLocal/OutgoingLinkLocalSession.h
new file mode 100644
index 0000000..32f78e4
--- /dev/null
+++ b/Swiften/LinkLocal/OutgoingLinkLocalSession.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <vector>
+
+#include "Swiften/Session/Session.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class ConnectionFactory;
+ class String;
+ class Element;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+
+ class OutgoingLinkLocalSession : public Session {
+ public:
+ OutgoingLinkLocalSession(
+ const JID& localJID,
+ const JID& remoteJID,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers);
+
+ void queueElement(boost::shared_ptr<Element> element);
+
+ private:
+ void handleSessionStarted();
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamStart(const ProtocolHeader&);
+
+ private:
+ std::vector<boost::shared_ptr<Element> > queuedElements_;
+ };
+}
diff --git a/Swiften/LinkLocal/SConscript b/Swiften/LinkLocal/SConscript
new file mode 100644
index 0000000..b929db1
--- /dev/null
+++ b/Swiften/LinkLocal/SConscript
@@ -0,0 +1,37 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env.get("BONJOUR_FLAGS", ""))
+
+sources = [
+ "DNSSD/DNSSDBrowseQuery.cpp",
+ "DNSSD/DNSSDQuerier.cpp",
+ "DNSSD/DNSSDRegisterQuery.cpp",
+ "DNSSD/DNSSDResolveHostnameQuery.cpp",
+ "DNSSD/DNSSDResolveServiceQuery.cpp",
+ "DNSSD/DNSSDServiceID.cpp",
+ "DNSSD/Fake/FakeDNSSDQuerier.cpp",
+ "DNSSD/Fake/FakeDNSSDQuery.cpp",
+ "DNSSD/PlatformDNSSDQuerierFactory.cpp",
+ "IncomingLinkLocalSession.cpp",
+ "LinkLocalConnector.cpp",
+ "LinkLocalService.cpp",
+ "LinkLocalServiceBrowser.cpp",
+ "LinkLocalServiceInfo.cpp",
+ "OutgoingLinkLocalSession.cpp",
+ ]
+
+if myenv.get("HAVE_BONJOUR", 0) :
+ myenv.Append(CPPDEFINES = "HAVE_BONJOUR")
+ sources += [
+ "DNSSD/Bonjour/BonjourQuerier.cpp",
+ "DNSSD/Bonjour/BonjourQuery.cpp",
+ ]
+elif myenv.get("HAVE_AVAHI", 0) :
+ sources += [
+ "DNSSD/Avahi/AvahiQuerier.cpp",
+ "DNSSD/Avahi/AvahiQuery.cpp"
+ ]
+
+objects = myenv.StaticObject(sources)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp
new file mode 100644
index 0000000..ee5e414
--- /dev/null
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp
@@ -0,0 +1,135 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/LinkLocal/LinkLocalConnector.h"
+#include "Swiften/LinkLocal/LinkLocalService.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+#include "Swiften/Network/FakeConnection.h"
+
+using namespace Swift;
+
+class LinkLocalConnectorTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(LinkLocalConnectorTest);
+ CPPUNIT_TEST(testConnect);
+ CPPUNIT_TEST(testConnect_UnableToResolve);
+ CPPUNIT_TEST(testConnect_UnableToConnect);
+ CPPUNIT_TEST(testCancel_DuringResolve);
+ CPPUNIT_TEST(testCancel_DuringConnect);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ querier = boost::shared_ptr<FakeDNSSDQuerier>(
+ new FakeDNSSDQuerier("rabbithole.local"));
+ connection = boost::shared_ptr<FakeConnection>(new FakeConnection());
+ connectFinished = false;
+ }
+
+ void tearDown() {
+ delete eventLoop;
+ }
+
+ void testConnect() {
+ boost::shared_ptr<LinkLocalConnector>
+ testling(createConnector("rabbithole.local", 1234));
+ querier->setAddress("rabbithole.local", HostAddress("192.168.1.1"));
+
+ testling->connect();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(connectFinished);
+ CPPUNIT_ASSERT(!connectError);
+ CPPUNIT_ASSERT(connection->connectedTo);
+ CPPUNIT_ASSERT_EQUAL(String(connection->connectedTo->getAddress().toString()), String("192.168.1.1"));
+ CPPUNIT_ASSERT_EQUAL(connection->connectedTo->getPort(), 1234);
+ }
+
+ void testConnect_UnableToResolve() {
+ boost::shared_ptr<LinkLocalConnector>
+ testling(createConnector("rabbithole.local", 1234));
+ querier->setAddress("rabbithole.local", boost::optional<HostAddress>());
+
+ testling->connect();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(connectFinished);
+ CPPUNIT_ASSERT(connectError);
+ CPPUNIT_ASSERT(!connection->connectedTo);
+ }
+
+ void testConnect_UnableToConnect() {
+ boost::shared_ptr<LinkLocalConnector>
+ testling(createConnector("rabbithole.local", 1234));
+ querier->setAddress("rabbithole.local", HostAddress("192.168.1.1"));
+ connection->setError(Connection::ReadError);
+
+ testling->connect();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(connectFinished);
+ CPPUNIT_ASSERT(connectError);
+ CPPUNIT_ASSERT(!connection->connectedTo);
+ }
+
+ void testCancel_DuringResolve() {
+ boost::shared_ptr<LinkLocalConnector>
+ testling(createConnector("rabbithole.local", 1234));
+ testling->connect();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT(!connectFinished);
+
+ testling->cancel();
+ eventLoop->processEvents();
+ querier->setAddress("rabbithole.local", HostAddress("192.168.1.1"));
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(FakeConnection::Disconnected == connection->state);
+ }
+
+ void testCancel_DuringConnect() {
+ boost::shared_ptr<LinkLocalConnector>
+ testling(createConnector("rabbithole.local", 1234));
+ querier->setAddress("rabbithole.local", HostAddress("192.168.1.1"));
+ connection->setDelayConnect();
+ testling->connect();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT(FakeConnection::Connecting == connection->state);
+
+ testling->cancel();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(FakeConnection::Disconnected == connection->state);
+ }
+
+ private:
+ boost::shared_ptr<LinkLocalConnector> createConnector(const String& hostname, int port) {
+ LinkLocalService service(
+ DNSSDServiceID("myname", "local."),
+ DNSSDResolveServiceQuery::Result(
+ "myname._presence._tcp.local", hostname, port,
+ LinkLocalServiceInfo().toTXTRecord()));
+ boost::shared_ptr<LinkLocalConnector> result(
+ new LinkLocalConnector(service, querier, connection));
+ result->onConnectFinished.connect(
+ boost::bind(&LinkLocalConnectorTest::handleConnected, this, _1));
+ return result;
+ }
+
+ void handleConnected(bool e) {
+ connectFinished = true;
+ connectError = e;
+ }
+
+ private:
+ DummyEventLoop* eventLoop;
+ boost::shared_ptr<FakeDNSSDQuerier> querier;
+ boost::shared_ptr<FakeConnection> connection;
+ bool connectFinished;
+ bool connectError;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalConnectorTest);
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
new file mode 100644
index 0000000..9f91269
--- /dev/null
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp
@@ -0,0 +1,379 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <map>
+
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
+#include "Swiften/LinkLocal/LinkLocalService.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h"
+#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"
+#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+using namespace Swift;
+
+class LinkLocalServiceBrowserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(LinkLocalServiceBrowserTest);
+ CPPUNIT_TEST(testConstructor);
+ CPPUNIT_TEST(testStart);
+ CPPUNIT_TEST(testServiceAdded);
+ CPPUNIT_TEST(testServiceAdded_NoServiceInfo);
+ CPPUNIT_TEST(testServiceAdded_RegisteredService);
+ CPPUNIT_TEST(testServiceAdded_UnregisteredService);
+ CPPUNIT_TEST(testServiceChanged);
+ CPPUNIT_TEST(testServiceRemoved);
+ CPPUNIT_TEST(testServiceRemoved_UnregisteredService);
+ CPPUNIT_TEST(testError_BrowseErrorAfterStart);
+ CPPUNIT_TEST(testError_BrowseErrorAfterResolve);
+ CPPUNIT_TEST(testRegisterService);
+ CPPUNIT_TEST(testRegisterService_Error);
+ CPPUNIT_TEST(testRegisterService_Reregister);
+ CPPUNIT_TEST(testUpdateService);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ LinkLocalServiceBrowserTest() {}
+
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ querier = boost::shared_ptr<FakeDNSSDQuerier>(new FakeDNSSDQuerier("wonderland.lit"));
+ aliceServiceID = new DNSSDServiceID("alice", "wonderland.lit");
+ aliceServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.wonderland.lit", "xmpp.wonderland.lit", 1234, LinkLocalServiceInfo().toTXTRecord());
+ testServiceID = new DNSSDServiceID("foo", "bar.local");
+ testServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.bar.local", "xmpp.bar.local", 1234, LinkLocalServiceInfo().toTXTRecord());
+ testServiceInfo2 = new DNSSDResolveServiceQuery::Result("_presence.tcp.bar.local", "xmpp.foo.local", 2345, LinkLocalServiceInfo().toTXTRecord());
+ errorStopReceived = false;
+ normalStopReceived = false;
+ }
+
+ void tearDown() {
+ addedServices.clear();
+ removedServices.clear();
+ changedServices.clear();
+
+ delete aliceServiceID;
+ delete aliceServiceInfo;
+ delete testServiceInfo2;
+ delete testServiceInfo;
+ delete testServiceID;
+ delete eventLoop;
+ }
+
+ void testConstructor() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+
+ CPPUNIT_ASSERT(!testling->isRunning());
+ CPPUNIT_ASSERT(!testling->hasError());
+ }
+
+ void testStart() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+
+ CPPUNIT_ASSERT(testling->isRunning());
+ CPPUNIT_ASSERT(!testling->hasError());
+
+ testling->stop();
+ }
+
+ void testServiceAdded() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+
+ querier->setServiceInfo(*testServiceID,*testServiceInfo);
+ querier->addService(*testServiceID);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
+ CPPUNIT_ASSERT(addedServices[0].getID() == *testServiceID);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
+ std::vector<LinkLocalService> services = testling->getServices();
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size()));
+ CPPUNIT_ASSERT(*testServiceID == services[0].getID());
+ CPPUNIT_ASSERT(testServiceInfo->port == services[0].getPort());
+ CPPUNIT_ASSERT(testServiceInfo->host == services[0].getHostname());
+
+ testling->stop();
+ }
+
+ void testServiceAdded_NoServiceInfo() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+
+ querier->addService(*testServiceID);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(addedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
+
+ testling->stop();
+ }
+
+ void testServiceAdded_RegisteredService() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+
+ testling->registerService("alice", 1234, LinkLocalServiceInfo());
+ eventLoop->processEvents();
+ querier->setServiceInfo(*aliceServiceID, *aliceServiceInfo);
+ querier->addService(*aliceServiceID);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(addedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
+
+ testling->stop();
+ }
+
+ void testServiceAdded_UnregisteredService() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+ testling->registerService("alice", 1234, LinkLocalServiceInfo());
+ eventLoop->processEvents();
+ testling->unregisterService();
+ eventLoop->processEvents();
+
+ querier->setServiceInfo(*aliceServiceID, *aliceServiceInfo);
+ querier->addService(*aliceServiceID);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
+ CPPUNIT_ASSERT(addedServices[0].getID() == *aliceServiceID);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
+ std::vector<LinkLocalService> services = testling->getServices();
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size()));
+ CPPUNIT_ASSERT(*aliceServiceID == services[0].getID());
+ CPPUNIT_ASSERT(aliceServiceInfo->port == services[0].getPort());
+ CPPUNIT_ASSERT(aliceServiceInfo->host == services[0].getHostname());
+
+ testling->stop();
+ }
+
+ void testServiceRemoved_UnregisteredService() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+ testling->registerService("alice", 1234, LinkLocalServiceInfo());
+ eventLoop->processEvents();
+ testling->unregisterService();
+ eventLoop->processEvents();
+
+ querier->removeService(*aliceServiceID);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
+
+ testling->stop();
+ }
+
+ void testServiceChanged() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ querier->setServiceInfo(*testServiceID,*testServiceInfo);
+ querier->addService(*testServiceID);
+ eventLoop->processEvents();
+
+ querier->setServiceInfo(*testServiceID,*testServiceInfo2);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size()));
+ CPPUNIT_ASSERT(changedServices[0].getID() == *testServiceID);
+ std::vector<LinkLocalService> services = testling->getServices();
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size()));
+ CPPUNIT_ASSERT(*testServiceID == services[0].getID());
+ CPPUNIT_ASSERT(testServiceInfo2->port == services[0].getPort());
+ CPPUNIT_ASSERT(testServiceInfo2->host == services[0].getHostname());
+
+ testling->stop();
+ }
+
+ void testServiceRemoved() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ querier->setServiceInfo(*testServiceID,*testServiceInfo);
+ querier->addService(*testServiceID);
+ eventLoop->processEvents();
+
+ querier->removeService(*testServiceID);
+ eventLoop->processEvents();
+ querier->setServiceInfo(*testServiceID,*testServiceInfo2);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(removedServices.size()));
+ CPPUNIT_ASSERT(removedServices[0].getID() == *testServiceID);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
+
+ testling->stop();
+ }
+
+ void testError_BrowseErrorAfterStart() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+
+ querier->setBrowseError();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(!testling->isRunning());
+ CPPUNIT_ASSERT(testling->hasError());
+ CPPUNIT_ASSERT(errorStopReceived);
+ }
+
+ void testError_BrowseErrorAfterResolve() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ querier->setServiceInfo(*testServiceID,*testServiceInfo);
+ querier->addService(*testServiceID);
+ eventLoop->processEvents();
+
+ querier->setBrowseError();
+ eventLoop->processEvents();
+ querier->setServiceInfo(*testServiceID,*testServiceInfo2);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(!testling->isRunning());
+ CPPUNIT_ASSERT(testling->hasError());
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size()));
+ CPPUNIT_ASSERT(errorStopReceived);
+ }
+
+ void testRegisterService() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+
+ LinkLocalServiceInfo info;
+ info.setFirstName("Foo");
+ testling->registerService("foo@bar", 1234, info);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(registeredServices.size()));
+ CPPUNIT_ASSERT(registeredServices[0] == DNSSDServiceID("foo@bar", "wonderland.lit"));
+ testling->stop();
+ }
+
+ void testRegisterService_Error() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ LinkLocalServiceInfo info;
+ testling->registerService("foo@bar", 1234, info);
+ eventLoop->processEvents();
+
+ querier->setRegisterError();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(!testling->isRunning());
+ CPPUNIT_ASSERT(testling->hasError());
+ CPPUNIT_ASSERT(errorStopReceived);
+ CPPUNIT_ASSERT(!querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord()));
+ }
+
+ void testRegisterService_Reregister() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+ LinkLocalServiceInfo info;
+ info.setFirstName("Foo");
+ testling->registerService("foo@bar", 1234, info);
+ eventLoop->processEvents();
+ testling->unregisterService();
+ eventLoop->processEvents();
+
+ info.setFirstName("Bar");
+ testling->registerService("bar@baz", 3456, info);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT(querier->isServiceRegistered("bar@baz", 3456, info.toTXTRecord()));
+
+ testling->stop();
+ }
+
+ void testUpdateService() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling();
+ testling->start();
+ eventLoop->processEvents();
+
+ LinkLocalServiceInfo info;
+ info.setFirstName("Foo");
+ testling->registerService("foo@bar", 1234, info);
+ eventLoop->processEvents();
+ info.setFirstName("Bar");
+ testling->updateService(info);
+
+ CPPUNIT_ASSERT(querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord()));
+
+ testling->stop();
+ }
+
+ private:
+ boost::shared_ptr<LinkLocalServiceBrowser> createTestling() {
+ boost::shared_ptr<LinkLocalServiceBrowser> testling(
+ new LinkLocalServiceBrowser(querier));
+ testling->onServiceAdded.connect(boost::bind(
+ &LinkLocalServiceBrowserTest::handleServiceAdded, this, _1));
+ testling->onServiceChanged.connect(boost::bind(
+ &LinkLocalServiceBrowserTest::handleServiceChanged, this, _1));
+ testling->onServiceRemoved.connect(boost::bind(
+ &LinkLocalServiceBrowserTest::handleServiceRemoved, this, _1));
+ testling->onServiceRegistered.connect(boost::bind(
+ &LinkLocalServiceBrowserTest::handleServiceRegistered, this, _1));
+ testling->onStopped.connect(boost::bind(
+ &LinkLocalServiceBrowserTest::handleStopped, this, _1));
+ return testling;
+ }
+
+ void handleServiceAdded(const LinkLocalService& service) {
+ addedServices.push_back(service);
+ }
+
+ void handleServiceRemoved(const LinkLocalService& service) {
+ removedServices.push_back(service);
+ }
+
+ void handleServiceChanged(const LinkLocalService& service) {
+ changedServices.push_back(service);
+ }
+
+ void handleServiceRegistered(const DNSSDServiceID& service) {
+ registeredServices.push_back(service);
+ }
+
+ void handleStopped(bool error) {
+ CPPUNIT_ASSERT(!errorStopReceived);
+ CPPUNIT_ASSERT(!normalStopReceived);
+ if (error) {
+ errorStopReceived = true;
+ }
+ else {
+ normalStopReceived = true;
+ }
+ }
+
+ private:
+ DummyEventLoop* eventLoop;
+ boost::shared_ptr<FakeDNSSDQuerier> querier;
+ std::vector<LinkLocalService> addedServices;
+ std::vector<LinkLocalService> changedServices;
+ std::vector<LinkLocalService> removedServices;
+ std::vector<DNSSDServiceID> registeredServices;
+ DNSSDServiceID* aliceServiceID;
+ DNSSDResolveServiceQuery::Result* aliceServiceInfo;
+ DNSSDServiceID* testServiceID;
+ DNSSDResolveServiceQuery::Result* testServiceInfo;
+ DNSSDResolveServiceQuery::Result* testServiceInfo2;
+ bool errorStopReceived;
+ bool normalStopReceived;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceBrowserTest);
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp
new file mode 100644
index 0000000..b5d7ef5
--- /dev/null
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp
@@ -0,0 +1,63 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+
+using namespace Swift;
+
+class LinkLocalServiceInfoTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(LinkLocalServiceInfoTest);
+ CPPUNIT_TEST(testGetTXTRecord);
+ CPPUNIT_TEST(testCreateFromTXTRecord);
+ CPPUNIT_TEST(testCreateFromTXTRecord_InvalidSize);
+ CPPUNIT_TEST(testGetTXTRecordCreateFromTXTRecord_RoundTrip);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetTXTRecord() {
+ LinkLocalServiceInfo info;
+ info.setFirstName("Remko");
+ info.setLastName("Tron\xc3\xe7on");
+ info.setStatus(LinkLocalServiceInfo::Away);
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x09txtvers=1\x09" + String("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away")), info.toTXTRecord());
+ }
+
+ void testCreateFromTXTRecord() {
+ LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(ByteArray("\x09txtvers=1\x09" + String("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away")));
+
+ CPPUNIT_ASSERT_EQUAL(String("Remko"), info.getFirstName());
+ CPPUNIT_ASSERT_EQUAL(String("Tron\xc3\xe7on"), info.getLastName());
+ CPPUNIT_ASSERT_EQUAL(LinkLocalServiceInfo::Away, info.getStatus());
+ }
+
+ void testCreateFromTXTRecord_InvalidSize() {
+ LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(ByteArray("\x10last=a"));
+
+ CPPUNIT_ASSERT_EQUAL(String("a"), info.getLastName());
+ }
+
+ void testGetTXTRecordCreateFromTXTRecord_RoundTrip() {
+ LinkLocalServiceInfo info;
+ info.setFirstName("Remko");
+ info.setLastName("Tron\xc3\xe7on");
+ info.setEMail("remko-email@swift.im");
+ info.setJID(JID("remko-jid@swift.im"));
+ info.setMessage("I'm busy");
+ info.setNick("el-tramo");
+ info.setStatus(LinkLocalServiceInfo::DND);
+ info.setPort(1234);
+
+ LinkLocalServiceInfo info2 = LinkLocalServiceInfo::createFromTXTRecord(info.toTXTRecord());
+ CPPUNIT_ASSERT_EQUAL(info.getFirstName(), info2.getFirstName());
+ CPPUNIT_ASSERT_EQUAL(info.getLastName(), info2.getLastName());
+ CPPUNIT_ASSERT_EQUAL(info.getEMail(), info2.getEMail());
+ CPPUNIT_ASSERT_EQUAL(info.getJID(), info2.getJID());
+ CPPUNIT_ASSERT_EQUAL(info.getMessage(), info2.getMessage());
+ CPPUNIT_ASSERT_EQUAL(info.getNick(), info2.getNick());
+ CPPUNIT_ASSERT(info.getStatus() == info2.getStatus());
+ CPPUNIT_ASSERT(info.getPort() == info2.getPort());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceInfoTest);
diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp
new file mode 100644
index 0000000..69ec718
--- /dev/null
+++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp
@@ -0,0 +1,62 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/LinkLocal/LinkLocalService.h"
+
+using namespace Swift;
+
+class LinkLocalServiceTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(LinkLocalServiceTest);
+ CPPUNIT_TEST(testGetDescription_WithNick);
+ CPPUNIT_TEST(testGetDescription_WithFirstName);
+ CPPUNIT_TEST(testGetDescription_WithLastName);
+ CPPUNIT_TEST(testGetDescription_WithFirstAndLastName);
+ CPPUNIT_TEST(testGetDescription_NoInfo);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetDescription_WithNick() {
+ LinkLocalService testling = createService("alice@wonderland", "Alice", "Alice In", "Wonderland");
+
+ CPPUNIT_ASSERT_EQUAL(String("Alice"), testling.getDescription());
+ }
+
+ void testGetDescription_WithFirstName() {
+ LinkLocalService testling = createService("alice@wonderland", "", "Alice In");
+
+ CPPUNIT_ASSERT_EQUAL(String("Alice In"), testling.getDescription());
+ }
+
+ void testGetDescription_WithLastName() {
+ LinkLocalService testling = createService("alice@wonderland", "", "", "Wonderland");
+
+ CPPUNIT_ASSERT_EQUAL(String("Wonderland"), testling.getDescription());
+ }
+
+ void testGetDescription_WithFirstAndLastName() {
+ LinkLocalService testling = createService("alice@wonderland", "", "Alice In", "Wonderland");
+
+ CPPUNIT_ASSERT_EQUAL(String("Alice In Wonderland"), testling.getDescription());
+ }
+
+ void testGetDescription_NoInfo() {
+ LinkLocalService testling = createService("alice@wonderland");
+
+ CPPUNIT_ASSERT_EQUAL(String("alice@wonderland"), testling.getDescription());
+ }
+
+ private:
+ LinkLocalService createService(const String& name, const String& nickName = String(), const String& firstName = String(), const String& lastName = String()) {
+ DNSSDServiceID service(name, "local.");
+ LinkLocalServiceInfo info;
+ info.setFirstName(firstName);
+ info.setLastName(lastName);
+ info.setNick(nickName);
+ return LinkLocalService(service,
+ DNSSDResolveServiceQuery::Result(
+ name + "._presence._tcp.local", "rabbithole.local", 1234,
+ info.toTXTRecord()));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceTest);
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
new file mode 100644
index 0000000..3c06c7a
--- /dev/null
+++ b/Swiften/MUC/MUC.cpp
@@ -0,0 +1,70 @@
+#include "Swiften/MUC/MUC.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/MUCPayload.h"
+
+namespace Swift {
+
+typedef std::pair<String, MUCOccupant> StringMUCOccupantPair;
+
+MUC::MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc) : ownMUCJID(muc), stanzaChannel(stanzaChannel), presenceSender(presenceSender) {
+ scopedConnection_ = stanzaChannel->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
+}
+
+void MUC::joinAs(const String &nick) {
+ firstPresenceSeen = false;
+
+ ownMUCJID = JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
+
+ boost::shared_ptr<Presence> joinPresence(new Presence());
+ joinPresence->setTo(ownMUCJID);
+ joinPresence->addPayload(boost::shared_ptr<Payload>(new MUCPayload()));
+ presenceSender->sendPresence(joinPresence);
+}
+
+void MUC::part() {
+ presenceSender->removeDirectedPresenceReceiver(ownMUCJID);
+}
+
+void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ if (!isFromMUC(presence->getFrom())) {
+ return;
+ }
+
+ if (!firstPresenceSeen) {
+ if (presence->getType() == Presence::Error) {
+ onJoinComplete(JoinFailed);
+ return;
+ }
+ firstPresenceSeen = true;
+ onJoinComplete(JoinSucceeded);
+ presenceSender->addDirectedPresenceReceiver(ownMUCJID);
+ }
+
+ String nick = presence->getFrom().getResource();
+ if (nick.isEmpty()) {
+ return;
+ }
+ if (presence->getType() == Presence::Unavailable) {
+ std::map<String,MUCOccupant>::iterator i = occupants.find(nick);
+ if (i != occupants.end()) {
+ onOccupantLeft(i->second, Part, "");
+ occupants.erase(i);
+ }
+ }
+ else if (presence->getType() == Presence::Available) {
+ std::pair<std::map<String,MUCOccupant>::iterator, bool> result = occupants.insert(std::make_pair(nick, MUCOccupant(nick)));
+ if (result.second) {
+ onOccupantJoined(result.first->second);
+ }
+ onOccupantPresenceChange(presence);
+ }
+}
+
+
+}
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
new file mode 100644
index 0000000..1ef974f
--- /dev/null
+++ b/Swiften/MUC/MUC.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#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/shared_ptr.hpp>
+#include <boost/signals.hpp>
+#include <boost/signals/connection.hpp>
+
+#include <map>
+
+namespace Swift {
+ class StanzaChannel;
+ class PresenceSender;
+
+ class MUC {
+ public:
+ enum JoinResult { JoinSucceeded, JoinFailed };
+ enum LeavingType { Part };
+
+ public:
+ MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &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<Presence>)> onOccupantPresenceChange;
+ boost::signal<void (const MUCOccupant&)> onOccupantJoined;
+ /**Occupant, type, and reason. */
+ boost::signal<void (const MUCOccupant&, LeavingType, const String&)> onOccupantLeft;
+
+ private:
+ bool isFromMUC(const JID& j) const {
+ return ownMUCJID.equals(j, JID::WithoutResource);
+ }
+
+ const String& getOwnNick() const {
+ return ownMUCJID.getResource();
+ }
+
+ private:
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+
+ private:
+ JID ownMUCJID;
+ StanzaChannel* stanzaChannel;
+ PresenceSender* presenceSender;
+ std::map<String, MUCOccupant> occupants;
+ bool firstPresenceSeen;
+ boost::bsignals::scoped_connection scopedConnection_;
+ };
+}
diff --git a/Swiften/MUC/MUCBookmark.h b/Swiften/MUC/MUCBookmark.h
new file mode 100644
index 0000000..439e716
--- /dev/null
+++ b/Swiften/MUC/MUCBookmark.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class MUCBookmark {
+ public:
+ MUCBookmark(const JID& room, const String& bookmarkName) : room_(room), name_(bookmarkName){};
+ void setAutojoin(bool enabled) {autojoin_ = enabled;};
+ void setNick(const boost::optional<String>& nick) {nick_ = nick;};
+ void setPassword(const boost::optional<String>& password) {password_ = password;};
+ bool getAutojoin() const {return autojoin_;};
+ const boost::optional<String>& getNick() const {return nick_;};
+ const boost::optional<String>& getPassword() const {return password_;};
+ const String& getName() const {return name_;};
+ const JID& getRoom() const {return room_;};
+ private:
+ JID room_;
+ String name_;
+ boost::optional<String> nick_;
+ boost::optional<String> password_;
+ bool autojoin_;
+ };
+}
+
diff --git a/Swiften/MUC/MUCBookmarkManager.cpp b/Swiften/MUC/MUCBookmarkManager.cpp
new file mode 100644
index 0000000..f9295e2
--- /dev/null
+++ b/Swiften/MUC/MUCBookmarkManager.cpp
@@ -0,0 +1,39 @@
+#include "MUCBookmarkManager.h"
+
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+MUCBookmarkManager::MUCBookmarkManager(IQRouter* iqRouter) {
+ iqRouter_ = iqRouter;
+}
+
+void MUCBookmarkManager::addBookmark(boost::shared_ptr<MUCBookmark> bookmark) {
+ bookmarks_.push_back(bookmark);
+ flush();
+ onBookmarkAdded(bookmark);
+}
+
+
+void MUCBookmarkManager::removeBookmark(boost::shared_ptr<MUCBookmark> bookmark) {
+ std::vector<boost::shared_ptr<MUCBookmark> >::iterator it;
+ for (it = bookmarks_.begin(); it != bookmarks_.end(); it++) {
+ if ((*it).get() == bookmark.get()) {
+ bookmarks_.erase(it);
+ onBookmarkRemoved(bookmark);
+ return;
+ }
+ }
+ assert(false);
+ flush();
+}
+
+void MUCBookmarkManager::flush() {
+ //FIXME: some code may be useful
+}
+
+const std::vector<boost::shared_ptr<MUCBookmark> >& MUCBookmarkManager::getBookmarks() {
+ return bookmarks_;
+}
+
+}
diff --git a/Swiften/MUC/MUCBookmarkManager.h b/Swiften/MUC/MUCBookmarkManager.h
new file mode 100644
index 0000000..ade2e3e
--- /dev/null
+++ b/Swiften/MUC/MUCBookmarkManager.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signals.hpp>
+
+#include "Swiften/MUC/MUCBookmark.h"
+
+namespace Swift {
+ class IQRouter;
+ class MUCBookmarkManager {
+ public:
+ MUCBookmarkManager(IQRouter* iqRouter);
+ void addBookmark(boost::shared_ptr<MUCBookmark> bookmark);
+ void removeBookmark(boost::shared_ptr<MUCBookmark> bookmark);
+ /** Call flush after editing an existing bookmark. */
+ void flush();
+ /** Returns pointers to the bookmarks. These can be edited, and then flush()ed.*/
+ const std::vector<boost::shared_ptr<MUCBookmark> >& getBookmarks();
+ boost::signal<void (boost::shared_ptr<MUCBookmark>)> onBookmarkAdded;
+ boost::signal<void (boost::shared_ptr<MUCBookmark>)> onBookmarkRemoved;
+ private:
+
+ std::vector<boost::shared_ptr<MUCBookmark> > bookmarks_;
+ IQRouter* iqRouter_;
+ };
+}
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/MUCRegistry.cpp b/Swiften/MUC/MUCRegistry.cpp
new file mode 100644
index 0000000..95bab08
--- /dev/null
+++ b/Swiften/MUC/MUCRegistry.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/MUC/MUCRegistry.h"
+
+namespace Swift {
+
+MUCRegistry::~MUCRegistry() {
+}
+
+}
diff --git a/Swiften/MUC/MUCRegistry.h b/Swiften/MUC/MUCRegistry.h
new file mode 100644
index 0000000..a843abb
--- /dev/null
+++ b/Swiften/MUC/MUCRegistry.h
@@ -0,0 +1,12 @@
+#pragma once
+
+namespace Swift {
+ class JID;
+
+ class MUCRegistry {
+ public:
+ virtual ~MUCRegistry();
+
+ virtual bool isMUC(const JID&) const = 0;
+ };
+}
diff --git a/Swiften/Network/BoostConnection.cpp b/Swiften/Network/BoostConnection.cpp
new file mode 100644
index 0000000..0d62300
--- /dev/null
+++ b/Swiften/Network/BoostConnection.cpp
@@ -0,0 +1,109 @@
+#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/HostAddressPort.h"
+
+namespace Swift {
+
+static const size_t BUFFER_SIZE = 4096;
+
+// -----------------------------------------------------------------------------
+
+// A reference-counted non-modifiable buffer class.
+class SharedBuffer {
+ public:
+ SharedBuffer(const ByteArray& data) :
+ data_(new std::vector<char>(data.begin(), data.end())),
+ buffer_(boost::asio::buffer(*data_)) {
+ }
+
+ // ConstBufferSequence requirements.
+ typedef boost::asio::const_buffer value_type;
+ typedef const boost::asio::const_buffer* const_iterator;
+ const boost::asio::const_buffer* begin() const { return &buffer_; }
+ const boost::asio::const_buffer* end() const { return &buffer_ + 1; }
+
+ private:
+ boost::shared_ptr< std::vector<char> > data_;
+ boost::asio::const_buffer buffer_;
+};
+
+// -----------------------------------------------------------------------------
+
+BoostConnection::BoostConnection(boost::asio::io_service* ioService) :
+ socket_(*ioService), readBuffer_(BUFFER_SIZE) {
+}
+
+BoostConnection::~BoostConnection() {
+}
+
+void BoostConnection::listen() {
+ doRead();
+}
+
+void BoostConnection::connect(const HostAddressPort& addressPort) {
+ 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, shared_from_this(), boost::asio::placeholders::error));
+}
+
+void BoostConnection::disconnect() {
+ //MainEventLoop::removeEventsFromOwner(shared_from_this());
+ socket_.close();
+}
+
+void BoostConnection::write(const ByteArray& data) {
+ boost::asio::async_write(socket_, SharedBuffer(data),
+ boost::bind(&BoostConnection::handleDataWritten, shared_from_this(), boost::asio::placeholders::error));
+}
+
+void BoostConnection::handleConnectFinished(const boost::system::error_code& error) {
+ if (!error) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onConnectFinished), false), shared_from_this());
+ doRead();
+ }
+ else if (error != boost::asio::error::operation_aborted) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onConnectFinished), true), shared_from_this());
+ }
+}
+
+void BoostConnection::doRead() {
+ socket_.async_read_some(
+ boost::asio::buffer(readBuffer_),
+ boost::bind(&BoostConnection::handleSocketRead, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
+}
+
+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)), shared_from_this());
+ doRead();
+ }
+ else if (error == boost::asio::error::eof) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), boost::optional<Error>()), shared_from_this());
+ }
+ else if (error != boost::asio::error::operation_aborted) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), ReadError), shared_from_this());
+ }
+}
+
+void BoostConnection::handleDataWritten(const boost::system::error_code& error) {
+ if (!error) {
+ return;
+ }
+ if (error == boost::asio::error::eof) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), boost::optional<Error>()), shared_from_this());
+ }
+ else if (error && error != boost::asio::error::operation_aborted) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onDisconnected), WriteError), shared_from_this());
+ }
+}
+
+}
diff --git a/Swiften/Network/BoostConnection.h b/Swiften/Network/BoostConnection.h
new file mode 100644
index 0000000..ae09fb8
--- /dev/null
+++ b/Swiften/Network/BoostConnection.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <boost/asio.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Network/Connection.h"
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace boost {
+ class thread;
+ namespace system {
+ class error_code;
+ }
+}
+
+namespace Swift {
+ class BoostConnection : public Connection, public EventOwner, public boost::enable_shared_from_this<BoostConnection> {
+ public:
+ BoostConnection(boost::asio::io_service* ioService);
+ ~BoostConnection();
+
+ virtual void listen();
+ virtual void connect(const HostAddressPort& address);
+ virtual void disconnect();
+ virtual void write(const ByteArray& data);
+
+ boost::asio::ip::tcp::socket& getSocket() {
+ return socket_;
+ }
+
+ private:
+ void handleConnectFinished(const boost::system::error_code& error);
+ void handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred);
+ void handleDataWritten(const boost::system::error_code& error);
+ void doRead();
+
+ private:
+ boost::asio::ip::tcp::socket socket_;
+ std::vector<char> readBuffer_;
+ bool disconnecting_;
+ };
+}
diff --git a/Swiften/Network/BoostConnectionFactory.cpp b/Swiften/Network/BoostConnectionFactory.cpp
new file mode 100644
index 0000000..3f62730
--- /dev/null
+++ b/Swiften/Network/BoostConnectionFactory.cpp
@@ -0,0 +1,13 @@
+#include "Swiften/Network/BoostConnectionFactory.h"
+#include "Swiften/Network/BoostConnection.h"
+
+namespace Swift {
+
+BoostConnectionFactory::BoostConnectionFactory(boost::asio::io_service* ioService) : ioService(ioService) {
+}
+
+boost::shared_ptr<Connection> BoostConnectionFactory::createConnection() {
+ return boost::shared_ptr<Connection>(new BoostConnection(ioService));
+}
+
+}
diff --git a/Swiften/Network/BoostConnectionFactory.h b/Swiften/Network/BoostConnectionFactory.h
new file mode 100644
index 0000000..5695c6c
--- /dev/null
+++ b/Swiften/Network/BoostConnectionFactory.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <boost/asio.hpp>
+
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/Network/BoostConnection.h"
+
+namespace Swift {
+ class BoostConnection;
+
+ class BoostConnectionFactory : public ConnectionFactory {
+ public:
+ BoostConnectionFactory(boost::asio::io_service*);
+
+ virtual boost::shared_ptr<Connection> createConnection();
+
+ private:
+ boost::asio::io_service* ioService;
+ };
+}
diff --git a/Swiften/Network/BoostConnectionServer.cpp b/Swiften/Network/BoostConnectionServer.cpp
new file mode 100644
index 0000000..cea016d
--- /dev/null
+++ b/Swiften/Network/BoostConnectionServer.cpp
@@ -0,0 +1,68 @@
+#include "Swiften/Network/BoostConnectionServer.h"
+
+#include <boost/bind.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+BoostConnectionServer::BoostConnectionServer(int port, boost::asio::io_service* ioService) : port_(port), ioService_(ioService), acceptor_(NULL) {
+}
+
+
+void BoostConnectionServer::start() {
+ try {
+ assert(!acceptor_);
+ acceptor_ = new boost::asio::ip::tcp::acceptor(
+ *ioService_,
+ boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port_));
+ acceptNextConnection();
+ }
+ catch (const boost::system::system_error& e) {
+ if (e.code() == boost::asio::error::address_in_use) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), Conflict), shared_from_this());
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), UnknownError), shared_from_this());
+ }
+ }
+}
+
+
+void BoostConnectionServer::stop() {
+ stop(boost::optional<Error>());
+}
+
+void BoostConnectionServer::stop(boost::optional<Error> e) {
+ if (acceptor_) {
+ acceptor_->close();
+ delete acceptor_;
+ acceptor_ = NULL;
+ }
+ MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), e), shared_from_this());
+}
+
+void BoostConnectionServer::acceptNextConnection() {
+ boost::shared_ptr<BoostConnection> newConnection(new BoostConnection(&acceptor_->io_service()));
+ acceptor_->async_accept(newConnection->getSocket(),
+ boost::bind(&BoostConnectionServer::handleAccept, shared_from_this(), newConnection, boost::asio::placeholders::error));
+}
+
+void BoostConnectionServer::handleAccept(boost::shared_ptr<BoostConnection> newConnection, const boost::system::error_code& error) {
+ if (error) {
+ MainEventLoop::postEvent(
+ boost::bind(
+ &BoostConnectionServer::stop, shared_from_this(), UnknownError),
+ shared_from_this());
+ }
+ else {
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onNewConnection), newConnection),
+ shared_from_this());
+ newConnection->listen();
+ acceptNextConnection();
+ }
+}
+
+}
diff --git a/Swiften/Network/BoostConnectionServer.h b/Swiften/Network/BoostConnectionServer.h
new file mode 100644
index 0000000..d8e5eb4
--- /dev/null
+++ b/Swiften/Network/BoostConnectionServer.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/asio.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Network/BoostConnection.h"
+#include "Swiften/Network/ConnectionServer.h"
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+ class BoostConnectionServer : public ConnectionServer, public EventOwner, public boost::enable_shared_from_this<BoostConnectionServer> {
+ public:
+ enum Error {
+ Conflict,
+ UnknownError
+ };
+ BoostConnectionServer(int port, boost::asio::io_service* ioService);
+
+ void start();
+ void stop();
+
+ boost::signal<void (boost::optional<Error>)> onStopped;
+
+ private:
+ void stop(boost::optional<Error> e);
+ void acceptNextConnection();
+ void handleAccept(boost::shared_ptr<BoostConnection> newConnection, const boost::system::error_code& error);
+
+ private:
+ int port_;
+ boost::asio::io_service* ioService_;
+ boost::asio::ip::tcp::acceptor* acceptor_;
+ };
+}
diff --git a/Swiften/Network/BoostIOServiceThread.cpp b/Swiften/Network/BoostIOServiceThread.cpp
new file mode 100644
index 0000000..01c3bf3
--- /dev/null
+++ b/Swiften/Network/BoostIOServiceThread.cpp
@@ -0,0 +1,18 @@
+#include "Swiften/Network/BoostIOServiceThread.h"
+
+namespace Swift {
+
+BoostIOServiceThread::BoostIOServiceThread() : thread_(boost::bind(&BoostIOServiceThread::doRun, this)) {
+}
+
+BoostIOServiceThread::~BoostIOServiceThread() {
+ ioService_.stop();
+ thread_.join();
+}
+
+void BoostIOServiceThread::doRun() {
+ boost::asio::io_service::work work(ioService_);
+ ioService_.run();
+}
+
+}
diff --git a/Swiften/Network/BoostIOServiceThread.h b/Swiften/Network/BoostIOServiceThread.h
new file mode 100644
index 0000000..ddc90bf
--- /dev/null
+++ b/Swiften/Network/BoostIOServiceThread.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <boost/asio.hpp>
+#include <boost/thread.hpp>
+
+namespace Swift {
+ class BoostIOServiceThread {
+ public:
+ BoostIOServiceThread();
+ ~BoostIOServiceThread();
+
+ boost::asio::io_service& getIOService() {
+ return ioService_;
+ }
+
+ private:
+ void doRun();
+
+ private:
+ boost::asio::io_service ioService_;
+ boost::thread thread_;
+ };
+}
diff --git a/Swiften/Network/BoostTimer.cpp b/Swiften/Network/BoostTimer.cpp
new file mode 100644
index 0000000..fdbd45d
--- /dev/null
+++ b/Swiften/Network/BoostTimer.cpp
@@ -0,0 +1,34 @@
+#include "Swiften/Network/BoostTimer.h"
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/asio.hpp>
+
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+
+BoostTimer::BoostTimer(int milliseconds, boost::asio::io_service* service) :
+ timeout(milliseconds), timer(*service) {
+}
+
+void BoostTimer::start() {
+ timer.expires_from_now(boost::posix_time::milliseconds(timeout));
+ timer.async_wait(boost::bind(&BoostTimer::handleTimerTick, shared_from_this(), boost::asio::placeholders::error));
+}
+
+void BoostTimer::stop() {
+ timer.cancel();
+}
+
+void BoostTimer::handleTimerTick(const boost::system::error_code& error) {
+ if (error) {
+ assert(error == boost::asio::error::operation_aborted);
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onTick)), shared_from_this());
+ timer.expires_from_now(boost::posix_time::milliseconds(timeout));
+ timer.async_wait(boost::bind(&BoostTimer::handleTimerTick, shared_from_this(), boost::asio::placeholders::error));
+ }
+}
+
+}
diff --git a/Swiften/Network/BoostTimer.h b/Swiften/Network/BoostTimer.h
new file mode 100644
index 0000000..9b27cf9
--- /dev/null
+++ b/Swiften/Network/BoostTimer.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <boost/asio.hpp>
+#include <boost/thread.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/EventLoop/EventOwner.h"
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+ class BoostTimer : public Timer, public EventOwner, public boost::enable_shared_from_this<BoostTimer> {
+ public:
+ BoostTimer(int milliseconds, boost::asio::io_service* service);
+
+ virtual void start();
+ virtual void stop();
+
+ private:
+ void handleTimerTick(const boost::system::error_code& error);
+
+ private:
+ int timeout;
+ boost::asio::deadline_timer timer;
+ };
+}
diff --git a/Swiften/Network/BoostTimerFactory.cpp b/Swiften/Network/BoostTimerFactory.cpp
new file mode 100644
index 0000000..bbcd83f
--- /dev/null
+++ b/Swiften/Network/BoostTimerFactory.cpp
@@ -0,0 +1,13 @@
+#include "Swiften/Network/BoostTimerFactory.h"
+#include "Swiften/Network/BoostTimer.h"
+
+namespace Swift {
+
+BoostTimerFactory::BoostTimerFactory(boost::asio::io_service* ioService) : ioService(ioService) {
+}
+
+boost::shared_ptr<Timer> BoostTimerFactory::createTimer(int milliseconds) {
+ return boost::shared_ptr<Timer>(new BoostTimer(milliseconds, ioService));
+}
+
+}
diff --git a/Swiften/Network/BoostTimerFactory.h b/Swiften/Network/BoostTimerFactory.h
new file mode 100644
index 0000000..e98c9de
--- /dev/null
+++ b/Swiften/Network/BoostTimerFactory.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <boost/asio.hpp>
+
+#include "Swiften/Network/TimerFactory.h"
+#include "Swiften/Network/BoostTimer.h"
+
+namespace Swift {
+ class BoostTimer;
+
+ class BoostTimerFactory : public TimerFactory {
+ public:
+ BoostTimerFactory(boost::asio::io_service*);
+
+ virtual boost::shared_ptr<Timer> createTimer(int milliseconds);
+
+ private:
+ boost::asio::io_service* ioService;
+ };
+}
diff --git a/Swiften/Network/CAresDomainNameResolver.cpp b/Swiften/Network/CAresDomainNameResolver.cpp
new file mode 100644
index 0000000..c0bf8a0
--- /dev/null
+++ b/Swiften/Network/CAresDomainNameResolver.cpp
@@ -0,0 +1,162 @@
+// TODO: Check the second param of postEvent. We sometimes omit it. Same
+// goes for the PlatformDomainNameResolver.
+
+#include "Swiften/Network/CAresDomainNameResolver.h"
+#include "Swiften/Base/Platform.h"
+
+#ifndef SWIFTEN_PLATFORM_WINDOWS
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+#include <algorithm>
+
+#include "Swiften/Network/DomainNameServiceQuery.h"
+#include "Swiften/Network/DomainNameAddressQuery.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+class CAresQuery : public boost::enable_shared_from_this<CAresQuery>, public EventOwner {
+ public:
+ CAresQuery(const String& query, int dnsclass, int type, CAresDomainNameResolver* resolver) : query(query), dnsclass(dnsclass), type(type), resolver(resolver) {
+ }
+
+ virtual ~CAresQuery() {
+ }
+
+ void addToQueue() {
+ resolver->addToQueue(shared_from_this());
+ }
+
+ void doRun(ares_channel* channel) {
+ ares_query(*channel, query.getUTF8Data(), dnsclass, type, &CAresQuery::handleResult, this);
+ }
+
+ static void handleResult(void* arg, int status, int timeouts, unsigned char* buffer, int len) {
+ reinterpret_cast<CAresQuery*>(arg)->handleResult(status, timeouts, buffer, len);
+ }
+
+ virtual void handleResult(int status, int, unsigned char* buffer, int len) = 0;
+
+ private:
+ String query;
+ int dnsclass;
+ int type;
+ CAresDomainNameResolver* resolver;
+};
+
+class CAresDomainNameServiceQuery : public DomainNameServiceQuery, public CAresQuery {
+ public:
+ CAresDomainNameServiceQuery(const String& service, CAresDomainNameResolver* resolver) : CAresQuery(service, 1, 33, resolver) {
+ }
+
+ virtual void run() {
+ addToQueue();
+ }
+
+ void handleResult(int status, int, unsigned char* buffer, int len) {
+ if (status == ARES_SUCCESS) {
+ std::vector<DomainNameServiceQuery::Result> records;
+ ares_srv_reply* rawRecords;
+ if (ares_parse_srv_reply(buffer, len, &rawRecords) == ARES_SUCCESS) {
+ for( ; rawRecords != NULL; rawRecords = rawRecords->next) {
+ DomainNameServiceQuery::Result record;
+ record.priority = rawRecords->priority;
+ record.weight = rawRecords->weight;
+ record.port = rawRecords->port;
+ record.hostname = String(rawRecords->host);
+ records.push_back(record);
+ }
+ }
+ std::sort(records.begin(), records.end(), ResultPriorityComparator());
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), records));
+ }
+ else if (status != ARES_EDESTRUCTION) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), std::vector<DomainNameServiceQuery::Result>()), shared_from_this());
+ }
+ }
+};
+
+class CAresDomainNameAddressQuery : public DomainNameAddressQuery, public CAresQuery {
+ public:
+ CAresDomainNameAddressQuery(const String& host, CAresDomainNameResolver* resolver) : CAresQuery(host, 1, 1, resolver) {
+ }
+
+ virtual void run() {
+ addToQueue();
+ }
+
+ void handleResult(int status, int, unsigned char* buffer, int len) {
+ if (status == ARES_SUCCESS) {
+ struct hostent* hosts;
+ if (ares_parse_a_reply(buffer, len, &hosts, NULL, NULL) == ARES_SUCCESS) {
+ // Check whether the different fields are what we expect them to be
+ struct in_addr addr;
+ addr.s_addr = *(unsigned int*)hosts->h_addr_list[0];
+ HostAddress result(inet_ntoa(addr));
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), result, boost::optional<DomainNameResolveError>()), boost::dynamic_pointer_cast<CAresDomainNameAddressQuery>(shared_from_this()));
+ ares_free_hostent(hosts);
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), shared_from_this());
+ }
+ }
+ else if (status != ARES_EDESTRUCTION) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), shared_from_this());
+ }
+ }
+};
+
+CAresDomainNameResolver::CAresDomainNameResolver() : stopRequested(false) {
+ ares_init(&channel);
+ thread = new boost::thread(boost::bind(&CAresDomainNameResolver::run, this));
+}
+
+CAresDomainNameResolver::~CAresDomainNameResolver() {
+ stopRequested = true;
+ thread->join();
+ ares_destroy(channel);
+}
+
+boost::shared_ptr<DomainNameServiceQuery> CAresDomainNameResolver::createServiceQuery(const String& name) {
+ return boost::shared_ptr<DomainNameServiceQuery>(new CAresDomainNameServiceQuery(getNormalized(name), this));
+}
+
+boost::shared_ptr<DomainNameAddressQuery> CAresDomainNameResolver::createAddressQuery(const String& name) {
+ return boost::shared_ptr<DomainNameAddressQuery>(new CAresDomainNameAddressQuery(getNormalized(name), this));
+}
+
+void CAresDomainNameResolver::addToQueue(boost::shared_ptr<CAresQuery> query) {
+ boost::lock_guard<boost::mutex> lock(pendingQueriesMutex);
+ pendingQueries.push_back(query);
+}
+
+void CAresDomainNameResolver::run() {
+ fd_set readers, writers;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100000;
+ while(!stopRequested) {
+ {
+ boost::unique_lock<boost::mutex> lock(pendingQueriesMutex);
+ foreach(const boost::shared_ptr<CAresQuery>& query, pendingQueries) {
+ query->doRun(&channel);
+ }
+ pendingQueries.clear();
+ }
+ FD_ZERO(&readers);
+ FD_ZERO(&writers);
+ int nfds = ares_fds(channel, &readers, &writers);
+ //if (nfds) {
+ // break;
+ //}
+ struct timeval tv;
+ struct timeval* tvp = ares_timeout(channel, &timeout, &tv);
+ select(nfds, &readers, &writers, NULL, tvp);
+ ares_process(channel, &readers, &writers);
+ }
+}
+
+}
diff --git a/Swiften/Network/CAresDomainNameResolver.h b/Swiften/Network/CAresDomainNameResolver.h
new file mode 100644
index 0000000..0cdd163
--- /dev/null
+++ b/Swiften/Network/CAresDomainNameResolver.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <ares.h>
+#include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <list>
+
+#include "Swiften/Network/DomainNameResolver.h"
+
+namespace Swift {
+ class CAresQuery;
+
+ class CAresDomainNameResolver : public DomainNameResolver {
+ public:
+ CAresDomainNameResolver();
+ ~CAresDomainNameResolver();
+
+ virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name);
+ virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name);
+
+ private:
+ friend class CAresQuery;
+
+ void run();
+ void addToQueue(boost::shared_ptr<CAresQuery>);
+
+ private:
+ bool stopRequested;
+ ares_channel channel;
+ boost::thread* thread;
+ boost::mutex pendingQueriesMutex;
+ std::list< boost::shared_ptr<CAresQuery> > pendingQueries;
+ };
+}
diff --git a/Swiften/Network/Connection.h b/Swiften/Network/Connection.h
new file mode 100644
index 0000000..a995774
--- /dev/null
+++ b/Swiften/Network/Connection.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <boost/signals.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class HostAddressPort;
+
+ class Connection {
+ public:
+ enum Error {
+ ReadError,
+ WriteError
+ };
+
+ Connection() {}
+ virtual ~Connection() {}
+
+ virtual void listen() = 0;
+ virtual void connect(const HostAddressPort& address) = 0;
+ virtual void disconnect() = 0;
+ virtual void write(const ByteArray& data) = 0;
+
+ public:
+ boost::signal<void (bool /* error */)> onConnectFinished;
+ boost::signal<void (const boost::optional<Error>&)> onDisconnected;
+ boost::signal<void (const ByteArray&)> onDataRead;
+ };
+}
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..e78f6ab
--- /dev/null
+++ b/Swiften/Network/ConnectionFactory.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class Connection;
+
+ class ConnectionFactory {
+ public:
+ virtual ~ConnectionFactory();
+
+ virtual boost::shared_ptr<Connection> createConnection() = 0;
+ };
+}
diff --git a/Swiften/Network/ConnectionServer.cpp b/Swiften/Network/ConnectionServer.cpp
new file mode 100644
index 0000000..7f63fee
--- /dev/null
+++ b/Swiften/Network/ConnectionServer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Network/ConnectionServer.h"
+
+namespace Swift {
+
+ConnectionServer::~ConnectionServer() {
+}
+
+}
diff --git a/Swiften/Network/ConnectionServer.h b/Swiften/Network/ConnectionServer.h
new file mode 100644
index 0000000..539367d
--- /dev/null
+++ b/Swiften/Network/ConnectionServer.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Network/Connection.h"
+
+namespace Swift {
+ class ConnectionServer {
+ public:
+ virtual ~ConnectionServer();
+
+ boost::signal<void (boost::shared_ptr<Connection>)> onNewConnection;
+ };
+}
diff --git a/Swiften/Network/Connector.cpp b/Swiften/Network/Connector.cpp
new file mode 100644
index 0000000..d372bf2
--- /dev/null
+++ b/Swiften/Network/Connector.cpp
@@ -0,0 +1,126 @@
+#include "Swiften/Network/Connector.h"
+
+#include <boost/bind.hpp>
+#include <iostream>
+
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/Network/DomainNameResolver.h"
+#include "Swiften/Network/DomainNameAddressQuery.h"
+#include "Swiften/Network/TimerFactory.h"
+
+namespace Swift {
+
+Connector::Connector(const String& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0), queriedAllHosts(true) {
+}
+
+void Connector::setTimeoutMilliseconds(int milliseconds) {
+ timeoutMilliseconds = milliseconds;
+}
+
+void Connector::start() {
+ //std::cout << "Connector::start()" << std::endl;
+ assert(!currentConnection);
+ assert(!serviceQuery);
+ assert(!timer);
+ queriedAllHosts = false;
+ serviceQuery = resolver->createServiceQuery("_xmpp-client._tcp." + hostname);
+ serviceQuery->onResult.connect(boost::bind(&Connector::handleServiceQueryResult, this, _1));
+ if (timeoutMilliseconds > 0) {
+ timer = timerFactory->createTimer(timeoutMilliseconds);
+ timer->onTick.connect(boost::bind(&Connector::handleTimeout, this));
+ timer->start();
+ }
+ serviceQuery->run();
+}
+
+void Connector::queryAddress(const String& hostname) {
+ assert(!addressQuery);
+ addressQuery = resolver->createAddressQuery(hostname);
+ addressQuery->onResult.connect(boost::bind(&Connector::handleAddressQueryResult, this, _1, _2));
+ addressQuery->run();
+}
+
+void Connector::handleServiceQueryResult(const std::vector<DomainNameServiceQuery::Result>& result) {
+ //std::cout << "Received SRV results" << std::endl;
+ serviceQueryResults = std::deque<DomainNameServiceQuery::Result>(result.begin(), result.end());
+ serviceQuery.reset();
+ tryNextHostname();
+}
+
+void Connector::tryNextHostname() {
+ if (queriedAllHosts) {
+ //std::cout << "Connector::tryNextHostName(): Queried all hosts. Error." << std::endl;
+ finish(boost::shared_ptr<Connection>());
+ }
+ else if (serviceQueryResults.empty()) {
+ //std::cout << "Connector::tryNextHostName(): Falling back on A resolution" << std::endl;
+ // Fall back on simple address resolving
+ queriedAllHosts = true;
+ queryAddress(hostname);
+ }
+ else {
+ //std::cout << "Connector::tryNextHostName(): Querying next address" << std::endl;
+ queryAddress(serviceQueryResults.front().hostname);
+ }
+}
+
+void Connector::handleAddressQueryResult(const HostAddress& address, boost::optional<DomainNameResolveError> error) {
+ //std::cout << "Connector::handleAddressQueryResult(): Start" << std::endl;
+ addressQuery.reset();
+ if (!serviceQueryResults.empty()) {
+ DomainNameServiceQuery::Result serviceQueryResult = serviceQueryResults.front();
+ serviceQueryResults.pop_front();
+ if (error) {
+ //std::cout << "Connector::handleAddressQueryResult(): A lookup for SRV host " << serviceQueryResult.hostname << " failed." << std::endl;
+ tryNextHostname();
+ }
+ else {
+ //std::cout << "Connector::handleAddressQueryResult(): A lookup for SRV host " << serviceQueryResult.hostname << " succeeded: " << address.toString() << std::endl;
+ tryConnect(HostAddressPort(address, serviceQueryResult.port));
+ }
+ }
+ else if (error) {
+ //std::cout << "Connector::handleAddressQueryResult(): Fallback address query failed. Giving up" << std::endl;
+ // The fallback address query failed
+ assert(queriedAllHosts);
+ finish(boost::shared_ptr<Connection>());
+ }
+ else {
+ //std::cout << "Connector::handleAddressQueryResult(): Fallback address query succeeded: " << address.toString() << std::endl;
+ // The fallback query succeeded
+ tryConnect(HostAddressPort(address, 5222));
+ }
+}
+
+void Connector::tryConnect(const HostAddressPort& target) {
+ assert(!currentConnection);
+ //std::cout << "Connector::tryConnect() " << target.getAddress().toString() << " " << target.getPort() << std::endl;
+ currentConnection = connectionFactory->createConnection();
+ currentConnection->onConnectFinished.connect(boost::bind(&Connector::handleConnectionConnectFinished, this, _1));
+ currentConnection->connect(target);
+}
+
+void Connector::handleConnectionConnectFinished(bool error) {
+ //std::cout << "Connector::handleConnectionConnectFinished() " << error << std::endl;
+ if (error) {
+ currentConnection.reset();
+ tryNextHostname();
+ }
+ else {
+ finish(currentConnection);
+ }
+}
+
+void Connector::finish(boost::shared_ptr<Connection> connection) {
+ if (timer) {
+ timer->stop();
+ timer.reset();
+ }
+ onConnectFinished(connection);
+}
+
+void Connector::handleTimeout() {
+ finish(boost::shared_ptr<Connection>());
+}
+
+};
diff --git a/Swiften/Network/Connector.h b/Swiften/Network/Connector.h
new file mode 100644
index 0000000..507f085
--- /dev/null
+++ b/Swiften/Network/Connector.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <deque>
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Network/DomainNameServiceQuery.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Network/Timer.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/DomainNameResolveError.h"
+
+namespace Swift {
+ class DomainNameAddressQuery;
+ class DomainNameResolver;
+ class ConnectionFactory;
+ class TimerFactory;
+
+ class Connector : public boost::bsignals::trackable {
+ public:
+ Connector(const String& hostname, DomainNameResolver*, ConnectionFactory*, TimerFactory*);
+
+ void setTimeoutMilliseconds(int milliseconds);
+ void start();
+
+ boost::signal<void (boost::shared_ptr<Connection>)> onConnectFinished;
+
+ private:
+ void handleServiceQueryResult(const std::vector<DomainNameServiceQuery::Result>& result);
+ void handleAddressQueryResult(const HostAddress& address, boost::optional<DomainNameResolveError> error);
+ void queryAddress(const String& hostname);
+
+ void tryNextHostname();
+ void tryConnect(const HostAddressPort& target);
+
+ void handleConnectionConnectFinished(bool error);
+ void finish(boost::shared_ptr<Connection>);
+ void handleTimeout();
+
+ private:
+ String hostname;
+ DomainNameResolver* resolver;
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+ int timeoutMilliseconds;
+ boost::shared_ptr<Timer> timer;
+ boost::shared_ptr<DomainNameServiceQuery> serviceQuery;
+ std::deque<DomainNameServiceQuery::Result> serviceQueryResults;
+ boost::shared_ptr<DomainNameAddressQuery> addressQuery;
+ bool queriedAllHosts;
+ boost::shared_ptr<Connection> currentConnection;
+ };
+};
diff --git a/Swiften/Network/DomainNameAddressQuery.cpp b/Swiften/Network/DomainNameAddressQuery.cpp
new file mode 100644
index 0000000..5e77cd7
--- /dev/null
+++ b/Swiften/Network/DomainNameAddressQuery.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Network/DomainNameAddressQuery.h"
+
+namespace Swift {
+
+DomainNameAddressQuery::~DomainNameAddressQuery() {
+}
+
+}
diff --git a/Swiften/Network/DomainNameAddressQuery.h b/Swiften/Network/DomainNameAddressQuery.h
new file mode 100644
index 0000000..66a79db
--- /dev/null
+++ b/Swiften/Network/DomainNameAddressQuery.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Network/DomainNameResolveError.h"
+#include "Swiften/Network/HostAddress.h"
+
+namespace Swift {
+ class DomainNameAddressQuery {
+ public:
+ virtual ~DomainNameAddressQuery();
+
+ virtual void run() = 0;
+
+ boost::signal<void (const HostAddress&, boost::optional<DomainNameResolveError>)> onResult;
+ };
+}
diff --git a/Swiften/Network/DomainNameResolveError.h b/Swiften/Network/DomainNameResolveError.h
new file mode 100644
index 0000000..860ea23
--- /dev/null
+++ b/Swiften/Network/DomainNameResolveError.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "Swiften/Base/Error.h"
+
+namespace Swift {
+ class DomainNameResolveError : public Error {
+ public:
+ DomainNameResolveError() {}
+ };
+}
diff --git a/Swiften/Network/DomainNameResolver.cpp b/Swiften/Network/DomainNameResolver.cpp
new file mode 100644
index 0000000..63ed881
--- /dev/null
+++ b/Swiften/Network/DomainNameResolver.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/Network/DomainNameResolver.h"
+
+#include <idna.h>
+
+namespace Swift {
+
+DomainNameResolver::~DomainNameResolver() {
+}
+
+String DomainNameResolver::getNormalized(const String& domain) {
+ char* output;
+ if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) {
+ String result(output);
+ free(output);
+ return result;
+ }
+ else {
+ return domain;
+ }
+}
+
+}
diff --git a/Swiften/Network/DomainNameResolver.h b/Swiften/Network/DomainNameResolver.h
new file mode 100644
index 0000000..d3dab26
--- /dev/null
+++ b/Swiften/Network/DomainNameResolver.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class DomainNameServiceQuery;
+ class DomainNameAddressQuery;
+ class String;
+
+ class DomainNameResolver {
+ public:
+ virtual ~DomainNameResolver();
+
+ virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name) = 0;
+ virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name) = 0;
+
+ protected:
+ static String getNormalized(const String& domain);
+ };
+}
diff --git a/Swiften/Network/DomainNameServiceQuery.cpp b/Swiften/Network/DomainNameServiceQuery.cpp
new file mode 100644
index 0000000..7dfd353
--- /dev/null
+++ b/Swiften/Network/DomainNameServiceQuery.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Network/DomainNameServiceQuery.h"
+
+namespace Swift {
+
+DomainNameServiceQuery::~DomainNameServiceQuery() {
+}
+
+}
diff --git a/Swiften/Network/DomainNameServiceQuery.h b/Swiften/Network/DomainNameServiceQuery.h
new file mode 100644
index 0000000..57e48d3
--- /dev/null
+++ b/Swiften/Network/DomainNameServiceQuery.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/optional.hpp>
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/DomainNameResolveError.h"
+
+namespace Swift {
+ class DomainNameServiceQuery {
+ public:
+ struct Result {
+ Result(const String& hostname = "", int port = -1, int priority = -1, int weight = -1) : hostname(hostname), port(port), priority(priority), weight(weight) {}
+ String hostname;
+ int port;
+ int priority;
+ int weight;
+ };
+
+ struct ResultPriorityComparator {
+ bool operator()(const DomainNameServiceQuery::Result& a, const DomainNameServiceQuery::Result& b) const {
+ return a.priority < b.priority;
+ }
+ };
+
+ virtual ~DomainNameServiceQuery();
+
+ virtual void run() = 0;
+
+ boost::signal<void (const std::vector<Result>&)> onResult;
+ };
+}
diff --git a/Swiften/Network/DummyConnection.h b/Swiften/Network/DummyConnection.h
new file mode 100644
index 0000000..11281b3
--- /dev/null
+++ b/Swiften/Network/DummyConnection.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <cassert>
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Network/Connection.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/EventLoop/EventOwner.h"
+
+namespace Swift {
+ class DummyConnection :
+ public Connection,
+ public EventOwner,
+ public boost::enable_shared_from_this<DummyConnection> {
+
+ void listen() {
+ assert(false);
+ }
+
+ void connect(const HostAddressPort&) {
+ assert(false);
+ }
+
+ void disconnect() {
+ assert(false);
+ }
+
+ void write(const ByteArray& data) {
+ onDataWritten(data);
+ }
+
+ void receive(const ByteArray& data) {
+ MainEventLoop::postEvent(boost::bind(
+ boost::ref(onDataRead), ByteArray(data)), shared_from_this());
+ }
+
+ boost::signal<void (const ByteArray&)> onDataWritten;
+ };
+}
diff --git a/Swiften/Network/DummyTimerFactory.cpp b/Swiften/Network/DummyTimerFactory.cpp
new file mode 100644
index 0000000..7626584
--- /dev/null
+++ b/Swiften/Network/DummyTimerFactory.cpp
@@ -0,0 +1,60 @@
+#include "Swiften/Network/DummyTimerFactory.h"
+
+#include <algorithm>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+
+class DummyTimerFactory::DummyTimer : public Timer {
+ public:
+ DummyTimer(int timeout) : timeout(timeout), isRunning(false) {
+ }
+
+ virtual void start() {
+ isRunning = true;
+ }
+
+ virtual void stop() {
+ isRunning = false;
+ }
+
+ int timeout;
+ bool isRunning;
+};
+
+
+DummyTimerFactory::DummyTimerFactory() : currentTime(0) {
+}
+
+boost::shared_ptr<Timer> DummyTimerFactory::createTimer(int milliseconds) {
+ boost::shared_ptr<DummyTimer> timer(new DummyTimer(milliseconds));
+ timers.push_back(timer);
+ return timer;
+}
+
+static bool hasZeroTimeout(boost::shared_ptr<DummyTimerFactory::DummyTimer> timer) {
+ return timer->timeout == 0;
+}
+
+void DummyTimerFactory::setTime(int time) {
+ assert(time > currentTime);
+ int increment = time - currentTime;
+ std::vector< boost::shared_ptr<DummyTimer> > notifyTimers(timers.begin(), timers.end());
+ foreach(boost::shared_ptr<DummyTimer> timer, notifyTimers) {
+ if (increment >= timer->timeout) {
+ if (timer->isRunning) {
+ timer->onTick();
+ }
+ timer->timeout = 0;
+ }
+ else {
+ timer->timeout -= increment;
+ }
+ }
+ timers.erase(std::remove_if(timers.begin(), timers.end(), hasZeroTimeout), timers.end());
+ currentTime = time;
+}
+
+}
diff --git a/Swiften/Network/DummyTimerFactory.h b/Swiften/Network/DummyTimerFactory.h
new file mode 100644
index 0000000..feac029
--- /dev/null
+++ b/Swiften/Network/DummyTimerFactory.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <list>
+
+#include "Swiften/Network/TimerFactory.h"
+
+namespace Swift {
+ class DummyTimerFactory : public TimerFactory {
+ public:
+ class DummyTimer;
+
+ DummyTimerFactory();
+
+ virtual boost::shared_ptr<Timer> createTimer(int milliseconds);
+ void setTime(int time);
+
+ private:
+ friend class DummyTimer;
+ int currentTime;
+ std::list<boost::shared_ptr<DummyTimer> > timers;
+ };
+}
diff --git a/Swiften/Network/FakeConnection.h b/Swiften/Network/FakeConnection.h
new file mode 100644
index 0000000..92a03c3
--- /dev/null
+++ b/Swiften/Network/FakeConnection.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <boost/optional.hpp>
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <vector>
+
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/EventLoop/EventOwner.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class FakeConnection :
+ public Connection,
+ public EventOwner,
+ public boost::enable_shared_from_this<FakeConnection> {
+ public:
+ enum State {
+ Initial,
+ Connecting,
+ Connected,
+ Disconnected,
+ DisconnectedWithError
+ };
+
+ FakeConnection() : state(Initial), delayConnect(false) {}
+
+ virtual void listen() {
+ assert(false);
+ }
+
+ void setError(const Error& e) {
+ error = boost::optional<Error>(e);
+ state = DisconnectedWithError;
+ if (connectedTo) {
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onDisconnected), error),
+ shared_from_this());
+ }
+ }
+
+ virtual void connect(const HostAddressPort& address) {
+ if (delayConnect) {
+ state = Connecting;
+ }
+ else {
+ if (!error) {
+ connectedTo = address;
+ state = Connected;
+ }
+ else {
+ state = DisconnectedWithError;
+ }
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onConnectFinished), error),
+ shared_from_this());
+ }
+ }
+
+ virtual void disconnect() {
+ if (!error) {
+ state = Disconnected;
+ }
+ else {
+ state = DisconnectedWithError;
+ }
+ connectedTo.reset();
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onDisconnected), error),
+ shared_from_this());
+ }
+
+ virtual void write(const ByteArray& data) {
+ dataWritten.push_back(data);
+ }
+
+ void setDelayConnect() {
+ delayConnect = true;
+ }
+
+ boost::optional<HostAddressPort> connectedTo;
+ std::vector<ByteArray> dataWritten;
+ boost::optional<Error> error;
+ State state;
+ bool delayConnect;
+ };
+}
diff --git a/Swiften/Network/HostAddress.cpp b/Swiften/Network/HostAddress.cpp
new file mode 100644
index 0000000..8ac66bb
--- /dev/null
+++ b/Swiften/Network/HostAddress.cpp
@@ -0,0 +1,61 @@
+#include "Swiften/Network/HostAddress.h"
+
+#include <boost/numeric/conversion/cast.hpp>
+#include <boost/lexical_cast.hpp>
+#include <cassert>
+#include <sstream>
+#include <iomanip>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+
+HostAddress::HostAddress() {
+ for (int i = 0; i < 4; ++i) {
+ address_.push_back(0);
+ }
+}
+
+HostAddress::HostAddress(const String& address) {
+ std::vector<String> components = address.split('.');
+ assert(components.size() == 4);
+ foreach(const String& component, components) {
+ address_.push_back(boost::lexical_cast<int>(component.getUTF8String()));
+ }
+}
+
+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..11f8a2b
--- /dev/null
+++ b/Swiften/Network/HostAddress.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace Swift {
+ class String;
+
+ class HostAddress {
+ public:
+ HostAddress();
+ HostAddress(const String&);
+ HostAddress(const unsigned char* address, int length);
+
+ const std::vector<unsigned char>& getRawAddress() const {
+ return address_;
+ }
+
+ std::string toString() const;
+
+ bool operator==(const HostAddress& o) const {
+ return address_ == o.address_;
+ }
+
+ private:
+ std::vector<unsigned char> address_;
+ };
+}
diff --git a/Swiften/Network/HostAddressPort.h b/Swiften/Network/HostAddressPort.h
new file mode 100644
index 0000000..d632058
--- /dev/null
+++ b/Swiften/Network/HostAddressPort.h
@@ -0,0 +1,30 @@
+#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_;
+ }
+
+ bool operator==(const HostAddressPort& o) const {
+ return address_ == o.address_ && port_ == o.port_;
+ }
+
+ private:
+ HostAddress address_;
+ int port_;
+ };
+}
+
+#endif
diff --git a/Swiften/Network/MainBoostIOServiceThread.cpp b/Swiften/Network/MainBoostIOServiceThread.cpp
new file mode 100644
index 0000000..672bb07
--- /dev/null
+++ b/Swiften/Network/MainBoostIOServiceThread.cpp
@@ -0,0 +1,12 @@
+#include "Swiften/Network/MainBoostIOServiceThread.h"
+
+#include "Swiften/Network/BoostIOServiceThread.h"
+
+namespace Swift {
+
+BoostIOServiceThread& MainBoostIOServiceThread::getInstance() {
+ static BoostIOServiceThread instance;
+ return instance;
+}
+
+}
diff --git a/Swiften/Network/MainBoostIOServiceThread.h b/Swiften/Network/MainBoostIOServiceThread.h
new file mode 100644
index 0000000..cca7c2e
--- /dev/null
+++ b/Swiften/Network/MainBoostIOServiceThread.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace Swift {
+ class BoostIOServiceThread;
+
+ class MainBoostIOServiceThread {
+ public:
+ static BoostIOServiceThread& getInstance();
+ };
+}
diff --git a/Swiften/Network/PlatformDomainNameResolver.cpp b/Swiften/Network/PlatformDomainNameResolver.cpp
new file mode 100644
index 0000000..7b8a6d5
--- /dev/null
+++ b/Swiften/Network/PlatformDomainNameResolver.cpp
@@ -0,0 +1,94 @@
+#include "Swiften/Network/PlatformDomainNameResolver.h"
+
+// Putting this early on, because some system types conflict with thread
+#include "Swiften/Network/PlatformDomainNameServiceQuery.h"
+
+#include <string>
+#include <vector>
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <algorithm>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/HostAddress.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/Network/DomainNameAddressQuery.h"
+
+using namespace Swift;
+
+namespace {
+ struct AddressQuery : public DomainNameAddressQuery, public boost::enable_shared_from_this<AddressQuery>, public EventOwner {
+ AddressQuery(const String& host) : hostname(host), thread(NULL), safeToJoin(false) {}
+
+ ~AddressQuery() {
+ if (safeToJoin) {
+ thread->join();
+ }
+ else {
+ // FIXME: UGLYYYYY
+ }
+ delete thread;
+ }
+
+ void run() {
+ safeToJoin = false;
+ thread = new boost::thread(boost::bind(&AddressQuery::doRun, shared_from_this()));
+ }
+
+ void doRun() {
+ //std::cout << "PlatformDomainNameResolver::doRun()" << std::endl;
+ boost::asio::ip::tcp::resolver resolver(ioService);
+ boost::asio::ip::tcp::resolver::query query(hostname.getUTF8String(), "5222");
+ try {
+ //std::cout << "PlatformDomainNameResolver::doRun(): Resolving" << std::endl;
+ boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(query);
+ //std::cout << "PlatformDomainNameResolver::doRun(): Resolved" << std::endl;
+ if (endpointIterator == boost::asio::ip::tcp::resolver::iterator()) {
+ //std::cout << "PlatformDomainNameResolver::doRun(): Error 1" << std::endl;
+ emitError();
+ }
+ else {
+ boost::asio::ip::address address = (*endpointIterator).endpoint().address();
+ HostAddress result = (address.is_v4() ? HostAddress(&address.to_v4().to_bytes()[0], 4) : HostAddress(&address.to_v6().to_bytes()[0], 16));
+ //std::cout << "PlatformDomainNameResolver::doRun(): Success" << std::endl;
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onResult), result, boost::optional<DomainNameResolveError>()),
+ shared_from_this());
+ }
+ }
+ catch (...) {
+ //std::cout << "PlatformDomainNameResolver::doRun(): Error 2" << std::endl;
+ emitError();
+ }
+ safeToJoin = true;
+ }
+
+ void emitError() {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), shared_from_this());
+ }
+
+ boost::asio::io_service ioService;
+ String hostname;
+ boost::thread* thread;
+ bool safeToJoin;
+ };
+
+}
+
+namespace Swift {
+
+PlatformDomainNameResolver::PlatformDomainNameResolver() {
+}
+
+boost::shared_ptr<DomainNameServiceQuery> PlatformDomainNameResolver::createServiceQuery(const String& name) {
+ return boost::shared_ptr<DomainNameServiceQuery>(new PlatformDomainNameServiceQuery(getNormalized(name)));
+}
+
+boost::shared_ptr<DomainNameAddressQuery> PlatformDomainNameResolver::createAddressQuery(const String& name) {
+ return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(getNormalized(name)));
+}
+
+}
diff --git a/Swiften/Network/PlatformDomainNameResolver.h b/Swiften/Network/PlatformDomainNameResolver.h
new file mode 100644
index 0000000..4617b15
--- /dev/null
+++ b/Swiften/Network/PlatformDomainNameResolver.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "Swiften/Network/DomainNameResolver.h"
+
+namespace Swift {
+ class String;
+
+ class PlatformDomainNameResolver : public DomainNameResolver {
+ public:
+ PlatformDomainNameResolver();
+
+ virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name);
+ virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name);
+ };
+}
diff --git a/Swiften/Network/PlatformDomainNameServiceQuery.cpp b/Swiften/Network/PlatformDomainNameServiceQuery.cpp
new file mode 100644
index 0000000..bde851b
--- /dev/null
+++ b/Swiften/Network/PlatformDomainNameServiceQuery.cpp
@@ -0,0 +1,170 @@
+#include "Swiften/Network/PlatformDomainNameServiceQuery.h"
+
+#include "Swiften/Base/Platform.h"
+#include <stdlib.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 <boost/bind.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Base/foreach.h"
+
+using namespace Swift;
+
+namespace Swift {
+
+PlatformDomainNameServiceQuery::PlatformDomainNameServiceQuery(const String& service) : thread(NULL), service(service), safeToJoin(true) {
+}
+
+PlatformDomainNameServiceQuery::~PlatformDomainNameServiceQuery() {
+ if (safeToJoin) {
+ thread->join();
+ }
+ else {
+ // FIXME: UGLYYYYY
+ }
+ delete thread;
+}
+
+void PlatformDomainNameServiceQuery::run() {
+ safeToJoin = false;
+ thread = new boost::thread(boost::bind(&PlatformDomainNameServiceQuery::doRun, shared_from_this()));
+}
+
+void PlatformDomainNameServiceQuery::doRun() {
+ std::vector<DomainNameServiceQuery::Result> records;
+
+#if defined(SWIFTEN_PLATFORM_WINDOWS)
+ DNS_RECORD* responses;
+ // FIXME: This conversion doesn't work if unicode is deffed above
+ if (DnsQuery(service.getUTF8Data(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &responses, NULL) != ERROR_SUCCESS) {
+ emitError();
+ return;
+ }
+
+ DNS_RECORD* currentEntry = responses;
+ while (currentEntry) {
+ if (currentEntry->wType == DNS_TYPE_SRV) {
+ DomainNameServiceQuery::Result record;
+ record.priority = currentEntry->Data.SRV.wPriority;
+ record.weight = currentEntry->Data.SRV.wWeight;
+ record.port = currentEntry->Data.SRV.wPort;
+
+ // 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
+ record.hostname = String((const char*) currentEntry->Data.SRV.pNameTarget);
+ records.push_back(record);
+ }
+ currentEntry = currentEntry->pNext;
+ }
+ DnsRecordListFree(responses, DnsFreeRecordList);
+
+#else
+ // Make sure we reinitialize the domain list every time
+ res_init();
+
+ //std::cout << "SRV: Querying " << service << std::endl;
+ ByteArray response;
+ response.resize(NS_PACKETSZ);
+ int responseLength = res_query(const_cast<char*>(service.getUTF8Data()), ns_c_in, ns_t_srv, reinterpret_cast<u_char*>(response.getData()), response.getSize());
+ //std::cout << "res_query done " << (responseLength != -1) << std::endl;
+ if (responseLength == -1) {
+ emitError();
+ return;
+ }
+
+ // 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) {
+ emitError();
+ return;
+ }
+ currentEntry += entryLength + NS_QFIXEDSZ;
+ queriesCount--;
+ }
+
+ // Process the SRV answers
+ int answersCount = ntohs(header->ancount);
+ while (answersCount > 0) {
+ DomainNameServiceQuery::Result record;
+
+ int entryLength = dn_skipname(currentEntry, messageEnd);
+ currentEntry += entryLength;
+ currentEntry += NS_RRFIXEDSZ;
+
+ // Priority
+ if (currentEntry + 2 >= messageEnd) {
+ emitError();
+ return;
+ }
+ record.priority = ns_get16(currentEntry);
+ currentEntry += 2;
+
+ // Weight
+ if (currentEntry + 2 >= messageEnd) {
+ emitError();
+ return;
+ }
+ record.weight = ns_get16(currentEntry);
+ currentEntry += 2;
+
+ // Port
+ if (currentEntry + 2 >= messageEnd) {
+ emitError();
+ return;
+ }
+ record.port = ns_get16(currentEntry);
+ currentEntry += 2;
+
+ // Hostname
+ if (currentEntry >= messageEnd) {
+ emitError();
+ return;
+ }
+ ByteArray entry;
+ entry.resize(NS_MAXDNAME);
+ entryLength = dn_expand(messageStart, messageEnd, currentEntry, entry.getData(), entry.getSize());
+ if (entryLength < 0) {
+ emitError();
+ return;
+ }
+ record.hostname = String(entry.getData());
+ records.push_back(record);
+ currentEntry += entryLength;
+ answersCount--;
+ }
+#endif
+
+ safeToJoin = true;
+ std::sort(records.begin(), records.end(), ResultPriorityComparator());
+ //std::cout << "Sending out " << records.size() << " SRV results " << std::endl;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), records));
+}
+
+void PlatformDomainNameServiceQuery::emitError() {
+ safeToJoin = true;
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), std::vector<DomainNameServiceQuery::Result>()), shared_from_this());
+}
+
+}
diff --git a/Swiften/Network/PlatformDomainNameServiceQuery.h b/Swiften/Network/PlatformDomainNameServiceQuery.h
new file mode 100644
index 0000000..753e2c6
--- /dev/null
+++ b/Swiften/Network/PlatformDomainNameServiceQuery.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <boost/thread.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Network/DomainNameServiceQuery.h"
+#include "Swiften/EventLoop/EventOwner.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class PlatformDomainNameServiceQuery : public DomainNameServiceQuery, public boost::enable_shared_from_this<PlatformDomainNameServiceQuery>, public EventOwner {
+ public:
+ PlatformDomainNameServiceQuery(const String& service);
+ ~PlatformDomainNameServiceQuery();
+
+ virtual void run();
+
+ private:
+ void doRun();
+ void emitError();
+
+ private:
+ boost::thread* thread;
+ String service;
+ bool safeToJoin;
+ };
+}
diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript
new file mode 100644
index 0000000..937ab0c
--- /dev/null
+++ b/Swiften/Network/SConscript
@@ -0,0 +1,34 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
+if myenv["target"] == "native":
+ myenv.MergeFlags(myenv["CARES_FLAGS"])
+
+sourceList = [
+ "BoostConnection.cpp",
+ "BoostConnectionFactory.cpp",
+ "BoostConnectionServer.cpp",
+ "MainBoostIOServiceThread.cpp",
+ "BoostIOServiceThread.cpp",
+ "ConnectionFactory.cpp",
+ "ConnectionServer.cpp",
+ "Connector.cpp",
+ "TimerFactory.cpp",
+ "DummyTimerFactory.cpp",
+ "BoostTimerFactory.cpp",
+ "DomainNameResolver.cpp",
+ "DomainNameAddressQuery.cpp",
+ "DomainNameServiceQuery.cpp",
+ "PlatformDomainNameResolver.cpp",
+ "PlatformDomainNameServiceQuery.cpp",
+ "StaticDomainNameResolver.cpp",
+ "HostAddress.cpp",
+ "Timer.cpp",
+ "BoostTimer.cpp"]
+if myenv["target"] == "native":
+ sourceList.append("CAresDomainNameResolver.cpp")
+
+
+objects = myenv.StaticObject(sourceList)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/Network/StaticDomainNameResolver.cpp b/Swiften/Network/StaticDomainNameResolver.cpp
new file mode 100644
index 0000000..a7275d2
--- /dev/null
+++ b/Swiften/Network/StaticDomainNameResolver.cpp
@@ -0,0 +1,85 @@
+#include "Swiften/Network/StaticDomainNameResolver.h"
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Network/DomainNameResolveError.h"
+#include "Swiften/Base/String.h"
+
+using namespace Swift;
+
+namespace {
+ struct ServiceQuery : public DomainNameServiceQuery, public EventOwner {
+ ServiceQuery(const String& service, Swift::StaticDomainNameResolver* resolver) : service(service), resolver(resolver) {}
+
+ virtual void run() {
+ if (!resolver->getIsResponsive()) {
+ return;
+ }
+ std::vector<DomainNameServiceQuery::Result> results;
+ for(StaticDomainNameResolver::ServicesCollection::const_iterator i = resolver->getServices().begin(); i != resolver->getServices().end(); ++i) {
+ if (i->first == service) {
+ results.push_back(i->second);
+ }
+ }
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), results));
+ }
+
+ String service;
+ StaticDomainNameResolver* resolver;
+ };
+
+ struct AddressQuery : public DomainNameAddressQuery, public EventOwner {
+ AddressQuery(const String& host, StaticDomainNameResolver* resolver) : host(host), resolver(resolver) {}
+
+ virtual void run() {
+ if (!resolver->getIsResponsive()) {
+ return;
+ }
+ StaticDomainNameResolver::AddressesMap::const_iterator i = resolver->getAddresses().find(host);
+ if (i != resolver->getAddresses().end()) {
+ MainEventLoop::postEvent(
+ boost::bind(boost::ref(onResult), i->second, boost::optional<DomainNameResolveError>()));
+ }
+ else {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())));
+ }
+
+ }
+
+ String host;
+ StaticDomainNameResolver* resolver;
+ };
+}
+
+namespace Swift {
+
+StaticDomainNameResolver::StaticDomainNameResolver() : isResponsive(true) {
+}
+
+void StaticDomainNameResolver::addAddress(const String& domain, const HostAddress& address) {
+ addresses[domain] = address;
+}
+
+void StaticDomainNameResolver::addService(const String& service, const DomainNameServiceQuery::Result& result) {
+ services.push_back(std::make_pair(service, result));
+}
+
+void StaticDomainNameResolver::addXMPPClientService(const String& domain, const HostAddressPort& address) {
+ static int hostid = 0;
+ String hostname(std::string("host-") + boost::lexical_cast<std::string>(hostid));
+ hostid++;
+
+ addService("_xmpp-client._tcp." + domain, ServiceQuery::Result(hostname, address.getPort(), 0, 0));
+ addAddress(hostname, address.getAddress());
+}
+
+boost::shared_ptr<DomainNameServiceQuery> StaticDomainNameResolver::createServiceQuery(const String& name) {
+ return boost::shared_ptr<DomainNameServiceQuery>(new ServiceQuery(name, this));
+}
+
+boost::shared_ptr<DomainNameAddressQuery> StaticDomainNameResolver::createAddressQuery(const String& name) {
+ return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(name, this));
+}
+
+}
diff --git a/Swiften/Network/StaticDomainNameResolver.h b/Swiften/Network/StaticDomainNameResolver.h
new file mode 100644
index 0000000..d7e7ba4
--- /dev/null
+++ b/Swiften/Network/StaticDomainNameResolver.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <vector>
+#include <map>
+
+#include "Swiften/Network/HostAddress.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/Network/DomainNameResolver.h"
+#include "Swiften/Network/DomainNameServiceQuery.h"
+#include "Swiften/Network/DomainNameAddressQuery.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+namespace Swift {
+ class String;
+
+ class StaticDomainNameResolver : public DomainNameResolver {
+ public:
+ typedef std::map<String, HostAddress> AddressesMap;
+ typedef std::vector< std::pair<String, DomainNameServiceQuery::Result> > ServicesCollection;
+
+ public:
+ StaticDomainNameResolver();
+
+ void addAddress(const String& domain, const HostAddress& address);
+ void addService(const String& service, const DomainNameServiceQuery::Result& result);
+ void addXMPPClientService(const String& domain, const HostAddressPort&);
+
+ const AddressesMap& getAddresses() const {
+ return addresses;
+ }
+
+ const ServicesCollection& getServices() const {
+ return services;
+ }
+
+ bool getIsResponsive() const {
+ return isResponsive;
+ }
+
+ void setIsResponsive(bool b) {
+ isResponsive = b;
+ }
+
+ virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name);
+ virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name);
+
+ private:
+ bool isResponsive;
+ AddressesMap addresses;
+ ServicesCollection services;
+ };
+}
diff --git a/Swiften/Network/Timer.cpp b/Swiften/Network/Timer.cpp
new file mode 100644
index 0000000..a8d17c3
--- /dev/null
+++ b/Swiften/Network/Timer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+
+Timer::~Timer() {
+}
+
+}
diff --git a/Swiften/Network/Timer.h b/Swiften/Network/Timer.h
new file mode 100644
index 0000000..9b01a0d
--- /dev/null
+++ b/Swiften/Network/Timer.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/signals.hpp>
+
+namespace Swift {
+ class Timer {
+ public:
+ virtual ~Timer();
+
+ virtual void start() = 0;
+ virtual void stop() = 0;
+
+ boost::signal<void ()> onTick;
+ };
+}
diff --git a/Swiften/Network/TimerFactory.cpp b/Swiften/Network/TimerFactory.cpp
new file mode 100644
index 0000000..642ac52
--- /dev/null
+++ b/Swiften/Network/TimerFactory.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Network/TimerFactory.h"
+
+namespace Swift {
+
+TimerFactory::~TimerFactory() {
+}
+
+}
diff --git a/Swiften/Network/TimerFactory.h b/Swiften/Network/TimerFactory.h
new file mode 100644
index 0000000..f72a8fc
--- /dev/null
+++ b/Swiften/Network/TimerFactory.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class Timer;
+
+ class TimerFactory {
+ public:
+ virtual ~TimerFactory();
+
+ virtual boost::shared_ptr<Timer> createTimer(int milliseconds) = 0;
+ };
+}
diff --git a/Swiften/Network/UnitTest/ConnectorTest.cpp b/Swiften/Network/UnitTest/ConnectorTest.cpp
new file mode 100644
index 0000000..663011c
--- /dev/null
+++ b/Swiften/Network/UnitTest/ConnectorTest.cpp
@@ -0,0 +1,245 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/optional.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Network/Connector.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Network/ConnectionFactory.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/Network/StaticDomainNameResolver.h"
+#include "Swiften/Network/DummyTimerFactory.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+using namespace Swift;
+
+class ConnectorTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ConnectorTest);
+ CPPUNIT_TEST(testConnect);
+ CPPUNIT_TEST(testConnect_NoSRVHost);
+ CPPUNIT_TEST(testConnect_NoHosts);
+ CPPUNIT_TEST(testConnect_FirstSRVHostFails);
+ CPPUNIT_TEST(testConnect_AllSRVHostsFailWithoutFallbackHost);
+ CPPUNIT_TEST(testConnect_AllSRVHostsFailWithFallbackHost);
+ CPPUNIT_TEST(testConnect_SRVAndFallbackHostsFail);
+ CPPUNIT_TEST(testConnect_TimeoutDuringResolve);
+ CPPUNIT_TEST(testConnect_TimeoutDuringConnect);
+ CPPUNIT_TEST(testConnect_NoTimeout);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ConnectorTest() : host1(HostAddress("1.1.1.1"), 1234), host2(HostAddress("2.2.2.2"), 2345), host3(HostAddress("3.3.3.3"), 5222) {
+ }
+
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ resolver = new StaticDomainNameResolver();
+ connectionFactory = new MockConnectionFactory();
+ timerFactory = new DummyTimerFactory();
+ }
+
+ void tearDown() {
+ delete timerFactory;
+ delete connectionFactory;
+ delete resolver;
+ delete eventLoop;
+ }
+
+ void testConnect() {
+ std::auto_ptr<Connector> testling(createConnector());
+ resolver->addXMPPClientService("foo.com", host1);
+ resolver->addXMPPClientService("foo.com", host2);
+ resolver->addAddress("foo.com", host3.getAddress());
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(connections[0]);
+ CPPUNIT_ASSERT(host1 == *(connections[0]->hostAddressPort));
+ }
+
+ void testConnect_NoSRVHost() {
+ std::auto_ptr<Connector> testling(createConnector());
+ resolver->addAddress("foo.com", host3.getAddress());
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(connections[0]);
+ CPPUNIT_ASSERT(host3 == *(connections[0]->hostAddressPort));
+ }
+
+ void testConnect_NoHosts() {
+ std::auto_ptr<Connector> testling(createConnector());
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(!connections[0]);
+ }
+
+ void testConnect_FirstSRVHostFails() {
+ std::auto_ptr<Connector> testling(createConnector());
+ resolver->addXMPPClientService("foo.com", host1);
+ resolver->addXMPPClientService("foo.com", host2);
+ connectionFactory->failingPorts.push_back(host1);
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(host2 == *(connections[0]->hostAddressPort));
+ }
+
+ void testConnect_AllSRVHostsFailWithoutFallbackHost() {
+ std::auto_ptr<Connector> testling(createConnector());
+ resolver->addXMPPClientService("foo.com", host1);
+ resolver->addXMPPClientService("foo.com", host2);
+ connectionFactory->failingPorts.push_back(host1);
+ connectionFactory->failingPorts.push_back(host2);
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(!connections[0]);
+ }
+
+ void testConnect_AllSRVHostsFailWithFallbackHost() {
+ std::auto_ptr<Connector> testling(createConnector());
+ resolver->addXMPPClientService("foo.com", host1);
+ resolver->addXMPPClientService("foo.com", host2);
+ resolver->addAddress("foo.com", host3.getAddress());
+ connectionFactory->failingPorts.push_back(host1);
+ connectionFactory->failingPorts.push_back(host2);
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(connections[0]);
+ CPPUNIT_ASSERT(host3 == *(connections[0]->hostAddressPort));
+ }
+
+ void testConnect_SRVAndFallbackHostsFail() {
+ std::auto_ptr<Connector> testling(createConnector());
+ resolver->addXMPPClientService("foo.com", host1);
+ resolver->addAddress("foo.com", host3.getAddress());
+ connectionFactory->failingPorts.push_back(host1);
+ connectionFactory->failingPorts.push_back(host3);
+
+ testling->start();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(!connections[0]);
+ }
+
+ void testConnect_TimeoutDuringResolve() {
+ std::auto_ptr<Connector> testling(createConnector());
+ testling->setTimeoutMilliseconds(10);
+ resolver->setIsResponsive(false);
+
+ testling->start();
+ eventLoop->processEvents();
+ timerFactory->setTime(10);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(!connections[0]);
+ }
+
+ void testConnect_TimeoutDuringConnect() {
+ std::auto_ptr<Connector> testling(createConnector());
+ testling->setTimeoutMilliseconds(10);
+ resolver->addXMPPClientService("foo.com", host1);
+ connectionFactory->isResponsive = false;
+
+ testling->start();
+ eventLoop->processEvents();
+ timerFactory->setTime(10);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(!connections[0]);
+ }
+
+ void testConnect_NoTimeout() {
+ std::auto_ptr<Connector> testling(createConnector());
+ testling->setTimeoutMilliseconds(10);
+ resolver->addXMPPClientService("foo.com", host1);
+
+ testling->start();
+ eventLoop->processEvents();
+ timerFactory->setTime(10);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size()));
+ CPPUNIT_ASSERT(connections[0]);
+ }
+
+
+ private:
+ Connector* createConnector() {
+ Connector* connector = new Connector("foo.com", resolver, connectionFactory, timerFactory);
+ connector->onConnectFinished.connect(boost::bind(&ConnectorTest::handleConnectorFinished, this, _1));
+ return connector;
+ }
+
+ void handleConnectorFinished(boost::shared_ptr<Connection> connection) {
+ boost::shared_ptr<MockConnection> c(boost::dynamic_pointer_cast<MockConnection>(connection));
+ if (connection) {
+ assert(c);
+ }
+ connections.push_back(c);
+ }
+
+ struct MockConnection : public Connection {
+ public:
+ MockConnection(const std::vector<HostAddressPort>& failingPorts, bool isResponsive) : failingPorts(failingPorts), isResponsive(isResponsive) {}
+
+ void listen() { assert(false); }
+ void connect(const HostAddressPort& address) {
+ hostAddressPort = address;
+ if (isResponsive) {
+ MainEventLoop::postEvent(boost::bind(boost::ref(onConnectFinished), std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end()));
+ }
+ }
+
+ void disconnect() { assert(false); }
+ void write(const ByteArray&) { assert(false); }
+
+ boost::optional<HostAddressPort> hostAddressPort;
+ std::vector<HostAddressPort> failingPorts;
+ bool isResponsive;
+ };
+
+ struct MockConnectionFactory : public ConnectionFactory {
+ MockConnectionFactory() : isResponsive(true) {
+ }
+
+ boost::shared_ptr<Connection> createConnection() {
+ return boost::shared_ptr<Connection>(new MockConnection(failingPorts, isResponsive));
+ }
+
+ bool isResponsive;
+ std::vector<HostAddressPort> failingPorts;
+ };
+
+ private:
+ HostAddressPort host1;
+ HostAddressPort host2;
+ HostAddressPort host3;
+ DummyEventLoop* eventLoop;
+ StaticDomainNameResolver* resolver;
+ MockConnectionFactory* connectionFactory;
+ DummyTimerFactory* timerFactory;
+ std::vector< boost::shared_ptr<MockConnection> > connections;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ConnectorTest);
diff --git a/Swiften/Network/UnitTest/HostAddressTest.cpp b/Swiften/Network/UnitTest/HostAddressTest.cpp
new file mode 100644
index 0000000..50e9198
--- /dev/null
+++ b/Swiften/Network/UnitTest/HostAddressTest.cpp
@@ -0,0 +1,38 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Network/HostAddress.h"
+#include "Swiften/Base/String.h"
+
+using namespace Swift;
+
+class HostAddressTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(HostAddressTest);
+ CPPUNIT_TEST(testConstructor);
+ CPPUNIT_TEST(testToString);
+ CPPUNIT_TEST(testToString_IPv6);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testConstructor() {
+ HostAddress testling("192.168.1.254");
+
+ CPPUNIT_ASSERT_EQUAL(std::string("192.168.1.254"), testling.toString());
+ }
+
+ 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/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/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..82c839a
--- /dev/null
+++ b/Swiften/Parser/AttributeMap.h
@@ -0,0 +1,35 @@
+#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;
+ }
+ }
+
+ bool getBoolAttribute(const String& attribute, bool defaultValue = false) const {
+ AttributeMap::const_iterator i = find(attribute);
+ if (i == end()) {
+ return defaultValue;
+ }
+ else {
+ return i->second == "true" || i->second == "1";
+ }
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Parser/AuthChallengeParser.cpp b/Swiften/Parser/AuthChallengeParser.cpp
new file mode 100644
index 0000000..c83cf7d
--- /dev/null
+++ b/Swiften/Parser/AuthChallengeParser.cpp
@@ -0,0 +1,24 @@
+#include "Swiften/Parser/AuthChallengeParser.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthChallengeParser::AuthChallengeParser() : GenericElementParser<AuthChallenge>(), depth(0) {
+}
+
+void AuthChallengeParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++depth;
+}
+
+void AuthChallengeParser::handleEndElement(const String&, const String&) {
+ --depth;
+ if (depth == 0) {
+ getElementGeneric()->setValue(Base64::decode(text));
+ }
+}
+
+void AuthChallengeParser::handleCharacterData(const String& text) {
+ this->text += text;
+}
+
+}
diff --git a/Swiften/Parser/AuthChallengeParser.h b/Swiften/Parser/AuthChallengeParser.h
new file mode 100644
index 0000000..be44b96
--- /dev/null
+++ b/Swiften/Parser/AuthChallengeParser.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/AuthChallenge.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class AuthChallengeParser : public GenericElementParser<AuthChallenge> {
+ public:
+ AuthChallengeParser();
+
+ virtual void handleStartElement(const String&, const String& ns, const AttributeMap&);
+ virtual void handleEndElement(const String&, const String& ns);
+ virtual void handleCharacterData(const String&);
+
+ private:
+ int depth;
+ String text;
+ };
+}
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/AuthResponseParser.cpp b/Swiften/Parser/AuthResponseParser.cpp
new file mode 100644
index 0000000..b5976a5
--- /dev/null
+++ b/Swiften/Parser/AuthResponseParser.cpp
@@ -0,0 +1,24 @@
+#include "Swiften/Parser/AuthResponseParser.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthResponseParser::AuthResponseParser() : GenericElementParser<AuthResponse>(), depth(0) {
+}
+
+void AuthResponseParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++depth;
+}
+
+void AuthResponseParser::handleEndElement(const String&, const String&) {
+ --depth;
+ if (depth == 0) {
+ getElementGeneric()->setValue(Base64::decode(text));
+ }
+}
+
+void AuthResponseParser::handleCharacterData(const String& text) {
+ this->text += text;
+}
+
+}
diff --git a/Swiften/Parser/AuthResponseParser.h b/Swiften/Parser/AuthResponseParser.h
new file mode 100644
index 0000000..f2b3a9e
--- /dev/null
+++ b/Swiften/Parser/AuthResponseParser.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/AuthResponse.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class AuthResponseParser : public GenericElementParser<AuthResponse> {
+ public:
+ AuthResponseParser();
+
+ virtual void handleStartElement(const String&, const String& ns, const AttributeMap&);
+ virtual void handleEndElement(const String&, const String& ns);
+ virtual void handleCharacterData(const String&);
+
+ private:
+ int depth;
+ String text;
+ };
+}
diff --git a/Swiften/Parser/AuthSuccessParser.cpp b/Swiften/Parser/AuthSuccessParser.cpp
new file mode 100644
index 0000000..2dc2aa2
--- /dev/null
+++ b/Swiften/Parser/AuthSuccessParser.cpp
@@ -0,0 +1,24 @@
+#include "Swiften/Parser/AuthSuccessParser.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthSuccessParser::AuthSuccessParser() : GenericElementParser<AuthSuccess>(), depth(0) {
+}
+
+void AuthSuccessParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ ++depth;
+}
+
+void AuthSuccessParser::handleEndElement(const String&, const String&) {
+ --depth;
+ if (depth == 0) {
+ getElementGeneric()->setValue(Base64::decode(text));
+ }
+}
+
+void AuthSuccessParser::handleCharacterData(const String& text) {
+ this->text += text;
+}
+
+}
diff --git a/Swiften/Parser/AuthSuccessParser.h b/Swiften/Parser/AuthSuccessParser.h
new file mode 100644
index 0000000..5d987c5
--- /dev/null
+++ b/Swiften/Parser/AuthSuccessParser.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Parser/GenericElementParser.h"
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class AuthSuccessParser : public GenericElementParser<AuthSuccess> {
+ public:
+ AuthSuccessParser();
+
+ virtual void handleStartElement(const String&, const String& ns, const AttributeMap&);
+ virtual void handleEndElement(const String&, const String& ns);
+ virtual void handleCharacterData(const String&);
+
+ private:
+ int depth;
+ String text;
+ };
+}
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..6f7ff86
--- /dev/null
+++ b/Swiften/Parser/ExpatParser.cpp
@@ -0,0 +1,70 @@
+#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);
+ if (nsTagPair.second == "") {
+ nsTagPair.second = nsTagPair.first;
+ nsTagPair.first = "";
+ }
+ 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..f43ed00
--- /dev/null
+++ b/Swiften/Parser/LibXMLParser.cpp
@@ -0,0 +1,65 @@
+#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*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int, const xmlChar ** attributes) {
+ AttributeMap attributeValues;
+ for (int i = 0; i < nbAttributes*5; i += 5) {
+ attributeValues[String(reinterpret_cast<const char*>(attributes[i]))] = String(reinterpret_cast<const char*>(attributes[i+3]), attributes[i+4]-attributes[i+3]);
+ }
+ static_cast<XMLParserClient*>(client)->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : String()), attributeValues);
+}
+
+static void handleEndElement(void *client, const xmlChar* name, const xmlChar*, const xmlChar* xmlns) {
+ static_cast<XMLParserClient*>(client)->handleEndElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : String()));
+}
+
+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*, ... ) {
+ /*
+ va_list args;
+ va_start(args, m);
+ vfprintf(stdout, m, args);
+ va_end(args);
+ */
+}
+
+static void handleWarning(void*, const char*, ... ) {
+}
+
+
+
+LibXMLParser::LibXMLParser(XMLParserClient* client) : XMLParser(client) {
+ memset(&handler_, 0, sizeof(handler_) );
+ handler_.initialized = XML_SAX2_MAGIC;
+ handler_.startElementNs = &handleStartElement;
+ handler_.endElementNs = &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..58a65f1
--- /dev/null
+++ b/Swiften/Parser/LibXMLParser.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#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_;
+ };
+}
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..6d3a4cf
--- /dev/null
+++ b/Swiften/Parser/PayloadParserFactoryCollection.cpp
@@ -0,0 +1,31 @@
+#include <boost/bind.hpp>
+#include <algorithm>
+
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+
+namespace Swift {
+
+PayloadParserFactoryCollection::PayloadParserFactoryCollection() : defaultFactory_(NULL) {
+}
+
+void PayloadParserFactoryCollection::addFactory(PayloadParserFactory* factory) {
+ factories_.push_back(factory);
+}
+
+void PayloadParserFactoryCollection::removeFactory(PayloadParserFactory* factory) {
+ factories_.erase(remove(factories_.begin(), factories_.end(), factory), factories_.end());
+}
+
+void PayloadParserFactoryCollection::setDefaultFactory(PayloadParserFactory* factory) {
+ defaultFactory_ = factory;
+}
+
+PayloadParserFactory* PayloadParserFactoryCollection::getPayloadParserFactory(const String& element, const String& ns, const AttributeMap& attributes) {
+ std::vector<PayloadParserFactory*>::reverse_iterator i = std::find_if(
+ factories_.rbegin(), factories_.rend(),
+ boost::bind(&PayloadParserFactory::canParse, _1, element, ns, attributes));
+ return (i != factories_.rend() ? *i : defaultFactory_);
+}
+
+}
diff --git a/Swiften/Parser/PayloadParserFactoryCollection.h b/Swiften/Parser/PayloadParserFactoryCollection.h
new file mode 100644
index 0000000..80a763b
--- /dev/null
+++ b/Swiften/Parser/PayloadParserFactoryCollection.h
@@ -0,0 +1,28 @@
+#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);
+ void setDefaultFactory(PayloadParserFactory* factory);
+
+ PayloadParserFactory* getPayloadParserFactory(const String& element, const String& ns, const AttributeMap& attributes);
+
+ private:
+ std::vector<PayloadParserFactory*> factories_;
+ PayloadParserFactory* defaultFactory_;
+ };
+}
+
+#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/ChatStateParser.cpp b/Swiften/Parser/PayloadParsers/ChatStateParser.cpp
new file mode 100644
index 0000000..52d860a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ChatStateParser.cpp
@@ -0,0 +1,35 @@
+#include "Swiften/Parser/PayloadParsers/ChatStateParser.h"
+
+namespace Swift {
+
+ChatStateParser::ChatStateParser() : level_(0) {
+}
+
+void ChatStateParser::handleStartElement(const String& element, const String&, const AttributeMap&) {
+ if (level_ == 0) {
+ ChatState::ChatStateType state = ChatState::Active;
+ if (element == "active") {
+ state = ChatState::Active;
+ } else if (element == "composing") {
+ state = ChatState::Composing;
+ } else if (element == "inactive") {
+ state = ChatState::Inactive;
+ } else if (element == "paused") {
+ state = ChatState::Paused;
+ } else if (element == "gone") {
+ state = ChatState::Gone;
+ }
+ getPayloadInternal()->setChatState(state);
+ }
+ ++level_;
+}
+
+void ChatStateParser::handleEndElement(const String&, const String&) {
+ --level_;
+}
+
+void ChatStateParser::handleCharacterData(const String&) {
+
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/ChatStateParser.h b/Swiften/Parser/PayloadParsers/ChatStateParser.h
new file mode 100644
index 0000000..cd212c0
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ChatStateParser.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Swiften/Elements/ChatState.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class ChatStateParser : public GenericPayloadParser<ChatState> {
+ public:
+ ChatStateParser();
+
+ 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_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/ChatStateParserFactory.h b/Swiften/Parser/PayloadParsers/ChatStateParserFactory.h
new file mode 100644
index 0000000..1582d09
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ChatStateParserFactory.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ChatStateParser.h"
+
+namespace Swift {
+ class PayloadParserFactoryCollection;
+
+ class ChatStateParserFactory : public PayloadParserFactory {
+ public:
+ ChatStateParserFactory() {
+ }
+
+ virtual bool canParse(const String& element, const String& ns, const AttributeMap&) const {
+ return ns == "http://jabber.org/protocol/chatstates" &&
+ (element == "active" || element == "composing"
+ || element == "paused" || element == "inactive" || element == "gone");
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new ChatStateParser();
+ }
+
+ };
+}
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..ae85265
--- /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(ErrorPayload::Continue);
+ }
+ else if (type == "modify") {
+ getPayloadInternal()->setType(ErrorPayload::Modify);
+ }
+ else if (type == "auth") {
+ getPayloadInternal()->setType(ErrorPayload::Auth);
+ }
+ else if (type == "wait") {
+ getPayloadInternal()->setType(ErrorPayload::Wait);
+ }
+ else {
+ getPayloadInternal()->setType(ErrorPayload::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(ErrorPayload::BadRequest);
+ }
+ else if (element == "conflict") {
+ getPayloadInternal()->setCondition(ErrorPayload::Conflict);
+ }
+ else if (element == "feature-not-implemented") {
+ getPayloadInternal()->setCondition(ErrorPayload::FeatureNotImplemented);
+ }
+ else if (element == "forbidden") {
+ getPayloadInternal()->setCondition(ErrorPayload::Forbidden);
+ }
+ else if (element == "gone") {
+ getPayloadInternal()->setCondition(ErrorPayload::Gone);
+ }
+ else if (element == "internal-server-error") {
+ getPayloadInternal()->setCondition(ErrorPayload::InternalServerError);
+ }
+ else if (element == "item-not-found") {
+ getPayloadInternal()->setCondition(ErrorPayload::ItemNotFound);
+ }
+ else if (element == "jid-malformed") {
+ getPayloadInternal()->setCondition(ErrorPayload::JIDMalformed);
+ }
+ else if (element == "not-acceptable") {
+ getPayloadInternal()->setCondition(ErrorPayload::NotAcceptable);
+ }
+ else if (element == "not-allowed") {
+ getPayloadInternal()->setCondition(ErrorPayload::NotAllowed);
+ }
+ else if (element == "not-authorized") {
+ getPayloadInternal()->setCondition(ErrorPayload::NotAuthorized);
+ }
+ else if (element == "payment-required") {
+ getPayloadInternal()->setCondition(ErrorPayload::PaymentRequired);
+ }
+ else if (element == "recipient-unavailable") {
+ getPayloadInternal()->setCondition(ErrorPayload::RecipientUnavailable);
+ }
+ else if (element == "redirect") {
+ getPayloadInternal()->setCondition(ErrorPayload::Redirect);
+ }
+ else if (element == "registration-required") {
+ getPayloadInternal()->setCondition(ErrorPayload::RegistrationRequired);
+ }
+ else if (element == "remote-server-not-found") {
+ getPayloadInternal()->setCondition(ErrorPayload::RemoteServerNotFound);
+ }
+ else if (element == "remote-server-timeout") {
+ getPayloadInternal()->setCondition(ErrorPayload::RemoteServerTimeout);
+ }
+ else if (element == "resource-constraint") {
+ getPayloadInternal()->setCondition(ErrorPayload::ResourceConstraint);
+ }
+ else if (element == "service-unavailable") {
+ getPayloadInternal()->setCondition(ErrorPayload::ServiceUnavailable);
+ }
+ else if (element == "subscription-required") {
+ getPayloadInternal()->setCondition(ErrorPayload::SubscriptionRequired);
+ }
+ else if (element == "unexpected-request") {
+ getPayloadInternal()->setCondition(ErrorPayload::UnexpectedRequest);
+ }
+ else {
+ getPayloadInternal()->setCondition(ErrorPayload::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..17b78b9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ErrorParser.h
@@ -0,0 +1,26 @@
+#ifndef SWIFTEN_ErrorParser_H
+#define SWIFTEN_ErrorParser_H
+
+#include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class ErrorParser : public GenericPayloadParser<ErrorPayload> {
+ 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..0857f64
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -0,0 +1,61 @@
+#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/ChatStateParserFactory.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/StorageParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/VCardParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.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 StorageParserFactory()));
+ 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()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardUpdateParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new PrivateStorageParserFactory(this)));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new ChatStateParserFactory()));
+ foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
+ addFactory(factory.get());
+ }
+ defaultFactory_ = new RawXMLPayloadParserFactory();
+ setDefaultFactory(defaultFactory_);
+}
+
+FullPayloadParserFactoryCollection::~FullPayloadParserFactoryCollection() {
+ setDefaultFactory(NULL);
+ delete defaultFactory_;
+ 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..82e5a56
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#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_;
+ PayloadParserFactory* defaultFactory_;
+ };
+}
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/PrivateStorageParser.cpp b/Swiften/Parser/PayloadParsers/PrivateStorageParser.cpp
new file mode 100644
index 0000000..5c3af26
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/PrivateStorageParser.cpp
@@ -0,0 +1,43 @@
+#include "Swiften/Parser/PayloadParsers/PrivateStorageParser.h"
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+
+namespace Swift {
+
+PrivateStorageParser::PrivateStorageParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0) {
+}
+
+void PrivateStorageParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ if (level == 1) {
+ PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+ if (payloadParserFactory) {
+ currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+ }
+ }
+
+ if (level >= 1 && currentPayloadParser.get()) {
+ currentPayloadParser->handleStartElement(element, ns, attributes);
+ }
+ ++level;
+}
+
+void PrivateStorageParser::handleEndElement(const String& element, const String& ns) {
+ --level;
+ if (currentPayloadParser.get()) {
+ if (level >= 1) {
+ currentPayloadParser->handleEndElement(element, ns);
+ }
+
+ if (level == 1) {
+ getPayloadInternal()->setPayload(currentPayloadParser->getPayload());
+ }
+ }
+}
+
+void PrivateStorageParser::handleCharacterData(const String& data) {
+ if (level > 1 && currentPayloadParser.get()) {
+ currentPayloadParser->handleCharacterData(data);
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/PrivateStorageParser.h b/Swiften/Parser/PayloadParsers/PrivateStorageParser.h
new file mode 100644
index 0000000..fae0f10
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/PrivateStorageParser.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/PrivateStorage.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class PayloadParserFactoryCollection;
+
+ class PrivateStorageParser : public GenericPayloadParser<PrivateStorage> {
+ public:
+ PrivateStorageParser(PayloadParserFactoryCollection* factories);
+
+ private:
+ 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:
+ PayloadParserFactoryCollection* factories;
+ int level;
+ std::auto_ptr<PayloadParser> currentPayloadParser;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.h b/Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.h
new file mode 100644
index 0000000..4d9c02b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/PrivateStorageParser.h"
+
+namespace Swift {
+ class PayloadParserFactoryCollection;
+
+ class PrivateStorageParserFactory : public PayloadParserFactory {
+ public:
+ PrivateStorageParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+ }
+
+ virtual bool canParse(const String& element, const String& ns, const AttributeMap&) const {
+ return element == "query" && ns == "jabber:iq:private";
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new PrivateStorageParser(factories);
+ }
+
+ private:
+ PayloadParserFactoryCollection* factories;
+
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/RawXMLPayloadParser.cpp b/Swiften/Parser/PayloadParsers/RawXMLPayloadParser.cpp
new file mode 100644
index 0000000..c49af3e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/RawXMLPayloadParser.cpp
@@ -0,0 +1,26 @@
+#include "Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h"
+#include "Swiften/Parser/SerializingParser.h"
+
+namespace Swift {
+
+RawXMLPayloadParser::RawXMLPayloadParser() : level_(0) {
+}
+
+void RawXMLPayloadParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ ++level_;
+ serializingParser_.handleStartElement(element, ns, attributes);
+}
+
+void RawXMLPayloadParser::handleEndElement(const String& element, const String& ns) {
+ serializingParser_.handleEndElement(element, ns);
+ --level_;
+ if (level_ == 0) {
+ getPayloadInternal()->setRawXML(serializingParser_.getResult());
+ }
+}
+
+void RawXMLPayloadParser::handleCharacterData(const String& data) {
+ serializingParser_.handleCharacterData(data);
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h b/Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h
new file mode 100644
index 0000000..f636486
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Swiften/Elements/RawXMLPayload.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+#include "Swiften/Parser/SerializingParser.h"
+
+namespace Swift {
+ class SerializingParser;
+
+ class RawXMLPayloadParser : public GenericPayloadParser<RawXMLPayload> {
+ public:
+ RawXMLPayloadParser();
+
+ 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_;
+ SerializingParser serializingParser_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h b/Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h
new file mode 100644
index 0000000..46b1183
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class RawXMLPayloadParserFactory : public PayloadParserFactory {
+ public:
+ RawXMLPayloadParserFactory() {}
+
+ virtual bool canParse(const String&, const String&, const AttributeMap&) const {
+ return true;
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new RawXMLPayloadParser();
+ }
+ };
+}
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..e4da756
--- /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..799e43a
--- /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/StorageParser.cpp b/Swiften/Parser/PayloadParsers/StorageParser.cpp
new file mode 100644
index 0000000..3eab15e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StorageParser.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Parser/PayloadParsers/StorageParser.h"
+
+#include <cassert>
+
+namespace Swift {
+
+StorageParser::StorageParser() : level(TopLevel) {
+}
+
+void StorageParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+ if (level == BookmarkLevel) {
+ if (element == "conference") {
+ assert(!conference);
+ conference = Storage::Conference();
+ conference->autoJoin = attributes.getBoolAttribute("autojoin", false);
+ conference->jid = JID(attributes.getAttribute("jid"));
+ conference->name = attributes.getAttribute("name");
+ }
+ }
+ else if (level == DetailLevel) {
+ currentText = "";
+ }
+ ++level;
+}
+
+void StorageParser::handleEndElement(const String& element, const String&) {
+ --level;
+ if (level == BookmarkLevel) {
+ if (element == "conference") {
+ assert(conference);
+ getPayloadInternal()->addConference(*conference);
+ conference.reset();
+ }
+ }
+ else if (level == DetailLevel && conference) {
+ if (element == "nick") {
+ conference->nick = currentText;
+ }
+ else if (element == "password") {
+ conference->password = currentText;
+ }
+ }
+}
+
+void StorageParser::handleCharacterData(const String& data) {
+ currentText += data;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StorageParser.h b/Swiften/Parser/PayloadParsers/StorageParser.h
new file mode 100644
index 0000000..ad0ab4d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StorageParser.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/Storage.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class StorageParser : public GenericPayloadParser<Storage> {
+ public:
+ StorageParser();
+
+ 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,
+ BookmarkLevel = 1,
+ DetailLevel = 2
+ };
+ int level;
+ String currentText;
+ boost::optional<Storage::Conference> conference;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/StorageParserFactory.h b/Swiften/Parser/PayloadParsers/StorageParserFactory.h
new file mode 100644
index 0000000..147d178
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StorageParserFactory.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/StorageParser.h"
+
+namespace Swift {
+ class StorageParserFactory : public GenericPayloadParserFactory<StorageParser> {
+ public:
+ StorageParserFactory() : GenericPayloadParserFactory<StorageParser>("storage", "storage:bookmarks") {}
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp
new file mode 100644
index 0000000..2fc6180
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/BodyParserTest.cpp
@@ -0,0 +1,28 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/BodyParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class BodyParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(BodyParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ BodyParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<body>foo<baz>bar</baz>fum</body>"));
+
+ Body* payload = dynamic_cast<Body*>(parser.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..49b706e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp
@@ -0,0 +1,47 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class DiscoInfoParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(DiscoInfoParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ DiscoInfoParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ 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*>(parser.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..dcd3172
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp
@@ -0,0 +1,34 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/ErrorParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class ErrorParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ErrorParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ErrorParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ 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>"));
+
+ ErrorPayload* payload = dynamic_cast<ErrorPayload*>(parser.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::BadRequest, payload->getCondition());
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::Modify, payload->getType());
+ CPPUNIT_ASSERT_EQUAL(String("boo"), payload->getText());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ErrorParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h b/Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h
new file mode 100644
index 0000000..20a5feb
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "Swiften/Parser/UnitTest/ParserTester.h"
+#include "Swiften/Parser/PayloadParser.h"
+
+namespace Swift {
+ typedef ParserTester<PayloadParser> PayloadParserTester;
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h b/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h
new file mode 100644
index 0000000..ac167cf
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Parser/XMLParser.h"
+#include "Swiften/Parser/XMLParserClient.h"
+#include "Swiften/Parser/PlatformXMLParserFactory.h"
+
+namespace Swift {
+ class PayloadsParserTester : public XMLParserClient {
+ public:
+ PayloadsParserTester() : level(0) {
+ xmlParser = PlatformXMLParserFactory().createXMLParser(this);
+ }
+
+ ~PayloadsParserTester() {
+ delete xmlParser;
+ }
+
+ bool parse(const String& data) {
+ return xmlParser->parse(data);
+ }
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ if (level == 0) {
+ CPPUNIT_ASSERT(!payloadParser.get());
+ PayloadParserFactory* payloadParserFactory = factories.getPayloadParserFactory(element, ns, attributes);
+ CPPUNIT_ASSERT(payloadParserFactory);
+ payloadParser.reset(payloadParserFactory->createPayloadParser());
+ }
+ payloadParser->handleStartElement(element, ns, attributes);
+ level++;
+ }
+
+ virtual void handleEndElement(const String& element, const String& ns) {
+ level--;
+ payloadParser->handleEndElement(element, ns);
+ }
+
+ virtual void handleCharacterData(const String& data) {
+ payloadParser->handleCharacterData(data);
+ }
+
+ boost::shared_ptr<Payload> getPayload() const {
+ return payloadParser->getPayload();
+ }
+
+ private:
+ XMLParser* xmlParser;
+ FullPayloadParserFactoryCollection factories;
+ std::auto_ptr<PayloadParser> payloadParser;
+ int level;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp
new file mode 100644
index 0000000..acea437
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp
@@ -0,0 +1,28 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/PriorityParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class PriorityParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PriorityParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PriorityParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<priority>-120</priority>"));
+
+ Priority* payload = dynamic_cast<Priority*>(parser.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(-120, payload->getPriority());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PriorityParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp
new file mode 100644
index 0000000..e820083
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp
@@ -0,0 +1,89 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/Storage.h"
+#include "Swiften/Parser/PayloadParsers/PrivateStorageParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class PrivateStorageParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PrivateStorageParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST(testParse_NoPayload);
+ CPPUNIT_TEST(testParse_MultiplePayloads);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PrivateStorageParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<query xmlns='jabber:iq:private'>"
+ "<storage xmlns='storage:bookmarks'>"
+ "<conference name='Swift' jid='swift@rooms.swift.im'>"
+ "<nick>Alice</nick>"
+ "</conference>"
+ "</storage>"
+ "</query>"));
+
+ boost::shared_ptr<PrivateStorage> payload = boost::dynamic_pointer_cast<PrivateStorage>(parser.getPayload());
+ CPPUNIT_ASSERT(payload);
+ boost::shared_ptr<Storage> storage = boost::dynamic_pointer_cast<Storage>(payload->getPayload());
+ CPPUNIT_ASSERT(storage);
+ CPPUNIT_ASSERT_EQUAL(String("Alice"), storage->getConferences()[0].nick);
+ CPPUNIT_ASSERT_EQUAL(JID("swift@rooms.swift.im"), storage->getConferences()[0].jid);
+ }
+
+ void testParse_NoPayload() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<query xmlns='jabber:iq:private'/>"));
+
+ boost::shared_ptr<PrivateStorage> payload = boost::dynamic_pointer_cast<PrivateStorage>(parser.getPayload());
+ CPPUNIT_ASSERT(payload);
+ CPPUNIT_ASSERT(!payload->getPayload());
+ }
+
+ void testParse_MultiplePayloads() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<query xmlns='jabber:iq:private'>"
+ "<storage xmlns='storage:bookmarks'>"
+ "<conference name='Swift' jid='swift@rooms.swift.im'>"
+ "<nick>Alice</nick>"
+ "</conference>"
+ "</storage>"
+ "<storage xmlns='storage:bookmarks'>"
+ "<conference name='Swift' jid='swift@rooms.swift.im'>"
+ "<nick>Rabbit</nick>"
+ "</conference>"
+ "</storage>"
+ "</query>"));
+
+ boost::shared_ptr<PrivateStorage> payload = boost::dynamic_pointer_cast<PrivateStorage>(parser.getPayload());
+ CPPUNIT_ASSERT(payload);
+ boost::shared_ptr<Storage> storage = boost::dynamic_pointer_cast<Storage>(payload->getPayload());
+ CPPUNIT_ASSERT(storage);
+ CPPUNIT_ASSERT_EQUAL(String("Rabbit"), storage->getConferences()[0].nick);
+ }
+
+ void testParse_UnsupportedPayload() {
+ PayloadParserFactoryCollection factories;
+ PrivateStorageParser testling(&factories);
+ PayloadParserTester parser(&testling);
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<query xmlns='jabber:iq:private'>"
+ "<foo>Bar</foo>"
+ "</query>"));
+
+ CPPUNIT_ASSERT(!boost::dynamic_pointer_cast<PrivateStorage>(testling.getPayload())->getPayload());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PrivateStorageParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp
new file mode 100644
index 0000000..81cc207
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp
@@ -0,0 +1,34 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/RawXMLPayloadParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"
+
+using namespace Swift;
+
+class RawXMLPayloadParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RawXMLPayloadParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ RawXMLPayloadParserTest() {}
+
+ void testParse() {
+ RawXMLPayloadParser testling;
+ PayloadParserTester parser(&testling);
+
+ String xml =
+ "<foo foo-attr=\"foo-val\" xmlns=\"ns:foo\">"
+ "<bar bar-attr=\"bar-val\" xmlns=\"ns:bar\"/>"
+ "<baz baz-attr=\"baz-val\" xmlns=\"ns:baz\"/>"
+ "</foo>";
+ CPPUNIT_ASSERT(parser.parse(xml));
+
+ RawXMLPayload* payload = dynamic_cast<RawXMLPayload*>(testling.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(xml, payload->getRawXML());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(RawXMLPayloadParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp
new file mode 100644
index 0000000..b1b61ef
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp
@@ -0,0 +1,38 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/ResourceBindParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.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() {
+ PayloadsParserTester parser;
+
+ 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*>(parser.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(JID("somenode@example.com/someresource"), payload->getJID());
+ }
+
+ void testParse_Resource() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>someresource</resource></bind>"));
+
+ ResourceBind* payload = dynamic_cast<ResourceBind*>(parser.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..4c8db07
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp
@@ -0,0 +1,50 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/RosterParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class RosterParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RosterParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ RosterParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+ 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*>(parser.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..8272bee
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp
@@ -0,0 +1,45 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/SecurityLabelParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class SecurityLabelParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SecurityLabelParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SecurityLabelParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ 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*>(parser.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..b2378b6
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp
@@ -0,0 +1,45 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class SecurityLabelsCatalogParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SecurityLabelsCatalogParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SecurityLabelsCatalogParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ 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*>(parser.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..eeaa8d9
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp
@@ -0,0 +1,35 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/SoftwareVersionParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class SoftwareVersionParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SoftwareVersionParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SoftwareVersionParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ 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*>(parser.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..71d5b0b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StatusParserTest.cpp
@@ -0,0 +1,28 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/StatusParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class StatusParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StatusParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StatusParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<status>foo<baz>bar</baz>fum</status>"));
+
+ Status* payload = dynamic_cast<Status*>(parser.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..d45e98b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp
@@ -0,0 +1,68 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/StatusShowParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.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() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<show>invalid</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::Online == payload->getType());
+ }
+
+ void testParse_Away() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<show>away</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::Away == payload->getType());
+ }
+
+ void testParse_FFC() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<show>chat</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::FFC == payload->getType());
+ }
+
+ void testParse_XA() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<show>xa</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::XA == payload->getType());
+ }
+
+ void testParse_DND() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse("<show>dnd</show>"));
+
+ StatusShow* payload = dynamic_cast<StatusShow*>(parser.getPayload().get());
+ CPPUNIT_ASSERT(StatusShow::DND == payload->getType());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StatusShowParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
new file mode 100644
index 0000000..3fbf27a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
@@ -0,0 +1,64 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/StorageParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class StorageParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(StorageParserTest);
+ CPPUNIT_TEST(testParse_Conference);
+ CPPUNIT_TEST(testParse_MultipleConferences);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StorageParserTest() {}
+
+ void testParse_Conference() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<storage xmlns='storage:bookmarks'>"
+ "<conference "
+ "name='Council of Oberon' "
+ "autojoin='true' jid='council@conference.underhill.org'>"
+ "<nick>Puck</nick>"
+ "<password>MyPass</password>"
+ "</conference>"
+ "</storage>"));
+
+ Storage* payload = dynamic_cast<Storage*>(parser.getPayload().get());
+ std::vector<Storage::Conference> conferences = payload->getConferences();
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(conferences.size()));
+ CPPUNIT_ASSERT_EQUAL(String("Council of Oberon"), conferences[0].name);
+ CPPUNIT_ASSERT_EQUAL(JID("council@conference.underhill.org"), conferences[0].jid);
+ CPPUNIT_ASSERT(conferences[0].autoJoin);
+ CPPUNIT_ASSERT_EQUAL(String("Puck"), conferences[0].nick);
+ CPPUNIT_ASSERT_EQUAL(String("MyPass"), conferences[0].password);
+ }
+
+ void testParse_MultipleConferences() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<storage xmlns='storage:bookmarks'>"
+ "<conference "
+ "name='Council of Oberon' "
+ "jid='council@conference.underhill.org' />"
+ "<conference "
+ "name='Tea party' "
+ "jid='teaparty@wonderland.lit' />"
+ "</storage>"));
+
+ Storage* payload = dynamic_cast<Storage*>(parser.getPayload().get());
+ std::vector<Storage::Conference> conferences = payload->getConferences();
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(conferences.size()));
+ CPPUNIT_ASSERT_EQUAL(String("Council of Oberon"), conferences[0].name);
+ CPPUNIT_ASSERT_EQUAL(JID("council@conference.underhill.org"), conferences[0].jid);
+ CPPUNIT_ASSERT_EQUAL(String("Tea party"), conferences[1].name);
+ CPPUNIT_ASSERT_EQUAL(JID("teaparty@wonderland.lit"), conferences[1].jid);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StorageParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp
new file mode 100644
index 0000000..d1ddba3
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp
@@ -0,0 +1,51 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/VCardParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class VCardParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(VCardParserTest);
+ CPPUNIT_TEST(testParse_Photo);
+ CPPUNIT_TEST(testParse_Nickname);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ VCardParserTest() {}
+
+ void testParse_Photo() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<vCard xmlns='vcard-temp'>"
+ "<PHOTO>"
+ "<TYPE>image/jpeg</TYPE>"
+ "<BINVAL>"
+ "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ej"
+ "EyMzQ1Njc4OTA="
+ "</BINVAL>"
+ "</PHOTO>"
+ "</vCard>"));
+
+ VCard* payload = dynamic_cast<VCard*>(parser.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), payload->getPhotoType());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"), payload->getPhoto());
+ }
+
+ void testParse_Nickname() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<vCard xmlns='vcard-temp'>"
+ "<NICKNAME>mynick</NICKNAME>"
+ "</vCard>"));
+
+ VCard* payload = dynamic_cast<VCard*>(parser.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("mynick"), payload->getNickname());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(VCardParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp
new file mode 100644
index 0000000..ef6c78e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp
@@ -0,0 +1,31 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParsers/VCardUpdateParser.h"
+#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h"
+
+using namespace Swift;
+
+class VCardUpdateParserTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(VCardUpdateParserTest);
+ CPPUNIT_TEST(testParse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ VCardUpdateParserTest() {}
+
+ void testParse() {
+ PayloadsParserTester parser;
+
+ CPPUNIT_ASSERT(parser.parse(
+ "<x xmlns='vcard-temp:x:update'>"
+ "<photo>sha1-hash-of-image</photo>"
+ "</x>"));
+
+ VCardUpdate* payload = dynamic_cast<VCardUpdate*>(parser.getPayload().get());
+ CPPUNIT_ASSERT_EQUAL(String("sha1-hash-of-image"), payload->getPhotoHash());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(VCardUpdateParserTest);
diff --git a/Swiften/Parser/PayloadParsers/VCardParser.cpp b/Swiften/Parser/PayloadParsers/VCardParser.cpp
new file mode 100644
index 0000000..87416ab
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/VCardParser.cpp
@@ -0,0 +1,53 @@
+#include "Swiften/Parser/PayloadParsers/VCardParser.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+VCardParser::VCardParser() {
+}
+
+void VCardParser::handleStartElement(const String& element, const String&, const AttributeMap&) {
+ elementStack_.push_back(element);
+ currentText_ = "";
+}
+
+void VCardParser::handleEndElement(const String&, const String&) {
+ String elementHierarchy = getElementHierarchy();
+ if (elementHierarchy == "/vCard/PHOTO/TYPE") {
+ getPayloadInternal()->setPhotoType(currentText_);
+ }
+ else if (elementHierarchy == "/vCard/PHOTO/BINVAL") {
+ getPayloadInternal()->setPhoto(Base64::decode(currentText_));
+ }
+ else if (elementHierarchy == "/vCard/NICKNAME") {
+ getPayloadInternal()->setNickname(currentText_);
+ }
+ else if (elementHierarchy == "/vCard/FN") {
+ getPayloadInternal()->setFullName(currentText_);
+ }
+ else if (elementHierarchy == "/vCard/N/FAMILY") {
+ getPayloadInternal()->setFamilyName(currentText_);
+ }
+ else if (elementHierarchy == "/vCard/N/GIVEN") {
+ getPayloadInternal()->setGivenName(currentText_);
+ }
+ else if (elementHierarchy == "/vCard/EMAIL/USERID") {
+ getPayloadInternal()->setEMail(currentText_);
+ }
+ elementStack_.pop_back();
+}
+
+void VCardParser::handleCharacterData(const String& text) {
+ currentText_ += text;
+}
+
+String VCardParser::getElementHierarchy() const {
+ String result;
+ foreach(const String& element, elementStack_) {
+ result += "/" + element;
+ }
+ return result;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/VCardParser.h b/Swiften/Parser/PayloadParsers/VCardParser.h
new file mode 100644
index 0000000..c1ed46b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/VCardParser.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "Swiften/Elements/VCard.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class SerializingParser;
+
+ class VCardParser : public GenericPayloadParser<VCard> {
+ public:
+ VCardParser();
+
+ 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:
+ String getElementHierarchy() const;
+
+ private:
+ std::vector<String> elementStack_;
+ String currentText_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/VCardParserFactory.h b/Swiften/Parser/PayloadParsers/VCardParserFactory.h
new file mode 100644
index 0000000..2d5302b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/VCardParserFactory.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/VCardParser.h"
+
+namespace Swift {
+ class VCardParserFactory : public GenericPayloadParserFactory<VCardParser> {
+ public:
+ VCardParserFactory() : GenericPayloadParserFactory<VCardParser>("vCard", "vcard-temp") {}
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp b/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp
new file mode 100644
index 0000000..e195eb7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp
@@ -0,0 +1,26 @@
+#include "Swiften/Parser/PayloadParsers/VCardUpdateParser.h"
+
+namespace Swift {
+
+VCardUpdateParser::VCardUpdateParser() : level_(TopLevel) {
+}
+
+void VCardUpdateParser::handleStartElement(const String&, const String&, const AttributeMap&) {
+ if (level_ == PayloadLevel) {
+ currentText_ = "";
+ }
+ ++level_;
+}
+
+void VCardUpdateParser::handleEndElement(const String& element, const String&) {
+ --level_;
+ if (level_ == PayloadLevel && element == "photo") {
+ getPayloadInternal()->setPhotoHash(currentText_);
+ }
+}
+
+void VCardUpdateParser::handleCharacterData(const String& text) {
+ currentText_ += text;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/VCardUpdateParser.h b/Swiften/Parser/PayloadParsers/VCardUpdateParser.h
new file mode 100644
index 0000000..3682ccf
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/VCardUpdateParser.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/Elements/VCardUpdate.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class SerializingParser;
+
+ class VCardUpdateParser : public GenericPayloadParser<VCardUpdate> {
+ public:
+ VCardUpdateParser();
+
+ 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_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h b/Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h
new file mode 100644
index 0000000..0a8dddf
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/VCardUpdateParser.h"
+
+namespace Swift {
+ class VCardUpdateParserFactory : public GenericPayloadParserFactory<VCardUpdateParser> {
+ public:
+ VCardUpdateParserFactory() : GenericPayloadParserFactory<VCardUpdateParser>("x", "vcard-temp:x:update") {}
+ };
+}
diff --git a/Swiften/Parser/PlatformXMLParserFactory.cpp b/Swiften/Parser/PlatformXMLParserFactory.cpp
new file mode 100644
index 0000000..0624a2d
--- /dev/null
+++ b/Swiften/Parser/PlatformXMLParserFactory.cpp
@@ -0,0 +1,25 @@
+#include "Swiften/Parser/PlatformXMLParserFactory.h"
+
+#include <cassert>
+
+#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/SConscript b/Swiften/Parser/SConscript
new file mode 100644
index 0000000..7d93d8b
--- /dev/null
+++ b/Swiften/Parser/SConscript
@@ -0,0 +1,58 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env["BOOST_FLAGS"])
+myenv.MergeFlags(swiften_env.get("LIBXML_FLAGS", ""))
+myenv.MergeFlags(swiften_env.get("EXPAT_FLAGS", ""))
+
+sources = [
+ "AuthRequestParser.cpp",
+ "AuthChallengeParser.cpp",
+ "AuthSuccessParser.cpp",
+ "AuthResponseParser.cpp",
+ "CompressParser.cpp",
+ "ElementParser.cpp",
+ "IQParser.cpp",
+ "MessageParser.cpp",
+ "PayloadParser.cpp",
+ "PayloadParserFactory.cpp",
+ "PayloadParserFactoryCollection.cpp",
+ "PayloadParsers/BodyParser.cpp",
+ "PayloadParsers/ChatStateParser.cpp",
+ "PayloadParsers/DiscoInfoParser.cpp",
+ "PayloadParsers/ErrorParser.cpp",
+ "PayloadParsers/FullPayloadParserFactoryCollection.cpp",
+ "PayloadParsers/PriorityParser.cpp",
+ "PayloadParsers/PrivateStorageParser.cpp",
+ "PayloadParsers/RawXMLPayloadParser.cpp",
+ "PayloadParsers/ResourceBindParser.cpp",
+ "PayloadParsers/RosterParser.cpp",
+ "PayloadParsers/SecurityLabelParser.cpp",
+ "PayloadParsers/SecurityLabelsCatalogParser.cpp",
+ "PayloadParsers/SoftwareVersionParser.cpp",
+ "PayloadParsers/StorageParser.cpp",
+ "PayloadParsers/StatusParser.cpp",
+ "PayloadParsers/StatusShowParser.cpp",
+ "PayloadParsers/VCardParser.cpp",
+ "PayloadParsers/VCardUpdateParser.cpp",
+ "PlatformXMLParserFactory.cpp",
+ "PresenceParser.cpp",
+ "SerializingParser.cpp",
+ "StanzaParser.cpp",
+ "StreamFeaturesParser.cpp",
+ "XMLParser.cpp",
+ "XMLParserClient.cpp",
+ "XMLParserFactory.cpp",
+ "XMPPParser.cpp",
+ "XMPPParserClient.cpp",
+ ]
+
+if myenv.get("HAVE_EXPAT", 0) :
+ myenv.Append(CPPDEFINES = "HAVE_EXPAT")
+ sources += ["ExpatParser.cpp"]
+if myenv.get("HAVE_LIBXML", 0) :
+ myenv.Append(CPPDEFINES = "HAVE_LIBXML")
+ sources += ["LibXMLParser.cpp"]
+
+objects = myenv.StaticObject(sources)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
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/AttributeMapTest.cpp b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
new file mode 100644
index 0000000..17bda95
--- /dev/null
+++ b/Swiften/Parser/UnitTest/AttributeMapTest.cpp
@@ -0,0 +1,71 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/AttributeMap.h"
+
+using namespace Swift;
+
+class AttributeMapTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(AttributeMapTest);
+ CPPUNIT_TEST(testGetBoolAttribute_True);
+ CPPUNIT_TEST(testGetBoolAttribute_1);
+ CPPUNIT_TEST(testGetBoolAttribute_False);
+ CPPUNIT_TEST(testGetBoolAttribute_0);
+ CPPUNIT_TEST(testGetBoolAttribute_Invalid);
+ CPPUNIT_TEST(testGetBoolAttribute_UnknownWithDefaultTrue);
+ CPPUNIT_TEST(testGetBoolAttribute_UnknownWithDefaultFalse);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ AttributeMapTest() {}
+
+ void testGetBoolAttribute_True() {
+ AttributeMap testling;
+ testling["foo"] = "true";
+
+ CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));
+ }
+
+ void testGetBoolAttribute_1() {
+ AttributeMap testling;
+ testling["foo"] = "1";
+
+ CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));
+ }
+
+ void testGetBoolAttribute_False() {
+ AttributeMap testling;
+ testling["foo"] = "false";
+
+ CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
+ }
+
+ void testGetBoolAttribute_0() {
+ AttributeMap testling;
+ testling["foo"] = "0";
+
+ CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
+ }
+
+ void testGetBoolAttribute_Invalid() {
+ AttributeMap testling;
+ testling["foo"] = "bla";
+
+ CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));
+ }
+
+ void testGetBoolAttribute_UnknownWithDefaultTrue() {
+ AttributeMap testling;
+
+ CPPUNIT_ASSERT(testling.getBoolAttribute("foo", true));
+ }
+
+ void testGetBoolAttribute_UnknownWithDefaultFalse() {
+ AttributeMap testling;
+
+ CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", false));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(AttributeMapTest);
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/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/PayloadParserFactoryCollectionTest.cpp b/Swiften/Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp
new file mode 100644
index 0000000..bfd2c2c
--- /dev/null
+++ b/Swiften/Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp
@@ -0,0 +1,97 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Parser/PayloadParserFactoryCollection.h"
+#include "Swiften/Parser/PayloadParserFactory.h"
+
+using namespace Swift;
+
+class PayloadParserFactoryCollectionTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PayloadParserFactoryCollectionTest);
+ CPPUNIT_TEST(testGetPayloadParserFactory);
+ CPPUNIT_TEST(testGetPayloadParserFactory_NoMatchingFactory);
+ CPPUNIT_TEST(testGetPayloadParserFactory_TwoMatchingFactories);
+ CPPUNIT_TEST(testGetPayloadParserFactory_MatchWithDefaultFactory);
+ CPPUNIT_TEST(testGetPayloadParserFactory_NoMatchWithDefaultFactory);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PayloadParserFactoryCollectionTest() {}
+
+ void setUp() {
+ }
+
+ void tearDown() {
+ }
+
+ void testGetPayloadParserFactory() {
+ PayloadParserFactoryCollection testling;
+ DummyFactory factory1("foo");
+ testling.addFactory(&factory1);
+ DummyFactory factory2("bar");
+ testling.addFactory(&factory2);
+ DummyFactory factory3("baz");
+ testling.addFactory(&factory3);
+
+ PayloadParserFactory* factory = testling.getPayloadParserFactory("bar", "", AttributeMap());
+
+ CPPUNIT_ASSERT(factory == &factory2);
+ }
+
+ void testGetPayloadParserFactory_NoMatchingFactory() {
+ PayloadParserFactoryCollection testling;
+ DummyFactory factory("foo");
+
+ CPPUNIT_ASSERT(!testling.getPayloadParserFactory("bar", "", AttributeMap()));
+ }
+
+ void testGetPayloadParserFactory_TwoMatchingFactories() {
+ PayloadParserFactoryCollection testling;
+ DummyFactory factory1("foo");
+ testling.addFactory(&factory1);
+ DummyFactory factory2("foo");
+ testling.addFactory(&factory2);
+
+ PayloadParserFactory* factory = testling.getPayloadParserFactory("foo", "", AttributeMap());
+
+ CPPUNIT_ASSERT(factory == &factory2);
+ }
+
+ void testGetPayloadParserFactory_MatchWithDefaultFactory() {
+ PayloadParserFactoryCollection testling;
+ DummyFactory factory1("foo");
+ testling.addFactory(&factory1);
+ DummyFactory factory2;
+ testling.setDefaultFactory(&factory2);
+
+ PayloadParserFactory* factory = testling.getPayloadParserFactory("foo", "", AttributeMap());
+
+ CPPUNIT_ASSERT(factory == &factory1);
+ }
+
+ void testGetPayloadParserFactory_NoMatchWithDefaultFactory() {
+ PayloadParserFactoryCollection testling;
+ DummyFactory factory1("foo");
+ testling.addFactory(&factory1);
+ DummyFactory factory2;
+ testling.setDefaultFactory(&factory2);
+
+ PayloadParserFactory* factory = testling.getPayloadParserFactory("baz", "", AttributeMap());
+
+ CPPUNIT_ASSERT(factory == &factory2);
+ }
+
+
+ private:
+ struct DummyFactory : public PayloadParserFactory {
+ DummyFactory(const String& element = "") : element(element) {}
+ virtual bool canParse(const String& e, const String&, const AttributeMap&) const {
+ return element.isEmpty() ? true : element == e;
+ }
+ virtual PayloadParser* createPayloadParser() { return NULL; }
+ String element;
+ };
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PayloadParserFactoryCollectionTest);
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..6ef1899
--- /dev/null
+++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp
@@ -0,0 +1,229 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+
+#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_ElementInNamespacedElement);
+ 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:
+ 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(String(), client_.events[0].ns);
+
+ 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>(0), client_.events[1].attributes.size());
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[1].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(String("query"), client_.events[2].data);
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[2].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(String("iq"), client_.events[3].data);
+ CPPUNIT_ASSERT_EQUAL(String(), client_.events[3].ns);
+ }
+
+ void testParse_ElementInNamespacedElement() {
+ ParserType testling(&client_);
+
+ CPPUNIT_ASSERT(testling.parse(
+ "<query xmlns='jabber:iq:version'>"
+ "<name>Swift</name>"
+ "</query>"));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), client_.events.size());
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("query"), client_.events[0].data);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].attributes.size());
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[0].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("name"), client_.events[1].data);
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[1].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::CharacterData, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(String("Swift"), client_.events[2].data);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(String("name"), client_.events[3].data);
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[3].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[4].type);
+ CPPUNIT_ASSERT_EQUAL(String("query"), client_.events[4].data);
+ CPPUNIT_ASSERT_EQUAL(String("jabber:iq:version"), client_.events[4].ns);
+ }
+
+ 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("x"), client_.events[0].data);
+ CPPUNIT_ASSERT_EQUAL(String("bla"), client_.events[0].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);
+ CPPUNIT_ASSERT_EQUAL(String("y"), client_.events[1].data);
+ CPPUNIT_ASSERT_EQUAL(String("bla"), client_.events[1].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);
+ CPPUNIT_ASSERT_EQUAL(String("y"), client_.events[2].data);
+ CPPUNIT_ASSERT_EQUAL(String("bla"), client_.events[2].ns);
+
+ CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[3].type);
+ CPPUNIT_ASSERT_EQUAL(String("x"), client_.events[3].data);
+ CPPUNIT_ASSERT_EQUAL(String("bla"), client_.events[3].ns);
+ }
+
+ 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 String& ns,
+ const AttributeMap& attributes)
+ : type(type), data(data), ns(ns), attributes(attributes) {}
+ Event(Type type, const String& data, const String& ns = String())
+ : type(type), data(data), ns(ns) {}
+
+ Type type;
+ String data;
+ String ns;
+ AttributeMap attributes;
+ };
+
+ Client() {}
+
+ virtual void handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
+ events.push_back(Event(StartElement, element, ns, attributes));
+ }
+
+ virtual void handleEndElement(const String& element, const String& ns) {
+ events.push_back(Event(EndElement, element, ns));
+ }
+
+ 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..ecdd565
--- /dev/null
+++ b/Swiften/Parser/UnitTest/XMPPParserTest.cpp
@@ -0,0 +1,186 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#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_SimpleClientFromServerSession);
+ 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(String("example.com"), client_.events[0].to);
+ 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_SimpleClientFromServerSession() {
+ XMPPParser testling(&client_, &factories_);
+
+ CPPUNIT_ASSERT(testling.parse("<?xml version='1.0'?>"));
+ CPPUNIT_ASSERT(testling.parse("<stream:stream from='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='aeab'>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(client_.events.size()));
+ CPPUNIT_ASSERT_EQUAL(Client::StreamStart, client_.events[0].type);
+ CPPUNIT_ASSERT_EQUAL(String("example.com"), client_.events[0].from);
+ CPPUNIT_ASSERT_EQUAL(String("aeab"), client_.events[0].id);
+ }
+
+
+ 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, const String& from, const String& to, const String& id) : type(type), from(from), to(to), id(id) {}
+
+ Event(Type type) : type(type) {}
+
+ Type type;
+ String from;
+ String to;
+ String id;
+ boost::shared_ptr<Element> element;
+ };
+
+ Client() {}
+
+ void handleStreamStart(const ProtocolHeader& header) {
+ events.push_back(Event(StreamStart, header.getFrom(), header.getTo(), header.getID()));
+ }
+
+ 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..83de263
--- /dev/null
+++ b/Swiften/Parser/XMPPParser.cpp
@@ -0,0 +1,159 @@
+#include "Swiften/Parser/XMPPParser.h"
+
+#include <iostream>
+#include <cassert>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#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/AuthChallengeParser.h"
+#include "Swiften/Parser/AuthResponseParser.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") {
+ ProtocolHeader header;
+ header.setFrom(attributes.getAttribute("from"));
+ header.setTo(attributes.getAttribute("to"));
+ header.setID(attributes.getAttribute("id"));
+ header.setVersion(attributes.getAttribute("version"));
+ client_->handleStreamStart(header);
+ }
+ 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 == "challenge" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
+ return new AuthChallengeParser();
+ }
+ else if (element == "response" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
+ return new AuthResponseParser();
+ }
+ 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..b8f8e46
--- /dev/null
+++ b/Swiften/Parser/XMPPParserClient.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class String;
+ class ProtocolHeader;
+
+ class XMPPParserClient {
+ public:
+ virtual ~XMPPParserClient();
+
+ virtual void handleStreamStart(const ProtocolHeader&) = 0;
+ virtual void handleElement(boost::shared_ptr<Element>) = 0;
+ virtual void handleStreamEnd() = 0;
+ };
+}
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
new file mode 100644
index 0000000..988fc10
--- /dev/null
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -0,0 +1,58 @@
+#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_ = stanzaChannel;
+ stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
+}
+
+void PresenceOracle::cancelSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Unsubscribed);
+ stanza->setTo(jid);
+ stanzaChannel_->sendPresence(stanza);
+}
+
+void PresenceOracle::confirmSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Subscribed);
+ stanza->setTo(jid);
+ stanzaChannel_->sendPresence(stanza);
+}
+
+
+void PresenceOracle::requestSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Subscribe);
+ stanza->setTo(jid);
+ stanzaChannel_->sendPresence(stanza);
+}
+
+
+void PresenceOracle::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
+ JID bareJID = JID(presence->getFrom().toBare());
+
+ if (presence->getType() == Presence::Subscribe) {
+ onPresenceSubscriptionRequest(bareJID, presence->getStatus());
+ } else {
+ 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..dc6fe5d
--- /dev/null
+++ b/Swiften/Presence/PresenceOracle.h
@@ -0,0 +1,30 @@
+#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() {};
+
+ void cancelSubscription(const JID& jid);
+ void confirmSubscription(const JID& jid);
+ void requestSubscription(const JID& jid);
+
+ boost::signal<void (boost::shared_ptr<Presence>, boost::shared_ptr<Presence>)> onPresenceChange;
+ boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
+
+ 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/PresenceSender.cpp b/Swiften/Presence/PresenceSender.cpp
new file mode 100644
index 0000000..8e7ef68
--- /dev/null
+++ b/Swiften/Presence/PresenceSender.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+PresenceSender::PresenceSender(StanzaChannel* channel) : channel(channel) {
+}
+
+void PresenceSender::sendPresence(boost::shared_ptr<Presence> presence) {
+ if (!channel->isAvailable()) {
+ return;
+ }
+
+ channel->sendPresence(presence);
+
+ if (!presence->getTo().isValid()) {
+ boost::shared_ptr<Presence> presenceCopy(new Presence(*presence));
+ foreach(const JID& jid, directedPresenceReceivers) {
+ presenceCopy->setTo(jid);
+ channel->sendPresence(presenceCopy);
+ }
+
+ lastSentUndirectedPresence = presence;
+ }
+}
+
+void PresenceSender::addDirectedPresenceReceiver(const JID& jid) {
+ directedPresenceReceivers.insert(jid);
+ if (channel->isAvailable()) {
+ if (lastSentUndirectedPresence && lastSentUndirectedPresence->getType() == Presence::Available) {
+ boost::shared_ptr<Presence> presenceCopy(new Presence(*lastSentUndirectedPresence));
+ presenceCopy->setTo(jid);
+ channel->sendPresence(presenceCopy);
+ }
+ }
+}
+
+void PresenceSender::removeDirectedPresenceReceiver(const JID& jid) {
+ directedPresenceReceivers.erase(jid);
+ if (channel->isAvailable()) {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->setType(Presence::Unavailable);
+ presence->setTo(jid);
+ channel->sendPresence(presence);
+ }
+}
+
+}
diff --git a/Swiften/Presence/PresenceSender.h b/Swiften/Presence/PresenceSender.h
new file mode 100644
index 0000000..ef69447
--- /dev/null
+++ b/Swiften/Presence/PresenceSender.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <set>
+
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+ class StanzaChannel;
+
+ class PresenceSender {
+ public:
+ PresenceSender(StanzaChannel*);
+
+ void addDirectedPresenceReceiver(const JID&);
+ void removeDirectedPresenceReceiver(const JID&);
+
+ void sendPresence(boost::shared_ptr<Presence>);
+
+ private:
+ boost::shared_ptr<Presence> lastSentUndirectedPresence;
+ StanzaChannel* channel;
+ std::set<JID> directedPresenceReceivers;
+ };
+}
diff --git a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
new file mode 100644
index 0000000..2c9c526
--- /dev/null
+++ b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
@@ -0,0 +1,126 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+
+using namespace Swift;
+
+class PresencePointerPair {
+ public:
+ boost::shared_ptr<Presence> one;
+ boost::shared_ptr<Presence> two;
+};
+
+class SubscriptionRequestInfo {
+ public:
+ boost::optional<JID> jid;
+ boost::optional<String> reason;
+};
+
+class PresenceOracleTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PresenceOracleTest);
+ CPPUNIT_TEST(testFirstPresence);
+ CPPUNIT_TEST(testSecondPresence);
+ CPPUNIT_TEST(testSubscriptionRequest);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ PresenceOracle* oracle_;
+ DummyStanzaChannel* stanzaChannel_;
+
+ public:
+
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> lastPresence, PresencePointerPair* out) {
+ CPPUNIT_ASSERT(out->one.get() == NULL);
+ CPPUNIT_ASSERT(out->two.get() == NULL);
+ out->one = newPresence;
+ out->two = lastPresence;
+ CPPUNIT_ASSERT(newPresence.get());
+ CPPUNIT_ASSERT_EQUAL(newPresence, out->one);
+ }
+
+ void handlePresenceSubscriptionRequest(const JID& jid, const String& reason, SubscriptionRequestInfo* info) {
+ CPPUNIT_ASSERT(!info->jid);
+ CPPUNIT_ASSERT(!info->reason);
+ info->jid = jid;
+ info->reason = reason;
+ }
+
+ void setUp() {
+ stanzaChannel_ = new DummyStanzaChannel();
+ oracle_ = new PresenceOracle(stanzaChannel_);
+ }
+
+ void tearDown() {
+ delete oracle_;
+ delete stanzaChannel_;
+ }
+
+ void testFirstPresence() {
+ PresencePointerPair out;
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+
+ SubscriptionRequestInfo info;
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+ boost::shared_ptr<Presence> sentPresence(new Presence("blarb"));
+ stanzaChannel_->onPresenceReceived(sentPresence);
+
+ CPPUNIT_ASSERT(!info.jid);
+ CPPUNIT_ASSERT(!info.reason);
+ CPPUNIT_ASSERT(out.two.get() == NULL);
+ CPPUNIT_ASSERT_EQUAL(sentPresence, out.one);
+ }
+
+ void testSecondPresence() {
+ PresencePointerPair out;
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+
+ boost::shared_ptr<Presence> sentPresence1(new Presence("blarb"));
+ stanzaChannel_->onPresenceReceived(sentPresence1);
+ CPPUNIT_ASSERT_EQUAL(sentPresence1, out.one);
+ out.one = boost::shared_ptr<Presence>();
+
+ SubscriptionRequestInfo info;
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+ boost::shared_ptr<Presence> sentPresence2(new Presence("test2"));
+ stanzaChannel_->onPresenceReceived(sentPresence2);
+
+ CPPUNIT_ASSERT(!info.jid);
+ CPPUNIT_ASSERT(!info.reason);
+ CPPUNIT_ASSERT_EQUAL(sentPresence1, out.two);
+ CPPUNIT_ASSERT_EQUAL(sentPresence2, out.one);
+ }
+
+ void testSubscriptionRequest() {
+ PresencePointerPair out;
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+ SubscriptionRequestInfo info;
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+ String reasonText = "Because I want to";
+ JID sentJID = JID("me@example.com");
+
+ boost::shared_ptr<Presence> sentPresence(new Presence());
+ sentPresence->setType(Presence::Subscribe);
+ sentPresence->setFrom(sentJID);
+ sentPresence->setStatus(reasonText);
+ stanzaChannel_->onPresenceReceived(sentPresence);
+
+ CPPUNIT_ASSERT(info.jid);
+ CPPUNIT_ASSERT(info.reason);
+ CPPUNIT_ASSERT_EQUAL(sentJID, info.jid.get());
+ CPPUNIT_ASSERT_EQUAL(reasonText, info.reason.get());
+
+ CPPUNIT_ASSERT(!out.two);
+ CPPUNIT_ASSERT(!out.one);
+ }
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(PresenceOracleTest);
+
diff --git a/Swiften/Presence/UnitTest/PresenceSenderTest.cpp b/Swiften/Presence/UnitTest/PresenceSenderTest.cpp
new file mode 100644
index 0000000..1df0269
--- /dev/null
+++ b/Swiften/Presence/UnitTest/PresenceSenderTest.cpp
@@ -0,0 +1,120 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Client/DummyStanzaChannel.h"
+#include "Swiften/Presence/PresenceSender.h"
+
+using namespace Swift;
+
+class PresenceSenderTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PresenceSenderTest);
+ CPPUNIT_TEST(testSendPresence);
+ CPPUNIT_TEST(testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers);
+ CPPUNIT_TEST(testSendPresence_DirectedPresenceWithDirectedPresenceReceivers);
+ CPPUNIT_TEST(testAddDirectedPresenceReceiver);
+ CPPUNIT_TEST(testAddDirectedPresenceReceiver_AfterSendingDirectedPresence);
+ CPPUNIT_TEST(testRemoveDirectedPresenceReceiver);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ channel = new DummyStanzaChannel();
+ testPresence = boost::shared_ptr<Presence>(new Presence());
+ testPresence->setStatus("Foo");
+ secondTestPresence = boost::shared_ptr<Presence>(new Presence());
+ secondTestPresence->setStatus("Bar");
+ }
+
+ void tearDown() {
+ delete channel;
+ }
+
+ void testSendPresence() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT(testPresence == presence);
+ }
+
+ void testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT(testPresence == presence);
+ presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[1]);
+ CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+ CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+ }
+
+ void testSendPresence_DirectedPresenceWithDirectedPresenceReceivers() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+ channel->sentStanzas.clear();
+
+ testPresence->setTo(JID("foo@bar.com"));
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT(testPresence == presence);
+ }
+
+ void testAddDirectedPresenceReceiver() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->sendPresence(testPresence);
+ channel->sentStanzas.clear();
+
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+ CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+ }
+
+ void testAddDirectedPresenceReceiver_AfterSendingDirectedPresence() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->sendPresence(testPresence);
+ secondTestPresence->setTo(JID("foo@bar.com"));
+ testling->sendPresence(secondTestPresence);
+ channel->sentStanzas.clear();
+
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+ boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+ CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+ CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+ }
+
+ void testRemoveDirectedPresenceReceiver() {
+ std::auto_ptr<PresenceSender> testling(createPresenceSender());
+ testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+ channel->sentStanzas.clear();
+
+ testling->removeDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+ testling->sendPresence(testPresence);
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0])->getType(), Presence::Unavailable);
+ CPPUNIT_ASSERT(channel->sentStanzas[1] == testPresence);
+ }
+
+ private:
+ PresenceSender* createPresenceSender() {
+ return new PresenceSender(channel);
+ }
+
+ private:
+ DummyStanzaChannel* channel;
+ boost::shared_ptr<Presence> testPresence;
+ boost::shared_ptr<Presence> secondTestPresence;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PresenceSenderTest);
diff --git a/Swiften/QA/ClientTest/.gitignore b/Swiften/QA/ClientTest/.gitignore
new file mode 100644
index 0000000..9fb3e67
--- /dev/null
+++ b/Swiften/QA/ClientTest/.gitignore
@@ -0,0 +1 @@
+ClientTest
diff --git a/Swiften/QA/ClientTest/ClientTest.cpp b/Swiften/QA/ClientTest/ClientTest.cpp
new file mode 100644
index 0000000..74ec908
--- /dev/null
+++ b/Swiften/QA/ClientTest/ClientTest.cpp
@@ -0,0 +1,68 @@
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/Network/BoostTimer.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+#include "Swiften/Client/ClientXMLTracer.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/Network/MainBoostIOServiceThread.h"
+
+using namespace Swift;
+
+SimpleEventLoop eventLoop;
+
+Client* client = 0;
+bool reconnected = false;
+bool rosterReceived = false;
+
+void handleRosterReceived(boost::shared_ptr<Payload>) {
+ if (reconnected) {
+ rosterReceived = true;
+ client->disconnect();
+ eventLoop.stop();
+ }
+ else {
+ reconnected = true;
+ client->disconnect();
+ client->connect();
+ }
+}
+
+void handleConnected() {
+ boost::shared_ptr<GetRosterRequest> rosterRequest(new GetRosterRequest(client));
+ 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));
+ ClientXMLTracer* tracer = new ClientXMLTracer(client);
+ client->onConnected.connect(&handleConnected);
+ client->connect();
+
+ {
+ boost::shared_ptr<BoostTimer> timer(new BoostTimer(30000, &MainBoostIOServiceThread::getInstance().getIOService()));
+ timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
+ timer->start();
+
+ eventLoop.run();
+ }
+
+ delete tracer;
+ delete client;
+ return !rosterReceived;
+}
diff --git a/Swiften/QA/ClientTest/SConscript b/Swiften/QA/ClientTest/SConscript
new file mode 100644
index 0000000..bc9cf95
--- /dev/null
+++ b/Swiften/QA/ClientTest/SConscript
@@ -0,0 +1,22 @@
+import os
+
+Import("env")
+
+if env["TEST"] :
+ myenv = env.Clone()
+ myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
+ myenv.MergeFlags(myenv["CPPUNIT_FLAGS"])
+ myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
+ myenv.MergeFlags(myenv["BOOST_FLAGS"])
+ myenv.MergeFlags(myenv.get("SQLITE_FLAGS", ""))
+ myenv.MergeFlags(myenv["ZLIB_FLAGS"])
+ myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
+ myenv.MergeFlags(myenv.get("LIBXML_FLAGS", ""))
+ myenv.MergeFlags(myenv.get("EXPAT_FLAGS", ""))
+
+ for i in ["SWIFT_CLIENTTEST_JID", "SWIFT_CLIENTTEST_PASS"]:
+ if os.environ.get(i, "") :
+ myenv["ENV"][i] = os.environ[i]
+
+ tester = myenv.Program("ClientTest", ["ClientTest.cpp"])
+ myenv.Test(tester, "system")
diff --git a/Swiften/QA/NetworkTest/.gitignore b/Swiften/QA/NetworkTest/.gitignore
new file mode 100644
index 0000000..5a3caca
--- /dev/null
+++ b/Swiften/QA/NetworkTest/.gitignore
@@ -0,0 +1 @@
+NetworkTest
diff --git a/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp b/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp
new file mode 100644
index 0000000..926c9ae
--- /dev/null
+++ b/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp
@@ -0,0 +1,75 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/BoostConnectionServer.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+using namespace Swift;
+
+class BoostConnectionServerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(BoostConnectionServerTest);
+ CPPUNIT_TEST(testConstructor_TwoServersOnSamePort);
+ CPPUNIT_TEST(testStart_Conflict);
+ CPPUNIT_TEST(testStop);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ boostIOServiceThread_ = new BoostIOServiceThread();
+ eventLoop_ = new DummyEventLoop();
+ stopped = false;
+ stoppedError.reset();
+ }
+
+ void tearDown() {
+ while (eventLoop_->hasEvents()) {
+ eventLoop_->processEvents();
+ }
+ delete eventLoop_;
+ delete boostIOServiceThread_;
+ }
+
+ void testConstructor_TwoServersOnSamePort() {
+ boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+ boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+ }
+
+ void testStart_Conflict() {
+ boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+ testling->start();
+
+ boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+ testling2->onStopped.connect(
+ boost::bind(&BoostConnectionServerTest::handleStopped, this, _1));
+
+ testling->stop();
+ }
+
+ void testStop() {
+ boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+ testling->start();
+
+ testling->stop();
+
+ boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+ testling2->start();
+
+ testling2->stop();
+ }
+
+ void handleStopped(boost::optional<BoostConnectionServer::Error> e) {
+ stopped = true;
+ stoppedError = e;
+ }
+
+ private:
+ BoostIOServiceThread* boostIOServiceThread_;
+ DummyEventLoop* eventLoop_;
+ bool stopped;
+ boost::optional<BoostConnectionServer::Error> stoppedError;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BoostConnectionServerTest);
diff --git a/Swiften/QA/NetworkTest/BoostConnectionTest.cpp b/Swiften/QA/NetworkTest/BoostConnectionTest.cpp
new file mode 100644
index 0000000..af4e003
--- /dev/null
+++ b/Swiften/QA/NetworkTest/BoostConnectionTest.cpp
@@ -0,0 +1,88 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/sleep.h"
+#include "Swiften/Network/BoostConnection.h"
+#include "Swiften/Network/HostAddress.h"
+#include "Swiften/Network/HostAddressPort.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+const unsigned char* address = reinterpret_cast<const unsigned char*>("\x41\x63\xde\x89");
+
+using namespace Swift;
+
+class BoostConnectionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(BoostConnectionTest);
+ CPPUNIT_TEST(testDestructor);
+ CPPUNIT_TEST(testDestructor_PendingEvents);
+ CPPUNIT_TEST(testWrite);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ BoostConnectionTest() {}
+
+ void setUp() {
+ boostIOServiceThread_ = new BoostIOServiceThread();
+ eventLoop_ = new DummyEventLoop();
+ disconnected = false;
+ }
+
+ void tearDown() {
+ delete eventLoop_;
+ delete boostIOServiceThread_;
+ }
+
+ void testDestructor() {
+ {
+ boost::shared_ptr<BoostConnection> testling(new BoostConnection(&boostIOServiceThread_->getIOService()));
+ testling->connect(HostAddressPort(HostAddress(address, 4), 5222));
+ }
+ }
+
+ void testDestructor_PendingEvents() {
+ {
+ boost::shared_ptr<BoostConnection> testling(new BoostConnection(&boostIOServiceThread_->getIOService()));
+ testling->connect(HostAddressPort(HostAddress(address, 4), 5222));
+ while (!eventLoop_->hasEvents()) {
+ Swift::sleep(10);
+ }
+ }
+ eventLoop_->processEvents();
+ }
+
+ void testWrite() {
+ boost::shared_ptr<BoostConnection> testling(new BoostConnection(&boostIOServiceThread_->getIOService()));
+ testling->onConnectFinished.connect(boost::bind(&BoostConnectionTest::doWrite, this, testling.get()));
+ testling->onDataRead.connect(boost::bind(&BoostConnectionTest::handleDataRead, this, _1));
+ testling->onDisconnected.connect(boost::bind(&BoostConnectionTest::handleDisconnected, this));
+ testling->connect(HostAddressPort(HostAddress("65.99.222.137"), 5222));
+ while (receivedData.isEmpty()) {
+ Swift::sleep(10);
+ eventLoop_->processEvents();
+ }
+ testling->disconnect();
+ }
+
+ void doWrite(BoostConnection* connection) {
+ connection->write(ByteArray("<stream:stream>"));
+ }
+
+ void handleDataRead(const ByteArray& data) {
+ receivedData += data;
+ }
+
+ void handleDisconnected() {
+ disconnected = true;
+ }
+
+ private:
+ BoostIOServiceThread* boostIOServiceThread_;
+ DummyEventLoop* eventLoop_;
+ ByteArray receivedData;
+ bool disconnected;
+};
+
+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..09837d6
--- /dev/null
+++ b/Swiften/QA/NetworkTest/DomainNameResolverTest.cpp
@@ -0,0 +1,168 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/sleep.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Network/PlatformDomainNameResolver.h"
+#include "Swiften/Network/DomainNameAddressQuery.h"
+#include "Swiften/Network/DomainNameServiceQuery.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+using namespace Swift;
+
+class DomainNameResolverTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(DomainNameResolverTest);
+ CPPUNIT_TEST(testResolveAddress);
+ CPPUNIT_TEST(testResolveAddress_Error);
+ //CPPUNIT_TEST(testResolveAddress_IPv6);
+ CPPUNIT_TEST(testResolveAddress_International);
+ CPPUNIT_TEST(testResolveAddress_Localhost);
+ CPPUNIT_TEST(testResolveService);
+ CPPUNIT_TEST(testResolveService_Error);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ DomainNameResolverTest() {}
+
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ resolver = new PlatformDomainNameResolver();
+ resultsAvailable = false;
+ }
+
+ void tearDown() {
+ delete resolver;
+ delete eventLoop;
+ }
+
+ void testResolveAddress() {
+ boost::shared_ptr<DomainNameAddressQuery> query(createAddressQuery("xmpp.test.swift.im"));
+
+ query->run();
+ waitForResults();
+
+ CPPUNIT_ASSERT(!addressQueryError);
+ CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.0"), addressQueryResult.toString());
+ }
+
+ void testResolveAddress_Error() {
+ boost::shared_ptr<DomainNameAddressQuery> query(createAddressQuery("invalid.test.swift.im"));
+
+ query->run();
+ waitForResults();
+
+ CPPUNIT_ASSERT(addressQueryError);
+ }
+
+ void testResolveAddress_IPv6() {
+ boost::shared_ptr<DomainNameAddressQuery> query(createAddressQuery("xmpp-ipv6.test.swift.im"));
+
+ query->run();
+ waitForResults();
+
+ CPPUNIT_ASSERT(!addressQueryError);
+ CPPUNIT_ASSERT_EQUAL(std::string("0000:0000:0000:0000:0000:ffff:0a00:0104"), addressQueryResult.toString());
+ }
+
+ void testResolveAddress_International() {
+ boost::shared_ptr<DomainNameAddressQuery> query(createAddressQuery("tron\xc3\xa7on.test.swift.im"));
+
+ query->run();
+ waitForResults();
+
+ CPPUNIT_ASSERT(!addressQueryError);
+ CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.3"), addressQueryResult.toString());
+ }
+
+ void testResolveAddress_Localhost() {
+ boost::shared_ptr<DomainNameAddressQuery> query(createAddressQuery("localhost"));
+
+ query->run();
+ waitForResults();
+
+ CPPUNIT_ASSERT(!addressQueryError);
+ CPPUNIT_ASSERT_EQUAL(std::string("127.0.0.1"), addressQueryResult.toString());
+ }
+
+
+ void testResolveService() {
+ boost::shared_ptr<DomainNameServiceQuery> query(createServiceQuery("_xmpp-client._tcp.xmpp-srv.test.swift.im"));
+
+ query->run();
+ waitForResults();
+
+ CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(serviceQueryResult.size()));
+ CPPUNIT_ASSERT_EQUAL(String("xmpp1.test.swift.im"), serviceQueryResult[0].hostname);
+ CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[0].port);
+ CPPUNIT_ASSERT_EQUAL(0, serviceQueryResult[0].priority);
+ CPPUNIT_ASSERT_EQUAL(1, serviceQueryResult[0].weight);
+ CPPUNIT_ASSERT_EQUAL(String("xmpp-invalid.test.swift.im"), serviceQueryResult[1].hostname);
+ CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[1].port);
+ CPPUNIT_ASSERT_EQUAL(1, serviceQueryResult[1].priority);
+ CPPUNIT_ASSERT_EQUAL(100, serviceQueryResult[1].weight);
+ CPPUNIT_ASSERT_EQUAL(String("xmpp3.test.swift.im"), serviceQueryResult[2].hostname);
+ CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[2].port);
+ CPPUNIT_ASSERT_EQUAL(3, serviceQueryResult[2].priority);
+ CPPUNIT_ASSERT_EQUAL(100, serviceQueryResult[2].weight);
+ CPPUNIT_ASSERT_EQUAL(String("xmpp2.test.swift.im"), serviceQueryResult[3].hostname);
+ CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[3].port);
+ CPPUNIT_ASSERT_EQUAL(5, serviceQueryResult[3].priority);
+ CPPUNIT_ASSERT_EQUAL(100, serviceQueryResult[3].weight);
+ }
+
+ void testResolveService_Error() {
+ }
+
+/*
+ }
+ */
+
+ private:
+ boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& domain) {
+ boost::shared_ptr<DomainNameAddressQuery> result = resolver->createAddressQuery(domain);
+ result->onResult.connect(boost::bind(&DomainNameResolverTest::handleAddressQueryResult, this, _1, _2));
+ return result;
+ }
+
+ void handleAddressQueryResult(const HostAddress& address, boost::optional<DomainNameResolveError> error) {
+ addressQueryResult = address;
+ addressQueryError = error;
+ resultsAvailable = true;
+ }
+
+ boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& domain) {
+ boost::shared_ptr<DomainNameServiceQuery> result = resolver->createServiceQuery(domain);
+ result->onResult.connect(boost::bind(&DomainNameResolverTest::handleServiceQueryResult, this, _1));
+ return result;
+ }
+
+ void handleServiceQueryResult(const std::vector<DomainNameServiceQuery::Result>& result) {
+ serviceQueryResult = result;
+ resultsAvailable = true;
+ }
+
+ void waitForResults() {
+ eventLoop->processEvents();
+ int ticks = 0;
+ while (!resultsAvailable) {
+ ticks++;
+ if (ticks > 1000) {
+ CPPUNIT_ASSERT(false);
+ }
+ Swift::sleep(10);
+ eventLoop->processEvents();
+ }
+ }
+
+ private:
+ DummyEventLoop* eventLoop;
+ bool resultsAvailable;
+ HostAddress addressQueryResult;
+ boost::optional<DomainNameResolveError> addressQueryError;
+ std::vector<DomainNameServiceQuery::Result> serviceQueryResult;
+ PlatformDomainNameResolver* resolver;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DomainNameResolverTest);
diff --git a/Swiften/QA/NetworkTest/SConscript b/Swiften/QA/NetworkTest/SConscript
new file mode 100644
index 0000000..871df39
--- /dev/null
+++ b/Swiften/QA/NetworkTest/SConscript
@@ -0,0 +1,18 @@
+import os
+
+Import("env")
+
+if env["TEST"] :
+ myenv = env.Clone()
+ myenv.MergeFlags(myenv["CHECKER_FLAGS"])
+ myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
+ myenv.MergeFlags(myenv["CPPUNIT_FLAGS"])
+ myenv.MergeFlags(myenv["BOOST_FLAGS"])
+ myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
+
+ tester = myenv.Program("NetworkTest", [
+ "BoostConnectionServerTest.cpp",
+ "BoostConnectionTest.cpp",
+ "DomainNameResolverTest.cpp",
+ ])
+ myenv.Test(tester, "system")
diff --git a/Swiften/QA/SConscript b/Swiften/QA/SConscript
new file mode 100644
index 0000000..ede7b39
--- /dev/null
+++ b/Swiften/QA/SConscript
@@ -0,0 +1,4 @@
+SConscript([
+ "NetworkTest/SConscript",
+ "ClientTest/SConscript",
+ ])
diff --git a/Swiften/Queries/DummyIQChannel.h b/Swiften/Queries/DummyIQChannel.h
new file mode 100644
index 0000000..c0a4a25
--- /dev/null
+++ b/Swiften/Queries/DummyIQChannel.h
@@ -0,0 +1,29 @@
+#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";
+ }
+
+ virtual bool isAvailable() {
+ return true;
+ }
+
+ 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..77dae52
--- /dev/null
+++ b/Swiften/Queries/GenericRequest.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#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) :
+ Request(type, receiver, payload, router) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<ErrorPayload> error) {
+ onResponse(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(payload), error);
+ }
+
+ public:
+ boost::signal<void (boost::shared_ptr<PAYLOAD_TYPE>, const boost::optional<ErrorPayload>&)> onResponse;
+ };
+}
diff --git a/Swiften/Queries/GetResponder.h b/Swiften/Queries/GetResponder.h
new file mode 100644
index 0000000..2bd2fe2
--- /dev/null
+++ b/Swiften/Queries/GetResponder.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Swiften/Queries/Responder.h"
+
+namespace Swift {
+ template<typename T>
+ class GetResponder : public Responder<T> {
+ public:
+ GetResponder(IQRouter* router) : Responder<T>(router) {}
+
+ private:
+ virtual bool handleSetRequest(const JID&, const String&, boost::shared_ptr<T>) { return false; }
+ };
+}
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..1692afe
--- /dev/null
+++ b/Swiften/Queries/IQChannel.h
@@ -0,0 +1,24 @@
+#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;
+
+ virtual bool isAvailable() = 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..81bd773
--- /dev/null
+++ b/Swiften/Queries/IQHandler.cpp
@@ -0,0 +1,9 @@
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+IQHandler::~IQHandler() {
+}
+
+}
diff --git a/Swiften/Queries/IQHandler.h b/Swiften/Queries/IQHandler.h
new file mode 100644
index 0000000..7389b3a
--- /dev/null
+++ b/Swiften/Queries/IQHandler.h
@@ -0,0 +1,19 @@
+#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:
+ virtual ~IQHandler();
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/IQRouter.cpp b/Swiften/Queries/IQRouter.cpp
new file mode 100644
index 0000000..fdfa00b
--- /dev/null
+++ b/Swiften/Queries/IQRouter.cpp
@@ -0,0 +1,81 @@
+#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/ErrorPayload.h"
+
+namespace Swift {
+
+static void noop(IQHandler*) {}
+
+IQRouter::IQRouter(IQChannel* channel) : channel_(channel), queueRemoves_(false) {
+ channel->onIQReceived.connect(boost::bind(&IQRouter::handleIQ, this, _1));
+}
+
+IQRouter::~IQRouter() {
+}
+
+bool IQRouter::isAvailable() {
+ return channel_->isAvailable();
+}
+
+void IQRouter::handleIQ(boost::shared_ptr<IQ> iq) {
+ queueRemoves_ = true;
+
+ bool handled = false;
+ foreach(boost::shared_ptr<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(), ErrorPayload::FeatureNotImplemented, ErrorPayload::Cancel));
+ }
+
+ processPendingRemoves();
+
+ queueRemoves_ = false;
+}
+
+void IQRouter::processPendingRemoves() {
+ foreach(boost::shared_ptr<IQHandler> handler, queuedRemoves_) {
+ handlers_.erase(std::remove(handlers_.begin(), handlers_.end(), handler), handlers_.end());
+ }
+ queuedRemoves_.clear();
+}
+
+void IQRouter::addHandler(IQHandler* handler) {
+ addHandler(boost::shared_ptr<IQHandler>(handler, noop));
+}
+
+void IQRouter::removeHandler(IQHandler* handler) {
+ removeHandler(boost::shared_ptr<IQHandler>(handler, noop));
+}
+
+void IQRouter::addHandler(boost::shared_ptr<IQHandler> handler) {
+ handlers_.push_back(handler);
+}
+
+void IQRouter::removeHandler(boost::shared_ptr<IQHandler> handler) {
+ if (queueRemoves_) {
+ queuedRemoves_.push_back(handler);
+ }
+ else {
+ 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..717a845
--- /dev/null
+++ b/Swiften/Queries/IQRouter.h
@@ -0,0 +1,41 @@
+#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);
+ ~IQRouter();
+
+ void addHandler(IQHandler* handler);
+ void removeHandler(IQHandler* handler);
+ void addHandler(boost::shared_ptr<IQHandler> handler);
+ void removeHandler(boost::shared_ptr<IQHandler> handler);
+
+ void sendIQ(boost::shared_ptr<IQ> iq);
+ String getNewIQID();
+
+ bool isAvailable();
+
+ private:
+ void handleIQ(boost::shared_ptr<IQ> iq);
+ void processPendingRemoves();
+
+ private:
+ IQChannel* channel_;
+ std::vector< boost::shared_ptr<IQHandler> > handlers_;
+ std::vector< boost::shared_ptr<IQHandler> > queuedRemoves_;
+ bool queueRemoves_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Request.cpp b/Swiften/Queries/Request.cpp
new file mode 100644
index 0000000..ce9f763
--- /dev/null
+++ b/Swiften/Queries/Request.cpp
@@ -0,0 +1,55 @@
+#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) : router_(router), type_(type), receiver_(receiver), payload_(payload), sent_(false) {
+}
+
+Request::Request(IQ::Type type, const JID& receiver, IQRouter* router) : router_(router), type_(type), receiver_(receiver), sent_(false) {
+}
+
+void Request::send() {
+ assert(payload_);
+ assert(!sent_);
+ sent_ = true;
+
+ boost::shared_ptr<IQ> iq(new IQ(type_));
+ iq->setTo(receiver_);
+ iq->addPayload(payload_);
+ id_ = router_->getNewIQID();
+ iq->setID(id_);
+
+ try {
+ router_->addHandler(shared_from_this());
+ }
+ catch (const std::exception&) {
+ router_->addHandler(this);
+ }
+
+ router_->sendIQ(iq);
+}
+
+bool Request::handleIQ(boost::shared_ptr<IQ> iq) {
+ bool handled = false;
+ if (sent_ && iq->getID() == id_) {
+ if (iq->getType() == IQ::Result) {
+ handleResponse(iq->getPayloadOfSameType(payload_), boost::optional<ErrorPayload>());
+ }
+ else {
+ boost::shared_ptr<ErrorPayload> errorPayload = iq->getPayload<ErrorPayload>();
+ if (errorPayload) {
+ handleResponse(boost::shared_ptr<Payload>(), boost::optional<ErrorPayload>(*errorPayload));
+ }
+ else {
+ handleResponse(boost::shared_ptr<Payload>(), boost::optional<ErrorPayload>(ErrorPayload::UndefinedCondition));
+ }
+ }
+ router_->removeHandler(this);
+ handled = true;
+ }
+ return handled;
+}
+
+}
diff --git a/Swiften/Queries/Request.h b/Swiften/Queries/Request.h
new file mode 100644
index 0000000..cc4a58e
--- /dev/null
+++ b/Swiften/Queries/Request.h
@@ -0,0 +1,50 @@
+#ifndef SWIFTEN_Request_H
+#define SWIFTEN_Request_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class Request : public IQHandler, public boost::enable_shared_from_this<Request> {
+ public:
+ Request(
+ IQ::Type type,
+ const JID& receiver,
+ boost::shared_ptr<Payload> payload,
+ IQRouter* router);
+ Request(
+ IQ::Type type,
+ const JID& receiver,
+ IQRouter* router);
+
+ void send();
+
+ protected:
+ virtual void setPayload(boost::shared_ptr<Payload> p) {
+ payload_ = p;
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload>, boost::optional<ErrorPayload>) = 0;
+
+ private:
+ bool handleIQ(boost::shared_ptr<IQ>);
+
+ private:
+ IQRouter* router_;
+ IQ::Type type_;
+ JID receiver_;
+ boost::shared_ptr<Payload> payload_;
+ String id_;
+ bool sent_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Requests/GetDiscoInfoRequest.h b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
new file mode 100644
index 0000000..70f09ca
--- /dev/null
+++ b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class GetDiscoInfoRequest : public GenericRequest<DiscoInfo> {
+ public:
+ GetDiscoInfoRequest(const JID& jid, IQRouter* router) :
+ GenericRequest<DiscoInfo>(IQ::Get, jid, boost::shared_ptr<DiscoInfo>(new DiscoInfo()), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/GetPrivateStorageRequest.h b/Swiften/Queries/Requests/GetPrivateStorageRequest.h
new file mode 100644
index 0000000..5d6440e
--- /dev/null
+++ b/Swiften/Queries/Requests/GetPrivateStorageRequest.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Queries/Request.h"
+#include "Swiften/Elements/PrivateStorage.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class GetPrivateStorageRequest : public Request {
+ public:
+ GetPrivateStorageRequest(IQRouter* router) : Request(IQ::Get, JID(), boost::shared_ptr<PrivateStorage>(new PrivateStorage(boost::shared_ptr<Payload>(new PAYLOAD_TYPE()))), router) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<ErrorPayload> error) {
+ boost::shared_ptr<PrivateStorage> storage = boost::dynamic_pointer_cast<PrivateStorage>(payload);
+ if (storage) {
+ onResponse(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(storage->getPayload()), error);
+ }
+ else {
+ onResponse(boost::shared_ptr<PAYLOAD_TYPE>(), error);
+ }
+ }
+
+ public:
+ boost::signal<void (boost::shared_ptr<PAYLOAD_TYPE>, const boost::optional<ErrorPayload>&)> onResponse;
+ };
+}
diff --git a/Swiften/Queries/Requests/GetRosterRequest.h b/Swiften/Queries/Requests/GetRosterRequest.h
new file mode 100644
index 0000000..cac1c12
--- /dev/null
+++ b/Swiften/Queries/Requests/GetRosterRequest.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/RosterPayload.h"
+
+namespace Swift {
+ class GetRosterRequest : public GenericRequest<RosterPayload> {
+ public:
+ GetRosterRequest(IQRouter* router) :
+ GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
new file mode 100644
index 0000000..87b51ce
--- /dev/null
+++ b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/SecurityLabelsCatalog.h"
+
+namespace Swift {
+ class GetSecurityLabelsCatalogRequest : public GenericRequest<SecurityLabelsCatalog> {
+ public:
+ GetSecurityLabelsCatalogRequest(
+ const JID& recipient,
+ IQRouter* router) :
+ GenericRequest<SecurityLabelsCatalog>(
+ IQ::Get, JID(), boost::shared_ptr<SecurityLabelsCatalog>(new SecurityLabelsCatalog(recipient)), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/GetVCardRequest.h b/Swiften/Queries/Requests/GetVCardRequest.h
new file mode 100644
index 0000000..8fc6e17
--- /dev/null
+++ b/Swiften/Queries/Requests/GetVCardRequest.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/VCard.h"
+
+namespace Swift {
+ class GetVCardRequest : public GenericRequest<VCard> {
+ public:
+ GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest<VCard>(IQ::Get, jid, boost::shared_ptr<Payload>(new VCard()), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/SetPrivateStorageRequest.h b/Swiften/Queries/Requests/SetPrivateStorageRequest.h
new file mode 100644
index 0000000..834ddd8
--- /dev/null
+++ b/Swiften/Queries/Requests/SetPrivateStorageRequest.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Queries/Request.h"
+#include "Swiften/Elements/PrivateStorage.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class SetPrivateStorageRequest : public Request {
+ public:
+ SetPrivateStorageRequest(boost::shared_ptr<PAYLOAD_TYPE> payload, IQRouter* router) : Request(IQ::Set, JID(), boost::shared_ptr<PrivateStorage>(new PrivateStorage(payload)), router) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<ErrorPayload> error) {
+ onResponse(error);
+ }
+
+ public:
+ boost::signal<void (const boost::optional<ErrorPayload>&)> onResponse;
+ };
+}
diff --git a/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp b/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
new file mode 100644
index 0000000..a86a111
--- /dev/null
+++ b/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
@@ -0,0 +1,106 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/Requests/GetPrivateStorageRequest.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+#include "Swiften/Elements/Payload.h"
+
+using namespace Swift;
+
+class GetPrivateStorageRequestTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(GetPrivateStorageRequestTest);
+ CPPUNIT_TEST(testSend);
+ CPPUNIT_TEST(testHandleResponse);
+ CPPUNIT_TEST(testHandleResponse_Error);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ class MyPayload : public Payload {
+ public:
+ MyPayload(const String& text = "") : text(text) {}
+ String text;
+ };
+
+ public:
+ GetPrivateStorageRequestTest() {}
+
+ void setUp() {
+ channel = new DummyIQChannel();
+ router = new IQRouter(channel);
+ }
+
+ void tearDown() {
+ delete router;
+ delete channel;
+ }
+
+ void testSend() {
+ GetPrivateStorageRequest<MyPayload> request(router);
+ request.send();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(JID(), channel->iqs_[0]->getTo());
+ CPPUNIT_ASSERT_EQUAL(IQ::Get, channel->iqs_[0]->getType());
+ boost::shared_ptr<PrivateStorage> storage = channel->iqs_[0]->getPayload<PrivateStorage>();
+ CPPUNIT_ASSERT(storage);
+ boost::shared_ptr<MyPayload> payload = boost::dynamic_pointer_cast<MyPayload>(storage->getPayload());
+ CPPUNIT_ASSERT(payload);
+ }
+
+ void testHandleResponse() {
+ GetPrivateStorageRequest<MyPayload> testling(router);
+ testling.onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
+ testling.send();
+ channel->onIQReceived(createResponse("test-id", "foo"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(responses.size()));
+ CPPUNIT_ASSERT_EQUAL(String("foo"), boost::dynamic_pointer_cast<MyPayload>(responses[0])->text);
+ }
+
+ void testHandleResponse_Error() {
+ GetPrivateStorageRequest<MyPayload> testling(router);
+ testling.onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
+ testling.send();
+ channel->onIQReceived(createError("test-id"));
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(responses.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(errors.size()));
+ }
+
+ private:
+ void handleResponse(boost::shared_ptr<Payload> p, const boost::optional<ErrorPayload>& e) {
+ if (e) {
+ errors.push_back(*e);
+ }
+ else {
+ responses.push_back(p);
+ }
+ }
+
+ boost::shared_ptr<IQ> createResponse(const String& id, const String& text) {
+ boost::shared_ptr<IQ> iq(new IQ(IQ::Result));
+ boost::shared_ptr<PrivateStorage> storage(new PrivateStorage());
+ storage->setPayload(boost::shared_ptr<Payload>(new MyPayload(text)));
+ iq->addPayload(storage);
+ 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;
+ std::vector< ErrorPayload > errors;
+ std::vector< boost::shared_ptr<Payload> > responses;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GetPrivateStorageRequestTest);
diff --git a/Swiften/Queries/Responder.h b/Swiften/Queries/Responder.h
new file mode 100644
index 0000000..9c025eb
--- /dev/null
+++ b/Swiften/Queries/Responder.h
@@ -0,0 +1,58 @@
+#ifndef SWIFTEN_Responder_H
+#define SWIFTEN_Responder_H
+
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class Responder : public IQHandler {
+ public:
+ Responder(IQRouter* router) : router_(router) {
+ router_->addHandler(this);
+ }
+
+ ~Responder() {
+ router_->removeHandler(this);
+ }
+
+ 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) {
+ router_->sendIQ(IQ::createResult(to, id, payload));
+ }
+
+ void sendError(const JID& to, const String& id, ErrorPayload::Condition condition, ErrorPayload::Type type) {
+ router_->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) {
+ router_->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::NotAllowed, ErrorPayload::Cancel));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ IQRouter* router_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.cpp b/Swiften/Queries/Responders/DiscoInfoResponder.cpp
new file mode 100644
index 0000000..572f83f
--- /dev/null
+++ b/Swiften/Queries/Responders/DiscoInfoResponder.cpp
@@ -0,0 +1,36 @@
+#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+
+DiscoInfoResponder::DiscoInfoResponder(IQRouter* router) : GetResponder<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, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
+ }
+ }
+ return true;
+}
+
+}
diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.h b/Swiften/Queries/Responders/DiscoInfoResponder.h
new file mode 100644
index 0000000..3270d5d
--- /dev/null
+++ b/Swiften/Queries/Responders/DiscoInfoResponder.h
@@ -0,0 +1,28 @@
+#ifndef SWIFTEN_DiscoInfoResponder_H
+#define SWIFTEN_DiscoInfoResponder_H
+
+#include <map>
+
+#include "Swiften/Queries/GetResponder.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class DiscoInfoResponder : public GetResponder<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);
+
+ private:
+ DiscoInfo info_;
+ std::map<String, DiscoInfo> nodeInfo_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/RosterPushResponder.h b/Swiften/Queries/Responders/RosterPushResponder.h
new file mode 100644
index 0000000..69185c8
--- /dev/null
+++ b/Swiften/Queries/Responders/RosterPushResponder.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Queries/SetResponder.h"
+#include "Swiften/Elements/RosterPayload.h"
+
+namespace Swift {
+ class RosterPushResponder : public SetResponder<RosterPayload> {
+ public:
+ RosterPushResponder(IQRouter* router) : SetResponder<RosterPayload>(router) {}
+
+ public:
+ boost::signal<void (boost::shared_ptr<RosterPayload>)> onRosterReceived;
+
+ private:
+ virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<RosterPayload> payload) {
+ onRosterReceived(payload);
+ sendResponse(from, id, boost::shared_ptr<Payload>());
+ return true;
+ }
+ };
+}
diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.cpp b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp
new file mode 100644
index 0000000..e608f24
--- /dev/null
+++ b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp
@@ -0,0 +1,16 @@
+#include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+SoftwareVersionResponder::SoftwareVersionResponder(
+ const String& client, const String& version, IQRouter* router) :
+ GetResponder<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;
+}
+
+}
diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.h b/Swiften/Queries/Responders/SoftwareVersionResponder.h
new file mode 100644
index 0000000..85d1089
--- /dev/null
+++ b/Swiften/Queries/Responders/SoftwareVersionResponder.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_SoftwareVersionResponder_H
+#define SWIFTEN_SoftwareVersionResponder_H
+
+#include "Swiften/Queries/GetResponder.h"
+#include "Swiften/Elements/SoftwareVersion.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class SoftwareVersionResponder : public GetResponder<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);
+
+ 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..5993d0c
--- /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<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>());
+ CPPUNIT_ASSERT(payload);
+ }
+
+ private:
+ IQRouter* router_;
+ DummyIQChannel* channel_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoResponderTest);
diff --git a/Swiften/Queries/SetResponder.h b/Swiften/Queries/SetResponder.h
new file mode 100644
index 0000000..51fe39a
--- /dev/null
+++ b/Swiften/Queries/SetResponder.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Swiften/Queries/Responder.h"
+
+namespace Swift {
+ template<typename T>
+ class SetResponder : public Responder<T> {
+ public:
+ SetResponder(IQRouter* router) : Responder<T>(router) {}
+
+ private:
+ virtual bool handleGetRequest(const JID&, const String&, boost::shared_ptr<T>) { return false; }
+ };
+}
diff --git a/Swiften/Queries/UnitTest/IQRouterTest.cpp b/Swiften/Queries/UnitTest/IQRouterTest.cpp
new file mode 100644
index 0000000..5760b09
--- /dev/null
+++ b/Swiften/Queries/UnitTest/IQRouterTest.cpp
@@ -0,0 +1,143 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+
+using namespace Swift;
+
+class IQRouterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(IQRouterTest);
+ CPPUNIT_TEST(testRemoveHandler);
+ CPPUNIT_TEST(testRemoveHandler_AfterHandleIQ);
+ CPPUNIT_TEST(testHandleIQ_SuccesfulHandlerFirst);
+ CPPUNIT_TEST(testHandleIQ_SuccesfulHandlerLast);
+ CPPUNIT_TEST(testHandleIQ_NoSuccesfulHandler);
+ CPPUNIT_TEST(testHandleIQ_HandlerRemovedDuringHandle);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ IQRouterTest() {}
+
+ void setUp() {
+ channel_ = new DummyIQChannel();
+ }
+
+ void tearDown() {
+ delete channel_;
+ }
+
+ void testRemoveHandler() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(true, &testling);
+ DummyIQHandler handler2(true, &testling);
+ testling.removeHandler(&handler1);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(0, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(1, handler2.called);
+ }
+
+ void testRemoveHandler_AfterHandleIQ() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(true, &testling);
+ DummyIQHandler handler2(true, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+ testling.removeHandler(&handler1);
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(1, handler2.called);
+ }
+
+ void testHandleIQ_SuccesfulHandlerFirst() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(true, &testling);
+ DummyIQHandler handler2(false, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(0, handler2.called);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ void testHandleIQ_SuccesfulHandlerLast() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(false, &testling);
+ DummyIQHandler handler2(true, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(1, handler2.called);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ void testHandleIQ_NoSuccesfulHandler() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler(false, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT(channel_->iqs_[0]->getPayload<ErrorPayload>());
+ }
+
+
+ void testHandleIQ_HandlerRemovedDuringHandle() {
+ IQRouter testling(channel_);
+ RemovingIQHandler handler1(&testling);
+ DummyIQHandler handler2(true, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(2, handler2.called);
+ }
+
+ private:
+ struct DummyIQHandler : public IQHandler {
+ DummyIQHandler(bool handle, IQRouter* router) : handle(handle), router(router), called(0) {
+ router->addHandler(this);
+ }
+
+ ~DummyIQHandler() {
+ router->removeHandler(this);
+ }
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) {
+ called++;
+ return handle;
+ }
+ bool handle;
+ IQRouter* router;
+ int called;
+ };
+
+ struct RemovingIQHandler : public IQHandler {
+ RemovingIQHandler(IQRouter* router) : router(router), called(0) {
+ router->addHandler(this);
+ }
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) {
+ called++;
+ router->removeHandler(this);
+ return false;
+ }
+ IQRouter* router;
+ int called;
+ };
+
+
+ DummyIQChannel* channel_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IQRouterTest);
diff --git a/Swiften/Queries/UnitTest/RequestTest.cpp b/Swiften/Queries/UnitTest/RequestTest.cpp
new file mode 100644
index 0000000..c569bb5
--- /dev/null
+++ b/Swiften/Queries/UnitTest/RequestTest.cpp
@@ -0,0 +1,167 @@
+#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(testHandleIQ_ErrorWithoutPayload);
+ CPPUNIT_TEST(testHandleIQ_BeforeSend);
+ 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;
+ }
+
+ 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, static_cast<int>(receivedErrors.size()));
+ 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, static_cast<int>(receivedErrors.size()));
+ 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();
+
+ boost::shared_ptr<IQ> error = createError("test-id");
+ boost::shared_ptr<Payload> errorPayload = boost::shared_ptr<ErrorPayload>(new ErrorPayload(ErrorPayload::FeatureNotImplemented));
+ error->addPayload(errorPayload);
+ channel_->onIQReceived(error);
+
+ CPPUNIT_ASSERT_EQUAL(0, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(receivedErrors.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::FeatureNotImplemented, receivedErrors[0].getCondition());
+ }
+
+ void testHandleIQ_ErrorWithoutPayload() {
+ 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, static_cast<int>(receivedErrors.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::UndefinedCondition, receivedErrors[0].getCondition());
+ }
+
+ void testHandleIQ_BeforeSend() {
+ MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_);
+ testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2));
+ channel_->onIQReceived(createResponse("test-id"));
+
+ CPPUNIT_ASSERT_EQUAL(0, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedErrors.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ private:
+ void handleResponse(boost::shared_ptr<Payload> p, const boost::optional<ErrorPayload>& e) {
+ if (e) {
+ receivedErrors.push_back(*e);
+ }
+ 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_;
+ std::vector<ErrorPayload> receivedErrors;
+};
+
+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/AppearOffline.h b/Swiften/Roster/AppearOffline.h
new file mode 100644
index 0000000..673e018
--- /dev/null
+++ b/Swiften/Roster/AppearOffline.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+namespace Swift {
+
+class RosterItem;
+
+class AppearOffline : public RosterItemOperation {
+ public:
+ AppearOffline() {
+ }
+
+ virtual void operator() (RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ if (contact) {
+ contact->setStatusShow(StatusShow::None);
+ }
+ }
+
+};
+
+}
+
+
diff --git a/Swiften/Roster/ContactRosterItem.cpp b/Swiften/Roster/ContactRosterItem.cpp
new file mode 100644
index 0000000..968f7f1
--- /dev/null
+++ b/Swiften/Roster/ContactRosterItem.cpp
@@ -0,0 +1,50 @@
+#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;
+ widget_->setStatusShow(show);
+}
+
+void ContactRosterItem::setStatusText(const String& status) {
+ widget_->setStatusText(status);
+}
+
+void ContactRosterItem::setAvatarPath(const String& path) {
+ widget_->setAvatarPath(path);
+}
+
+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..f1810aa
--- /dev/null
+++ b/Swiften/Roster/ContactRosterItem.h
@@ -0,0 +1,41 @@
+#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);
+ void setStatusText(const String& status);
+ void setAvatarPath(const String& path);
+ 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..2ab59ea
--- /dev/null
+++ b/Swiften/Roster/GroupRosterItem.h
@@ -0,0 +1,68 @@
+#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);
+ }
+
+ ~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/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..28245af
--- /dev/null
+++ b/Swiften/Roster/Roster.cpp
@@ -0,0 +1,139 @@
+#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::removeContactFromGroup(const JID& jid, const String& groupName) {
+ std::vector<RosterItem*>::iterator it = children_.begin();
+ while (it != children_.end()) {
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group && group->getName() == groupName) {
+ 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);
+ }
+ }
+}
+
+}
+
diff --git a/Swiften/Roster/Roster.h b/Swiften/Roster/Roster.h
new file mode 100644
index 0000000..6010832
--- /dev/null
+++ b/Swiften/Roster/Roster.h
@@ -0,0 +1,49 @@
+#ifndef SWIFTEN_Roster_H
+#define SWIFTEN_Roster_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/UserRosterAction.h"
+#include "Swiften/Roster/RosterFilter.h"
+
+#include <vector>
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class TreeWidgetFactory;
+class TreeWidget;
+class RosterItem;
+class GroupRosterItem;
+
+class Roster {
+ public:
+ Roster(TreeWidget *treeWidget, TreeWidgetFactory *widgetFactory);
+ ~Roster();
+
+ TreeWidget* getWidget();
+ GroupRosterItem* getGroup(const String& groupName);
+ void addContact(const JID& jid, const String& name, const String& group);
+ void removeContact(const JID& jid);
+ void removeContactFromGroup(const JID& jid, const String& group);
+ void applyOnItems(const RosterItemOperation& operation);
+ boost::signal<void (boost::shared_ptr<UserRosterAction>)> onUserAction;
+ void addFilter(RosterFilter *filter) {filters_.push_back(filter);filterAll();}
+ void removeFilter(RosterFilter *filter);
+ std::vector<RosterFilter*> getFilters() {return filters_;}
+
+ private:
+ void filterItem(RosterItem* item);
+ void filterAll();
+ void handleUserAction(boost::shared_ptr<UserRosterAction> action);
+ TreeWidget *treeWidget_;
+ TreeWidgetFactory *widgetFactory_;
+ std::vector<RosterItem*> children_;
+ std::vector<RosterItem*> items_;
+ std::vector<RosterFilter*> filters_;
+};
+}
+
+#endif
diff --git a/Swiften/Roster/RosterFilter.h b/Swiften/Roster/RosterFilter.h
new file mode 100644
index 0000000..a824304
--- /dev/null
+++ b/Swiften/Roster/RosterFilter.h
@@ -0,0 +1,17 @@
+#ifndef SWIFTEN_RosterFilter_H
+#define SWIFTEN_RosterFilter_H
+
+#include "Swiften/Roster/RosterItem.h"
+
+namespace Swift {
+
+class RosterFilter {
+ public:
+ virtual ~RosterFilter() {}
+ virtual bool operator() (RosterItem* item) const = 0;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/Roster/RosterItem.h b/Swiften/Roster/RosterItem.h
new file mode 100644
index 0000000..2707920
--- /dev/null
+++ b/Swiften/Roster/RosterItem.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_RosterItem_H
+#define SWIFTEN_RosterItem_H
+
+#include "Swiften/Roster/UserRosterAction.h"
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class RosterItem {
+ public:
+ virtual ~RosterItem() {};
+ boost::signal<void (boost::shared_ptr<UserRosterAction>)> onUserAction;
+ protected:
+ void handleUserAction(boost::shared_ptr<UserRosterAction> action) {
+ action->setRosterItem(this);
+ onUserAction(action);
+ }
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/RosterItemOperation.h b/Swiften/Roster/RosterItemOperation.h
new file mode 100644
index 0000000..ea8e723
--- /dev/null
+++ b/Swiften/Roster/RosterItemOperation.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_RosterItemOperation_H
+#define SWIFTEN_RosterItemOperation_H
+
+#include "Swiften/Roster/RosterItem.h"
+
+namespace Swift {
+
+class RosterItemOperation {
+ public:
+ virtual ~RosterItemOperation() {}
+ virtual void operator() (RosterItem*) const = 0;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/SetAvatar.h b/Swiften/Roster/SetAvatar.h
new file mode 100644
index 0000000..7bc9c37
--- /dev/null
+++ b/Swiften/Roster/SetAvatar.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_SetAvatar_H
+#define SWIFTEN_SetAvatar_H
+
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+namespace Swift {
+
+class RosterItem;
+
+class SetAvatar : public RosterItemOperation {
+ public:
+ SetAvatar(const JID& jid, const String& path, JID::CompareType compareType = JID::WithoutResource) : jid_(jid), path_(path), compareType_(compareType) {
+ }
+
+ virtual void operator() (RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ if (contact && contact->getJID().equals(jid_, compareType_)) {
+ contact->setAvatarPath(path_);
+ }
+ }
+
+ private:
+ JID jid_;
+ String path_;
+ JID::CompareType compareType_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/SetPresence.h b/Swiften/Roster/SetPresence.h
new file mode 100644
index 0000000..a18ae6d
--- /dev/null
+++ b/Swiften/Roster/SetPresence.h
@@ -0,0 +1,38 @@
+#ifndef SWIFTEN_SetPresence_H
+#define SWIFTEN_SetPresence_H
+
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+namespace Swift {
+
+class RosterItem;
+
+class SetPresence : public RosterItemOperation {
+ public:
+ SetPresence(boost::shared_ptr<Presence> presence, JID::CompareType compareType = JID::WithoutResource) : presence_(presence), compareType_(compareType) {
+ }
+
+ virtual void operator() (RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ if (contact && contact->getJID().equals(presence_->getFrom(), compareType_)) {
+ if (presence_->getType() != Presence::Available) {
+ contact->setStatusShow(StatusShow::None);
+ contact->setStatusText(presence_->getStatus());
+ } else {
+ contact->setStatusShow(presence_->getShow());
+ contact->setStatusText(presence_->getStatus());
+ }
+ }
+ }
+
+ private:
+ boost::shared_ptr<Presence> presence_;
+ JID::CompareType compareType_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/TreeWidget.h b/Swiften/Roster/TreeWidget.h
new file mode 100644
index 0000000..a26003e
--- /dev/null
+++ b/Swiften/Roster/TreeWidget.h
@@ -0,0 +1,13 @@
+#ifndef SWIFTEN_TreeWidget_H
+#define SWIFTEN_TreeWidget_H
+
+namespace Swift {
+
+class TreeWidget {
+ public:
+ virtual ~TreeWidget() {}
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/TreeWidgetFactory.h b/Swiften/Roster/TreeWidgetFactory.h
new file mode 100644
index 0000000..f4ba68d
--- /dev/null
+++ b/Swiften/Roster/TreeWidgetFactory.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_TreeWidgetFactory_H
+#define SWIFTEN_TreeWidgetFactory_H
+
+namespace Swift {
+
+class TreeWidgetItem;
+class TreeWidget;
+
+class TreeWidgetFactory {
+ public:
+ virtual ~TreeWidgetFactory() {}
+ virtual TreeWidget* createTreeWidget() = 0;
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidgetItem* item) = 0;
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidget* item) = 0;
+};
+
+}
+
+#endif
+
diff --git a/Swiften/Roster/TreeWidgetItem.h b/Swiften/Roster/TreeWidgetItem.h
new file mode 100644
index 0000000..4124546
--- /dev/null
+++ b/Swiften/Roster/TreeWidgetItem.h
@@ -0,0 +1,34 @@
+#ifndef SWIFTEN_TreeWidgetItem_H
+#define SWIFTEN_TreeWidgetItem_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/UserRosterAction.h"
+#include "Swiften/Elements/StatusShow.h"
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class TreeWidgetItem {
+ public:
+ virtual ~TreeWidgetItem() {}
+ virtual void setText(const String& text) = 0;
+ virtual void setStatusText(const String& text) = 0;
+ virtual void setAvatarPath(const String& path) = 0;
+ virtual void setExpanded(bool b) = 0;
+ //virtual void setTextColor(unsigned long color) = 0;
+ virtual void setStatusShow(StatusShow::Type show) = 0;
+ //virtual void setBackgroundColor(unsigned long color) = 0;
+ boost::signal<void (boost::shared_ptr<UserRosterAction>)> onUserAction;
+ virtual void show() = 0;
+ virtual void hide() = 0;
+ void performUserAction(boost::shared_ptr<UserRosterAction> action) {
+ action->setTreeWidgetItem(this);
+ onUserAction(action);
+ }
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/UnitTest/MockTreeWidget.h b/Swiften/Roster/UnitTest/MockTreeWidget.h
new file mode 100644
index 0000000..e6f6def
--- /dev/null
+++ b/Swiften/Roster/UnitTest/MockTreeWidget.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_MockTreeWidget_H
+#define SWIFTEN_MockTreeWidget_H
+
+#include "Swiften/Roster/TreeWidget.h"
+
+namespace Swift {
+
+class MockTreeWidget : public TreeWidget {
+ public:
+ virtual ~MockTreeWidget() {}
+};
+
+}
+#endif
diff --git a/Swiften/Roster/UnitTest/MockTreeWidgetFactory.h b/Swiften/Roster/UnitTest/MockTreeWidgetFactory.h
new file mode 100644
index 0000000..b2b4f10
--- /dev/null
+++ b/Swiften/Roster/UnitTest/MockTreeWidgetFactory.h
@@ -0,0 +1,50 @@
+#ifndef SWIFTEN_MockTreeWidgetFactory_H
+#define SWIFTEN_MockTreeWidgetFactory_H
+
+#include "Swiften/Roster/TreeWidgetFactory.h"
+
+#include <vector>
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidget.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidgetItem.h"
+
+namespace Swift {
+
+class MockTreeWidgetItem;
+class MockTreeWidget;
+
+class MockTreeWidgetFactory : public TreeWidgetFactory {
+ public:
+ virtual ~MockTreeWidgetFactory() {}
+ virtual TreeWidget* createTreeWidget() {
+ root_ = new MockTreeWidget();
+ return root_;
+ };
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidgetItem* group) {
+ MockTreeWidgetItem* entry = new MockTreeWidgetItem();
+ groupMembers_[group].push_back(entry);
+ return entry;
+ };
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidget*) {
+ MockTreeWidgetItem* group = new MockTreeWidgetItem();
+ groups_.push_back(group);
+ return group;
+ };
+ virtual std::vector<String> getGroups() {
+ std::vector<String> groupNames;
+ foreach (MockTreeWidgetItem* group, groups_) {
+ groupNames.push_back(group->getText());
+ }
+ return groupNames;
+ };
+ private:
+ std::vector<MockTreeWidgetItem*> groups_;
+ std::map<TreeWidgetItem*, std::vector<MockTreeWidgetItem*> > groupMembers_;
+ MockTreeWidget* root_;
+};
+
+}
+
+#endif
+
+
diff --git a/Swiften/Roster/UnitTest/MockTreeWidgetItem.h b/Swiften/Roster/UnitTest/MockTreeWidgetItem.h
new file mode 100644
index 0000000..a40aca7
--- /dev/null
+++ b/Swiften/Roster/UnitTest/MockTreeWidgetItem.h
@@ -0,0 +1,30 @@
+#ifndef SWIFTEN_MockTreeWidgetItem_H
+#define SWIFTEN_MockTreeWidgetItem_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class MockTreeWidgetItem : public TreeWidgetItem {
+ public:
+ virtual ~MockTreeWidgetItem() {};
+ virtual void setText(const String& text) {text_ = text;};
+ String getText() {return text_;};
+ virtual void setStatusText(const String&) {};
+ virtual void setAvatarPath(const String&) {};
+ virtual void setExpanded(bool) {};
+ virtual void setStatusShow(StatusShow::Type /*show*/) {};
+ virtual void show() {};
+ virtual void hide() {};
+ private:
+ String text_;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp b/Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp
diff --git a/Swiften/Roster/UnitTest/RosterTest.cpp b/Swiften/Roster/UnitTest/RosterTest.cpp
new file mode 100644
index 0000000..b43a41c
--- /dev/null
+++ b/Swiften/Roster/UnitTest/RosterTest.cpp
@@ -0,0 +1,57 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Roster/Roster.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidget.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidgetFactory.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidgetItem.h"
+
+using namespace Swift;
+
+class RosterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RosterTest);
+ CPPUNIT_TEST(testGetGroup);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ Roster *roster_;
+ TreeWidget *widget_;
+ TreeWidgetFactory *factory_;
+ JID jid1_;
+ JID jid2_;
+ JID jid3_;
+
+ public:
+
+ RosterTest() : jid1_(JID("a@b.c")), jid2_(JID("b@c.d")), jid3_(JID("c@d.e")) {}
+
+ void setUp() {
+ factory_ = new MockTreeWidgetFactory();
+ widget_ = factory_->createTreeWidget();
+ roster_ = new Roster(widget_, factory_);
+ }
+
+ void tearDown() {
+ delete roster_;
+ //delete widget_;
+ delete factory_;
+ }
+
+ void testGetGroup() {
+ roster_->addContact(jid1_, "Bert", "group1");
+ roster_->addContact(jid2_, "Ernie", "group2");
+ roster_->addContact(jid3_, "Cookie", "group1");
+
+ CPPUNIT_ASSERT_EQUAL(roster_->getGroup("group1"), roster_->getGroup("group1"));
+ CPPUNIT_ASSERT_EQUAL(roster_->getGroup("group2"), roster_->getGroup("group2"));
+ CPPUNIT_ASSERT_EQUAL(roster_->getGroup("group3"), roster_->getGroup("group3"));
+ CPPUNIT_ASSERT(roster_->getGroup("group1") != roster_->getGroup("group2"));
+ CPPUNIT_ASSERT(roster_->getGroup("group2") != roster_->getGroup("group3"));
+ CPPUNIT_ASSERT(roster_->getGroup("group3") != roster_->getGroup("group1"));
+ }
+
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(RosterTest);
+
diff --git a/Swiften/Roster/UnitTest/XMPPRosterTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterTest.cpp
new file mode 100644
index 0000000..d03953e
--- /dev/null
+++ b/Swiften/Roster/UnitTest/XMPPRosterTest.cpp
@@ -0,0 +1,164 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include <vector>
+
+#include "Swiften/Roster/XMPPRoster.h"
+
+
+using namespace Swift;
+
+enum XMPPRosterEvents {None, Add, Remove, Update};
+
+class XMPPRosterSignalHandler {
+public:
+ XMPPRosterSignalHandler(XMPPRoster* roster) {
+ lastEvent_ = None;
+ roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1));
+ roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1));
+ roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3));
+ }
+
+ XMPPRosterEvents getLastEvent() {
+ return lastEvent_;
+ }
+
+ JID getLastJID() {
+ return lastJID_;
+ }
+
+ String getLastOldName() {
+ return lastOldName_;
+ }
+
+ std::vector<String> getLastOldGroups() {
+ return lastOldGroups_;
+ }
+
+ void reset() {
+ lastEvent_ = None;
+ }
+
+private:
+ void handleJIDAdded(const JID& jid) {
+ lastJID_ = jid;
+ lastEvent_ = Add;
+ }
+
+ void handleJIDRemoved(const JID& jid) {
+ lastJID_ = jid;
+ lastEvent_ = Remove;
+ }
+
+ void handleJIDUpdated(const JID& jid, const String& oldName, const std::vector<String>& oldGroups) {
+ CPPUNIT_ASSERT_EQUAL(None, lastEvent_);
+ lastJID_ = jid;
+ lastOldName_ = oldName;
+ lastOldGroups_ = oldGroups;
+ lastEvent_ = Update;
+ }
+
+ XMPPRosterEvents lastEvent_;
+ JID lastJID_;
+ String lastOldName_;
+ std::vector<String> lastOldGroups_;
+
+};
+
+class XMPPRosterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMPPRosterTest);
+ CPPUNIT_TEST(testJIDAdded);
+ CPPUNIT_TEST(testJIDRemoved);
+ CPPUNIT_TEST(testJIDUpdated);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ XMPPRoster* roster_;
+ XMPPRosterSignalHandler* handler_;
+ JID jid1_;
+ JID jid2_;
+ JID jid3_;
+ std::vector<String> groups1_;
+ std::vector<String> groups2_;
+
+
+ public:
+
+ XMPPRosterTest() : jid1_(JID("a@b.c")), jid2_(JID("b@c.d")), jid3_(JID("c@d.e")) {}
+
+ void setUp() {
+ roster_ = new XMPPRoster();
+ handler_ = new XMPPRosterSignalHandler(roster_);
+ groups1_.push_back("bobs");
+ groups1_.push_back("berts");
+ groups2_.push_back("ernies");
+ }
+
+ void tearDown() {
+ delete roster_;
+ }
+
+ void testJIDAdded() {
+ roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+ handler_->reset();
+ roster_->addContact(jid2_, "NameTwo", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid2_));
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid2_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+ handler_->reset();
+ roster_->addContact(jid3_, "NewName", groups2_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid3_));
+ CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid3_));
+ }
+
+ void testJIDRemoved() {
+ roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+ handler_->reset();
+ roster_->removeContact(jid1_);
+ CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ handler_->reset();
+ roster_->addContact(jid1_, "NewName2", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName2"), roster_->getNameForJID(jid1_));
+ roster_->addContact(jid2_, "NewName3", groups1_, RosterItemPayload::Both);
+ handler_->reset();
+ roster_->removeContact(jid2_);
+ CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+ handler_->reset();
+ roster_->removeContact(jid1_);
+ CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ }
+
+ void testJIDUpdated() {
+ roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+ handler_->reset();
+ roster_->addContact(jid1_, "NameTwo", groups2_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid1_));
+ }
+
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterTest);
+
diff --git a/Swiften/Roster/UserRosterAction.h b/Swiften/Roster/UserRosterAction.h
new file mode 100644
index 0000000..80ace68
--- /dev/null
+++ b/Swiften/Roster/UserRosterAction.h
@@ -0,0 +1,32 @@
+#ifndef SWIFTEN_UserRosterAction_H
+#define SWIFTEN_UserRosterAction_H
+
+namespace Swift {
+class RosterItem;
+class TreeWidgetItem;
+
+class UserRosterAction {
+ public:
+ virtual ~UserRosterAction() {};
+ void setRosterItem(RosterItem *item) {
+ rosterItem_ = item;
+ };
+ void setTreeWidgetItem(TreeWidgetItem *item) {
+ treeWidgetItem_ = item;
+ }
+ RosterItem* getRosterItem() {
+ return rosterItem_;
+ }
+ TreeWidgetItem* getTreeWidgetItem() {
+ return treeWidgetItem_;
+ }
+
+ private:
+ RosterItem *rosterItem_;
+ TreeWidgetItem *treeWidgetItem_;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/Roster/XMPPRoster.cpp b/Swiften/Roster/XMPPRoster.cpp
new file mode 100644
index 0000000..62edc45
--- /dev/null
+++ b/Swiften/Roster/XMPPRoster.cpp
@@ -0,0 +1,48 @@
+#include "Swiften/Roster/XMPPRoster.h"
+
+namespace Swift {
+
+void XMPPRoster::addContact(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) {
+ JID bareJID(jid.toBare());
+ bool exists = containsJID(bareJID);
+ String oldName = getNameForJID(bareJID);
+ std::vector<String> oldGroups = entries_[bareJID].groups;
+ if (exists) {
+ entries_.erase(bareJID);
+ }
+ XMPPRosterItem item;
+ item.groups = groups;
+ item.name = name;
+ item.jid = jid;
+ item.subscription = subscription;
+ entries_[bareJID] = item;
+ if (exists) {
+ onJIDUpdated(bareJID, oldName, oldGroups);
+ } else {
+ onJIDAdded(bareJID);
+ }
+}
+
+void XMPPRoster::removeContact(const JID& jid) {
+ entries_.erase(JID(jid.toBare()));
+ onJIDRemoved(jid);
+}
+
+bool XMPPRoster::containsJID(const JID& jid) {
+ return entries_.find(JID(jid.toBare())) != entries_.end();
+}
+
+const String& XMPPRoster::getNameForJID(const JID& jid) {
+ return entries_[JID(jid.toBare())].name;
+}
+
+const std::vector<String>& XMPPRoster::getGroupsForJID(const JID& jid) {
+ return entries_[JID(jid.toBare())].groups;
+}
+
+RosterItemPayload::Subscription XMPPRoster::getSubscriptionStateForJID(const JID& jid) {
+ return entries_[JID(jid.toBare())].subscription;
+}
+
+}
+
diff --git a/Swiften/Roster/XMPPRoster.h b/Swiften/Roster/XMPPRoster.h
new file mode 100644
index 0000000..47326c3
--- /dev/null
+++ b/Swiften/Roster/XMPPRoster.h
@@ -0,0 +1,44 @@
+#ifndef SWIFTEN_XMPPRoster_H
+#define SWIFTEN_XMPPRoster_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/RosterItemPayload.h"
+
+#include <map>
+#include <vector>
+#include <boost/signal.hpp>
+
+namespace Swift {
+
+struct XMPPRosterItem {
+ JID jid;
+ String name;
+ std::vector<String> groups;
+ RosterItemPayload::Subscription subscription;
+};
+
+class XMPPRoster {
+ public:
+ XMPPRoster() {};
+ ~XMPPRoster() {};
+
+ void addContact(const JID& jid, const String& name, const std::vector<String>& groups, const RosterItemPayload::Subscription subscription);
+ bool containsJID(const JID& jid);
+ void removeContact(const JID& jid);
+ RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid);
+ const String& getNameForJID(const JID& jid);
+ const std::vector<String>& getGroupsForJID(const JID& jid);
+
+ boost::signal<void (const JID&)> onJIDAdded;
+ boost::signal<void (const JID&)> onJIDRemoved;
+ boost::signal<void (const JID&, const String&, const std::vector<String>&)> onJIDUpdated;
+
+ private:
+ //std::map<JID, std::pair<String, std::vector<String> > > entries_;
+ std::map<JID, XMPPRosterItem> entries_;
+};
+}
+
+#endif
+
diff --git a/Swiften/SASL/ClientAuthenticator.cpp b/Swiften/SASL/ClientAuthenticator.cpp
new file mode 100644
index 0000000..5fc9e85
--- /dev/null
+++ b/Swiften/SASL/ClientAuthenticator.cpp
@@ -0,0 +1,11 @@
+#include "Swiften/SASL/ClientAuthenticator.h"
+
+namespace Swift {
+
+ClientAuthenticator::ClientAuthenticator(const String& name) : name(name) {
+}
+
+ClientAuthenticator::~ClientAuthenticator() {
+}
+
+}
diff --git a/Swiften/SASL/ClientAuthenticator.h b/Swiften/SASL/ClientAuthenticator.h
new file mode 100644
index 0000000..f42a51e
--- /dev/null
+++ b/Swiften/SASL/ClientAuthenticator.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class ClientAuthenticator {
+ public:
+ ClientAuthenticator(const String& name);
+ virtual ~ClientAuthenticator();
+
+ const String& getName() const {
+ return name;
+ }
+
+ void setCredentials(const String& authcid, const String& password, const String& authzid = String()) {
+ this->authcid = authcid;
+ this->password = password;
+ this->authzid = authzid;
+ }
+
+ virtual ByteArray getResponse() const = 0;
+ virtual bool setChallenge(const ByteArray&) = 0;
+
+ const String& getAuthenticationID() const {
+ return authcid;
+ }
+
+ const String& getAuthorizationID() const {
+ return authzid;
+ }
+
+ const String& getPassword() const {
+ return password;
+ }
+
+ private:
+ String name;
+ String authcid;
+ String password;
+ String authzid;
+ };
+}
diff --git a/Swiften/SASL/PLAINClientAuthenticator.cpp b/Swiften/SASL/PLAINClientAuthenticator.cpp
new file mode 100644
index 0000000..8f88c3c
--- /dev/null
+++ b/Swiften/SASL/PLAINClientAuthenticator.cpp
@@ -0,0 +1,16 @@
+#include "Swiften/SASL/PLAINClientAuthenticator.h"
+
+namespace Swift {
+
+PLAINClientAuthenticator::PLAINClientAuthenticator() : ClientAuthenticator("PLAIN") {
+}
+
+ByteArray PLAINClientAuthenticator::getResponse() const {
+ return ByteArray(getAuthorizationID()) + '\0' + ByteArray(getAuthenticationID()) + '\0' + ByteArray(getPassword());
+}
+
+bool PLAINClientAuthenticator::setChallenge(const ByteArray&) {
+ return true;
+}
+
+}
diff --git a/Swiften/SASL/PLAINClientAuthenticator.h b/Swiften/SASL/PLAINClientAuthenticator.h
new file mode 100644
index 0000000..854eb30
--- /dev/null
+++ b/Swiften/SASL/PLAINClientAuthenticator.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/SASL/ClientAuthenticator.h"
+
+namespace Swift {
+ class PLAINClientAuthenticator : public ClientAuthenticator {
+ public:
+ PLAINClientAuthenticator();
+
+ virtual ByteArray getResponse() const;
+ virtual bool setChallenge(const ByteArray&);
+ };
+}
diff --git a/Swiften/SASL/PLAINMessage.cpp b/Swiften/SASL/PLAINMessage.cpp
new file mode 100644
index 0000000..66f8cd0
--- /dev/null
+++ b/Swiften/SASL/PLAINMessage.cpp
@@ -0,0 +1,38 @@
+#include "Swiften/SASL/PLAINMessage.h"
+
+namespace Swift {
+
+PLAINMessage::PLAINMessage(const String& authcid, const String& password, const String& authzid) : authcid(authcid), authzid(authzid), password(password) {
+}
+
+PLAINMessage::PLAINMessage(const ByteArray& value) {
+ size_t i = 0;
+ while (i < value.getSize() && value[i] != '\0') {
+ authzid += value[i];
+ ++i;
+ }
+ if (i == value.getSize()) {
+ return;
+ }
+ ++i;
+ while (i < value.getSize() && value[i] != '\0') {
+ authcid += value[i];
+ ++i;
+ }
+ if (i == value.getSize()) {
+ authcid = "";
+ return;
+ }
+ ++i;
+ while (i < value.getSize()) {
+ password += value[i];
+ ++i;
+ }
+}
+
+ByteArray PLAINMessage::getValue() const {
+ String s = authzid + '\0' + authcid + '\0' + password;
+ return ByteArray(s.getUTF8Data(), s.getUTF8Size());
+}
+
+}
diff --git a/Swiften/SASL/PLAINMessage.h b/Swiften/SASL/PLAINMessage.h
new file mode 100644
index 0000000..dd5e2ee
--- /dev/null
+++ b/Swiften/SASL/PLAINMessage.h
@@ -0,0 +1,33 @@
+// TODO: Get rid of this
+//
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class PLAINMessage {
+ public:
+ PLAINMessage(const String& authcid, const String& password, const String& authzid = "");
+ PLAINMessage(const ByteArray& value);
+
+ ByteArray getValue() const;
+
+ const String& getAuthenticationID() const {
+ return authcid;
+ }
+
+ const String& getPassword() const {
+ return password;
+ }
+
+ const String& getAuthorizationID() const {
+ return authzid;
+ }
+
+ private:
+ String authcid;
+ String authzid;
+ String password;
+ };
+}
diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
new file mode 100644
index 0000000..5dc924e
--- /dev/null
+++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
@@ -0,0 +1,143 @@
+#include "Swiften/SASL/SCRAMSHA1ClientAuthenticator.h"
+
+#include <cassert>
+#include <map>
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/StringCodecs/SHA1.h"
+#include "Swiften/StringCodecs/Base64.h"
+#include "Swiften/StringCodecs/HMACSHA1.h"
+#include "Swiften/StringCodecs/PBKDF2.h"
+#include "Swiften/StringPrep/StringPrep.h"
+
+namespace Swift {
+
+static String escape(const String& s) {
+ String result;
+ for (size_t i = 0; i < s.getUTF8Size(); ++i) {
+ if (s[i] == ',') {
+ result += "=2C";
+ }
+ else if (s[i] == '=') {
+ result += "=3D";
+ }
+ else {
+ result += s[i];
+ }
+ }
+ return result;
+}
+
+
+SCRAMSHA1ClientAuthenticator::SCRAMSHA1ClientAuthenticator(const String& nonce) : ClientAuthenticator("SCRAM-SHA-1"), step(Initial), clientnonce(nonce) {
+}
+
+ByteArray SCRAMSHA1ClientAuthenticator::getResponse() const {
+ if (step == Initial) {
+ return getGS2Header() + getInitialBareClientMessage();
+ }
+ else if (step == Proof) {
+ ByteArray clientKey = HMACSHA1::getResult(saltedPassword, "Client Key");
+ ByteArray storedKey = SHA1::getHash(clientKey);
+ ByteArray clientSignature = HMACSHA1::getResult(storedKey, authMessage);
+ ByteArray clientProof = clientKey;
+ for (unsigned int i = 0; i < clientProof.getSize(); ++i) {
+ clientProof[i] ^= clientSignature[i];
+ }
+ ByteArray result = ByteArray("c=") + Base64::encode(getGS2Header()) + ",r=" + clientnonce + serverNonce + ",p=" + Base64::encode(clientProof);
+ return result;
+ }
+ else {
+ return ByteArray();
+ }
+}
+
+bool SCRAMSHA1ClientAuthenticator::setChallenge(const ByteArray& challenge) {
+ if (step == Initial) {
+ initialServerMessage = challenge;
+
+ std::map<char, String> keys = parseMap(String(initialServerMessage.getData(), initialServerMessage.getSize()));
+
+ // Extract the salt
+ ByteArray salt = Base64::decode(keys['s']);
+
+ // Extract the server nonce
+ String clientServerNonce = keys['r'];
+ if (clientServerNonce.getUTF8Size() <= clientnonce.getUTF8Size()) {
+ return false;
+ }
+ String receivedClientNonce = clientServerNonce.getSubstring(0, clientnonce.getUTF8Size());
+ if (receivedClientNonce != clientnonce) {
+ return false;
+ }
+ serverNonce = clientServerNonce.getSubstring(clientnonce.getUTF8Size(), clientServerNonce.npos());
+
+ // Extract the number of iterations
+ int iterations = 0;
+ try {
+ iterations = boost::lexical_cast<int>(keys['i'].getUTF8String());
+ }
+ catch (const boost::bad_lexical_cast&) {
+ return false;
+ }
+ if (iterations <= 0) {
+ return false;
+ }
+
+ // Compute all the values needed for the server signature
+ saltedPassword = PBKDF2::encode(StringPrep::getPrepared(getPassword(), StringPrep::SASLPrep), salt, iterations);
+ authMessage = getInitialBareClientMessage() + "," + initialServerMessage + "," + "c=" + Base64::encode(getGS2Header()) + ",r=" + clientnonce + serverNonce;
+ ByteArray serverKey = HMACSHA1::getResult(saltedPassword, "Server Key");
+ serverSignature = HMACSHA1::getResult(serverKey, authMessage);
+
+ step = Proof;
+ return true;
+ }
+ else if (step == Proof) {
+ ByteArray result = ByteArray("v=") + ByteArray(Base64::encode(serverSignature));
+ step = Final;
+ return challenge == result;
+ }
+ else {
+ return true;
+ }
+}
+
+std::map<char, String> SCRAMSHA1ClientAuthenticator::parseMap(const String& s) {
+ std::map<char, String> result;
+ if (s.getUTF8Size() > 0) {
+ char key;
+ String value;
+ size_t i = 0;
+ bool expectKey = true;
+ while (i < s.getUTF8Size()) {
+ if (expectKey) {
+ key = s[i];
+ expectKey = false;
+ i++;
+ }
+ else if (s[i] == ',') {
+ result[key] = value;
+ value = "";
+ expectKey = true;
+ }
+ else {
+ value += s[i];
+ }
+ i++;
+ }
+ result[key] = value;
+ }
+ return result;
+}
+
+ByteArray SCRAMSHA1ClientAuthenticator::getInitialBareClientMessage() const {
+ String authenticationID = StringPrep::getPrepared(getAuthenticationID(), StringPrep::SASLPrep);
+ return ByteArray(String("n=" + escape(authenticationID) + ",r=" + clientnonce));
+}
+
+ByteArray SCRAMSHA1ClientAuthenticator::getGS2Header() const {
+ return ByteArray("n,") + (getAuthorizationID().isEmpty() ? "" : "a=" + escape(getAuthorizationID())) + ",";
+}
+
+}
diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
new file mode 100644
index 0000000..3d28014
--- /dev/null
+++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <map>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/SASL/ClientAuthenticator.h"
+
+namespace Swift {
+ class SCRAMSHA1ClientAuthenticator : public ClientAuthenticator {
+ public:
+ SCRAMSHA1ClientAuthenticator(const String& nonce);
+
+ virtual ByteArray getResponse() const;
+ virtual bool setChallenge(const ByteArray&);
+
+ private:
+ ByteArray getInitialBareClientMessage() const;
+ ByteArray getGS2Header() const;
+
+ static std::map<char, String> parseMap(const String&);
+
+ private:
+ enum Step {
+ Initial,
+ Proof,
+ Final
+ } step;
+ String clientnonce;
+ ByteArray initialServerMessage;
+ ByteArray serverNonce;
+ ByteArray authMessage;
+ ByteArray saltedPassword;
+ ByteArray serverSignature;
+ };
+}
diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript
new file mode 100644
index 0000000..22b242e
--- /dev/null
+++ b/Swiften/SASL/SConscript
@@ -0,0 +1,12 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"])
+
+objects = myenv.StaticObject([
+ "ClientAuthenticator.cpp",
+ "PLAINClientAuthenticator.cpp",
+ "PLAINMessage.cpp",
+ "SCRAMSHA1ClientAuthenticator.cpp",
+ ])
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp
new file mode 100644
index 0000000..e92cd34
--- /dev/null
+++ b/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp
@@ -0,0 +1,32 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/SASL/PLAINClientAuthenticator.h"
+
+using namespace Swift;
+
+class PLAINClientAuthenticatorTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PLAINClientAuthenticatorTest);
+ CPPUNIT_TEST(testGetResponse_WithoutAuthzID);
+ CPPUNIT_TEST(testGetResponse_WithAuthzID);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetResponse_WithoutAuthzID() {
+ PLAINClientAuthenticator testling;
+
+ testling.setCredentials("user", "pass");
+
+ CPPUNIT_ASSERT_EQUAL(testling.getResponse(), ByteArray("\0user\0pass", 10));
+ }
+
+ void testGetResponse_WithAuthzID() {
+ PLAINClientAuthenticator testling;
+
+ testling.setCredentials("user", "pass", "authz");
+
+ CPPUNIT_ASSERT_EQUAL(testling.getResponse(), ByteArray("authz\0user\0pass", 15));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PLAINClientAuthenticatorTest);
diff --git a/Swiften/SASL/UnitTest/PLAINMessageTest.cpp b/Swiften/SASL/UnitTest/PLAINMessageTest.cpp
new file mode 100644
index 0000000..6493bd5
--- /dev/null
+++ b/Swiften/SASL/UnitTest/PLAINMessageTest.cpp
@@ -0,0 +1,61 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/SASL/PLAINMessage.h"
+
+using namespace Swift;
+
+class PLAINMessageTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PLAINMessageTest);
+ CPPUNIT_TEST(testGetValue_WithoutAuthzID);
+ CPPUNIT_TEST(testGetValue_WithAuthzID);
+ CPPUNIT_TEST(testConstructor_WithoutAuthzID);
+ CPPUNIT_TEST(testConstructor_WithAuthzID);
+ CPPUNIT_TEST(testConstructor_NoAuthcid);
+ CPPUNIT_TEST(testConstructor_NoPassword);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PLAINMessageTest() {}
+
+ void testGetValue_WithoutAuthzID() {
+ PLAINMessage message("user", "pass");
+ CPPUNIT_ASSERT_EQUAL(message.getValue(), ByteArray("\0user\0pass", 10));
+ }
+
+ void testGetValue_WithAuthzID() {
+ PLAINMessage message("user", "pass", "authz");
+ CPPUNIT_ASSERT_EQUAL(message.getValue(), ByteArray("authz\0user\0pass", 15));
+ }
+
+ void testConstructor_WithoutAuthzID() {
+ PLAINMessage message(ByteArray("\0user\0pass", 10));
+
+ CPPUNIT_ASSERT_EQUAL(String(""), message.getAuthorizationID());
+ CPPUNIT_ASSERT_EQUAL(String("user"), message.getAuthenticationID());
+ CPPUNIT_ASSERT_EQUAL(String("pass"), message.getPassword());
+ }
+
+ void testConstructor_WithAuthzID() {
+ PLAINMessage message(ByteArray("authz\0user\0pass", 15));
+
+ CPPUNIT_ASSERT_EQUAL(String("authz"), message.getAuthorizationID());
+ CPPUNIT_ASSERT_EQUAL(String("user"), message.getAuthenticationID());
+ CPPUNIT_ASSERT_EQUAL(String("pass"), message.getPassword());
+ }
+
+ void testConstructor_NoAuthcid() {
+ PLAINMessage message(ByteArray("authzid", 7));
+
+ CPPUNIT_ASSERT_EQUAL(String(""), message.getAuthenticationID());
+ }
+
+ void testConstructor_NoPassword() {
+ PLAINMessage message(ByteArray("authzid\0authcid", 15));
+
+ CPPUNIT_ASSERT_EQUAL(String(""), message.getAuthenticationID());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PLAINMessageTest);
diff --git a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
new file mode 100644
index 0000000..db69d13
--- /dev/null
+++ b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
@@ -0,0 +1,172 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/SASL/SCRAMSHA1ClientAuthenticator.h"
+#include "Swiften/Base/ByteArray.h"
+
+using namespace Swift;
+
+class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SCRAMSHA1ClientAuthenticatorTest);
+ CPPUNIT_TEST(testGetInitialResponse);
+ CPPUNIT_TEST(testGetInitialResponse_UsernameHasSpecialChars);
+ CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationID);
+ CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationIDWithSpecialChars);
+ CPPUNIT_TEST(testGetFinalResponse);
+ CPPUNIT_TEST(testSetChallenge);
+ CPPUNIT_TEST(testSetChallenge_InvalidClientNonce);
+ CPPUNIT_TEST(testSetChallenge_OnlyClientNonce);
+ CPPUNIT_TEST(testSetChallenge_InvalidIterations);
+ CPPUNIT_TEST(testSetChallenge_ZeroIterations);
+ CPPUNIT_TEST(testSetChallenge_NegativeIterations);
+ CPPUNIT_TEST(testSetChallenge_MissingIterations);
+ CPPUNIT_TEST(testSetFinalChallenge);
+ CPPUNIT_TEST(testSetFinalChallenge_InvalidChallenge);
+ CPPUNIT_TEST(testGetResponseAfterFinalChallenge);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ }
+
+ void testGetInitialResponse() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH");
+ testling.setCredentials("user", "pass", "");
+
+ ByteArray response = testling.getResponse();
+
+ CPPUNIT_ASSERT_EQUAL(String("n,,n=user,r=abcdefghABCDEFGH"), testling.getResponse().toString());
+ }
+
+ void testGetInitialResponse_UsernameHasSpecialChars() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH");
+ testling.setCredentials(",us=,er=", "pass", "");
+
+ ByteArray response = testling.getResponse();
+
+ CPPUNIT_ASSERT_EQUAL(String("n,,n==2Cus=3D=2Cer=3D,r=abcdefghABCDEFGH"), testling.getResponse().toString());
+ }
+
+ void testGetInitialResponse_WithAuthorizationID() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH");
+ testling.setCredentials("user", "pass", "auth");
+
+ ByteArray response = testling.getResponse();
+
+ CPPUNIT_ASSERT_EQUAL(String("n,a=auth,n=user,r=abcdefghABCDEFGH"), testling.getResponse().toString());
+ }
+
+ void testGetInitialResponse_WithAuthorizationIDWithSpecialChars() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH");
+ testling.setCredentials("user", "pass", "a=u,th");
+
+ ByteArray response = testling.getResponse();
+
+ CPPUNIT_ASSERT_EQUAL(String("n,a=a=3Du=2Cth,n=user,r=abcdefghABCDEFGH"), testling.getResponse().toString());
+ }
+
+ void testGetFinalResponse() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+ testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+
+ ByteArray response = testling.getResponse();
+
+ CPPUNIT_ASSERT_EQUAL(String("c=biws,r=abcdefghABCDEFGH,p=CZbjGDpIteIJwQNBgO0P8pKkMGY="), testling.getResponse().toString());
+ }
+
+ void testSetFinalChallenge() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+ testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+
+ bool result = testling.setChallenge(ByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo="));
+
+ CPPUNIT_ASSERT(result);
+ }
+
+ void testSetChallenge() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+
+ CPPUNIT_ASSERT(result);
+ }
+
+ void testSetChallenge_InvalidClientNonce() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefgiABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testSetChallenge_OnlyClientNonce() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefgh,s=MTIzNDU2NzgK,i=4096"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testSetChallenge_InvalidIterations() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=bla"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testSetChallenge_MissingIterations() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testSetChallenge_ZeroIterations() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=0"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testSetChallenge_NegativeIterations() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+
+ bool result = testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=-1"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testSetFinalChallenge_InvalidChallenge() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+ testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+ bool result = testling.setChallenge(ByteArray("v=e26kI69ICb6zosapLLxrER/631A="));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testGetResponseAfterFinalChallenge() {
+ SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+ testling.setCredentials("user", "pass", "");
+ testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+ testling.setChallenge(ByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo="));
+
+ ByteArray result = testling.getResponse();
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray(), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SCRAMSHA1ClientAuthenticatorTest);
diff --git a/Swiften/SConscript b/Swiften/SConscript
new file mode 100644
index 0000000..82c75d5
--- /dev/null
+++ b/Swiften/SConscript
@@ -0,0 +1,221 @@
+Import("env")
+
+################################################################################
+# Flags
+################################################################################
+
+if env["SCONS_STAGE"] == "flags" :
+ env["SWIFTEN_FLAGS"] = {
+ "LIBPATH": [Dir(".")],
+ "LIBS": ["Swiften"]
+ }
+
+################################################################################
+# Build
+################################################################################
+
+if env["SCONS_STAGE"] == "build" :
+ swiften_env = env.Clone()
+ swiften_env.MergeFlags(swiften_env["BOOST_FLAGS"])
+ Export("swiften_env")
+
+# TODO: Move all this to a submodule SConscript
+ myenv = swiften_env.Clone()
+ myenv.MergeFlags(myenv["ZLIB_FLAGS"])
+ myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
+ sources = [
+ "Avatars/AvatarFileStorage.cpp",
+ "Avatars/AvatarManager.cpp",
+ "Avatars/AvatarStorage.cpp",
+ "Chat/ChatStateTracker.cpp",
+ "Chat/ChatStateNotifier.cpp",
+ "Chat/ChatStateMessageSender.cpp",
+ "Client/Client.cpp",
+ "Client/ClientSession.cpp",
+ "Compress/ZLibCodecompressor.cpp",
+ "Disco/CapsInfoGenerator.cpp",
+ "Elements/DiscoInfo.cpp",
+ "Elements/Element.cpp",
+ "Elements/IQ.cpp",
+ "Elements/Payload.cpp",
+ "Elements/RosterPayload.cpp",
+ "Elements/Stanza.cpp",
+ "MUC/MUC.cpp",
+ "MUC/MUCOccupant.cpp",
+ "MUC/MUCRegistry.cpp",
+ "MUC/MUCBookmarkManager.cpp",
+ "Notifier/Notifier.cpp",
+ "Presence/PresenceOracle.cpp",
+ "Presence/PresenceSender.cpp",
+ "Queries/IQChannel.cpp",
+ "Queries/IQHandler.cpp",
+ "Queries/IQRouter.cpp",
+ "Queries/Request.cpp",
+ "Queries/Responders/DiscoInfoResponder.cpp",
+ "Queries/Responders/SoftwareVersionResponder.cpp",
+ "Roster/ContactRosterItem.cpp",
+ "Roster/Roster.cpp",
+ "Roster/XMPPRoster.cpp",
+ "Serializer/AuthRequestSerializer.cpp",
+ "Serializer/AuthSuccessSerializer.cpp",
+ "Serializer/AuthChallengeSerializer.cpp",
+ "Serializer/AuthResponseSerializer.cpp",
+ "Serializer/CompressRequestSerializer.cpp",
+ "Serializer/ElementSerializer.cpp",
+ "Serializer/MessageSerializer.cpp",
+ "Serializer/PayloadSerializer.cpp",
+ "Serializer/PayloadSerializerCollection.cpp",
+ "Serializer/PayloadSerializers/CapsInfoSerializer.cpp",
+ "Serializer/PayloadSerializers/ChatStateSerializer.cpp",
+ "Serializer/PayloadSerializers/DiscoInfoSerializer.cpp",
+ "Serializer/PayloadSerializers/ErrorSerializer.cpp",
+ "Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp",
+ "Serializer/PayloadSerializers/MUCPayloadSerializer.cpp",
+ "Serializer/PayloadSerializers/ResourceBindSerializer.cpp",
+ "Serializer/PayloadSerializers/RosterSerializer.cpp",
+ "Serializer/PayloadSerializers/SecurityLabelSerializer.cpp",
+ "Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp",
+ "Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp",
+ "Serializer/PayloadSerializers/VCardSerializer.cpp",
+ "Serializer/PayloadSerializers/VCardUpdateSerializer.cpp",
+ "Serializer/PayloadSerializers/StorageSerializer.cpp",
+ "Serializer/PayloadSerializers/PrivateStorageSerializer.cpp",
+ "Serializer/PresenceSerializer.cpp",
+ "Serializer/StanzaSerializer.cpp",
+ "Serializer/StreamFeaturesSerializer.cpp",
+ "Serializer/XML/XMLElement.cpp",
+ "Serializer/XML/XMLNode.cpp",
+ "Serializer/XMPPSerializer.cpp",
+ "Server/ServerFromClientSession.cpp",
+ "Server/ServerSession.cpp",
+ "Server/ServerStanzaRouter.cpp",
+ "Server/SimpleUserRegistry.cpp",
+ "Server/UserRegistry.cpp",
+ "Session/Session.cpp",
+ "Session/SessionStream.cpp",
+ "Session/BasicSessionStream.cpp",
+ "StringCodecs/Base64.cpp",
+ "StringCodecs/SHA1.cpp",
+ "StringCodecs/HMACSHA1.cpp",
+ "StringCodecs/MD5.cpp",
+ "StringCodecs/PBKDF2.cpp",
+ "StringCodecs/Hexify.cpp",
+ ]
+# "Notifier/GrowlNotifier.cpp",
+
+ if myenv.get("HAVE_OPENSSL", 0) :
+ sources += ["TLS/OpenSSL/OpenSSLContext.cpp"]
+
+ SConscript(dirs = [
+ "Base",
+ "StringPrep",
+ "SASL",
+ "Application",
+ "EventLoop",
+ "Parser",
+ "JID",
+ "Network",
+ "History",
+ "StreamStack",
+ "LinkLocal",
+ ])
+ SConscript(test_only = True, dirs = [
+ "QA",
+ ])
+ SConscript(dirs = [
+ "Examples"
+ ])
+
+ myenv.StaticLibrary("Swiften", sources + swiften_env["SWIFTEN_OBJECTS"])
+
+ env.Append(UNITTEST_SOURCES = [
+ File("Application/UnitTest/ApplicationTest.cpp"),
+ File("Avatars/UnitTest/MockAvatarManager.cpp"),
+ File("Base/UnitTest/IDGeneratorTest.cpp"),
+ File("Base/UnitTest/StringTest.cpp"),
+ File("Base/UnitTest/ByteArrayTest.cpp"),
+ File("Chat/UnitTest/ChatStateNotifierTest.cpp"),
+# File("Chat/UnitTest/ChatStateTrackerTest.cpp"),
+ File("Client/UnitTest/ClientSessionTest.cpp"),
+ File("Compress/UnitTest/ZLibCompressorTest.cpp"),
+ File("Compress/UnitTest/ZLibDecompressorTest.cpp"),
+ File("Disco/UnitTest/CapsInfoGeneratorTest.cpp"),
+ File("Elements/UnitTest/IQTest.cpp"),
+ File("Elements/UnitTest/StanzaTest.cpp"),
+ File("Elements/UnitTest/StanzasTest.cpp"),
+ File("EventLoop/UnitTest/EventLoopTest.cpp"),
+ File("EventLoop/UnitTest/SimpleEventLoopTest.cpp"),
+ File("History/UnitTest/SQLiteHistoryManagerTest.cpp"),
+ File("JID/UnitTest/JIDTest.cpp"),
+ File("LinkLocal/UnitTest/LinkLocalConnectorTest.cpp"),
+ File("LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp"),
+ File("LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp"),
+ File("LinkLocal/UnitTest/LinkLocalServiceTest.cpp"),
+ File("Network/UnitTest/HostAddressTest.cpp"),
+ File("Network/UnitTest/ConnectorTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/StatusParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/VCardParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/StorageParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp"),
+ File("Parser/UnitTest/AttributeMapTest.cpp"),
+ File("Parser/UnitTest/IQParserTest.cpp"),
+ File("Parser/UnitTest/MessageParserTest.cpp"),
+ File("Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp"),
+ File("Parser/UnitTest/PresenceParserTest.cpp"),
+ File("Parser/UnitTest/SerializingParserTest.cpp"),
+ File("Parser/UnitTest/StanzaParserTest.cpp"),
+ File("Parser/UnitTest/StreamFeaturesParserTest.cpp"),
+ File("Parser/UnitTest/XMLParserTest.cpp"),
+ File("Parser/UnitTest/XMPPParserTest.cpp"),
+ File("Presence/UnitTest/PresenceOracleTest.cpp"),
+ File("Presence/UnitTest/PresenceSenderTest.cpp"),
+ File("Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp"),
+ File("Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp"),
+ File("Queries/UnitTest/IQRouterTest.cpp"),
+ File("Queries/UnitTest/RequestTest.cpp"),
+ File("Queries/UnitTest/ResponderTest.cpp"),
+ File("Roster/UnitTest/OfflineRosterFilterTest.cpp"),
+ File("Roster/UnitTest/RosterTest.cpp"),
+ File("Roster/UnitTest/XMPPRosterTest.cpp"),
+ File("SASL/UnitTest/PLAINMessageTest.cpp"),
+ File("SASL/UnitTest/PLAINClientAuthenticatorTest.cpp"),
+ File("SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"),
+ File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),
+ File("Serializer/XML/UnitTest/XMLElementTest.cpp"),
+ File("Server/UnitTest/ServerStanzaRouterTest.cpp"),
+ File("StreamStack/UnitTest/StreamStackTest.cpp"),
+ File("StreamStack/UnitTest/XMPPLayerTest.cpp"),
+ File("StringCodecs/UnitTest/Base64Test.cpp"),
+ File("StringCodecs/UnitTest/SHA1Test.cpp"),
+ File("StringCodecs/UnitTest/MD5Test.cpp"),
+ File("StringCodecs/UnitTest/HexifyTest.cpp"),
+ File("StringCodecs/UnitTest/HMACSHA1Test.cpp"),
+ File("StringCodecs/UnitTest/PBKDF2Test.cpp"),
+ ])
diff --git a/Swiften/Serializer/AuthChallengeSerializer.cpp b/Swiften/Serializer/AuthChallengeSerializer.cpp
new file mode 100644
index 0000000..152607d
--- /dev/null
+++ b/Swiften/Serializer/AuthChallengeSerializer.cpp
@@ -0,0 +1,17 @@
+#include "Swiften/Serializer/AuthChallengeSerializer.h"
+
+#include "Swiften/Elements/AuthChallenge.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthChallengeSerializer::AuthChallengeSerializer() {
+}
+
+String AuthChallengeSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<AuthChallenge> authRequest(boost::dynamic_pointer_cast<AuthChallenge>(element));
+ String value = (authRequest->getValue().isEmpty() ? "=" : Base64::encode(authRequest->getValue()));
+ return "<challenge xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">" + value + "</challenge>";
+}
+
+}
diff --git a/Swiften/Serializer/AuthChallengeSerializer.h b/Swiften/Serializer/AuthChallengeSerializer.h
new file mode 100644
index 0000000..010d9a9
--- /dev/null
+++ b/Swiften/Serializer/AuthChallengeSerializer.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/AuthChallenge.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+
+namespace Swift {
+ class AuthChallengeSerializer : public GenericElementSerializer<AuthChallenge> {
+ public:
+ AuthChallengeSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const;
+ };
+}
diff --git a/Swiften/Serializer/AuthFailureSerializer.h b/Swiften/Serializer/AuthFailureSerializer.h
new file mode 100644
index 0000000..e7e2ced
--- /dev/null
+++ b/Swiften/Serializer/AuthFailureSerializer.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_AuthFailureSerializer_H
+#define SWIFTEN_AuthFailureSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/AuthFailure.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class AuthFailureSerializer : public GenericElementSerializer<AuthFailure> {
+ public:
+ AuthFailureSerializer() : GenericElementSerializer<AuthFailure>() {
+ }
+
+ virtual String serialize(boost::shared_ptr<Element>) const {
+ return XMLElement("failure", "urn:ietf:params:xml:ns:xmpp-sasl").serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/AuthRequestSerializer.cpp b/Swiften/Serializer/AuthRequestSerializer.cpp
new file mode 100644
index 0000000..7122485
--- /dev/null
+++ b/Swiften/Serializer/AuthRequestSerializer.cpp
@@ -0,0 +1,17 @@
+#include "Swiften/Serializer/AuthRequestSerializer.h"
+
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthRequestSerializer::AuthRequestSerializer() {
+}
+
+String AuthRequestSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<AuthRequest> authRequest(boost::dynamic_pointer_cast<AuthRequest>(element));
+ String value = (authRequest->getMessage().isEmpty() ? "=" : Base64::encode(authRequest->getMessage()));
+ return "<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"" + authRequest->getMechanism() + "\">" + value + "</auth>";
+}
+
+}
diff --git a/Swiften/Serializer/AuthRequestSerializer.h b/Swiften/Serializer/AuthRequestSerializer.h
new file mode 100644
index 0000000..b5131a3
--- /dev/null
+++ b/Swiften/Serializer/AuthRequestSerializer.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_AuthRequestSerializer_H
+#define SWIFTEN_AuthRequestSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+
+namespace Swift {
+ class AuthRequestSerializer : public GenericElementSerializer<AuthRequest> {
+ public:
+ AuthRequestSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/AuthResponseSerializer.cpp b/Swiften/Serializer/AuthResponseSerializer.cpp
new file mode 100644
index 0000000..2348a3d
--- /dev/null
+++ b/Swiften/Serializer/AuthResponseSerializer.cpp
@@ -0,0 +1,17 @@
+#include "Swiften/Serializer/AuthResponseSerializer.h"
+
+#include "Swiften/Elements/AuthResponse.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthResponseSerializer::AuthResponseSerializer() {
+}
+
+String AuthResponseSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<AuthResponse> authRequest(boost::dynamic_pointer_cast<AuthResponse>(element));
+ String value = (authRequest->getValue().isEmpty() ? "=" : Base64::encode(authRequest->getValue()));
+ return "<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">" + value + "</response>";
+}
+
+}
diff --git a/Swiften/Serializer/AuthResponseSerializer.h b/Swiften/Serializer/AuthResponseSerializer.h
new file mode 100644
index 0000000..8d47291
--- /dev/null
+++ b/Swiften/Serializer/AuthResponseSerializer.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/AuthResponse.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+
+namespace Swift {
+ class AuthResponseSerializer : public GenericElementSerializer<AuthResponse> {
+ public:
+ AuthResponseSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const;
+ };
+}
diff --git a/Swiften/Serializer/AuthSuccessSerializer.cpp b/Swiften/Serializer/AuthSuccessSerializer.cpp
new file mode 100644
index 0000000..6d7f195
--- /dev/null
+++ b/Swiften/Serializer/AuthSuccessSerializer.cpp
@@ -0,0 +1,17 @@
+#include "Swiften/Serializer/AuthSuccessSerializer.h"
+
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+AuthSuccessSerializer::AuthSuccessSerializer() {
+}
+
+String AuthSuccessSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<AuthSuccess> authRequest(boost::dynamic_pointer_cast<AuthSuccess>(element));
+ String value = (authRequest->getValue().isEmpty() ? "=" : Base64::encode(authRequest->getValue()));
+ return "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">" + value + "</challenge>";
+}
+
+}
diff --git a/Swiften/Serializer/AuthSuccessSerializer.h b/Swiften/Serializer/AuthSuccessSerializer.h
new file mode 100644
index 0000000..6ced772
--- /dev/null
+++ b/Swiften/Serializer/AuthSuccessSerializer.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+
+namespace Swift {
+ class AuthSuccessSerializer : public GenericElementSerializer<AuthSuccess> {
+ public:
+ AuthSuccessSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const;
+ };
+}
diff --git a/Swiften/Serializer/CompressFailureSerializer.h b/Swiften/Serializer/CompressFailureSerializer.h
new file mode 100644
index 0000000..c3e7953
--- /dev/null
+++ b/Swiften/Serializer/CompressFailureSerializer.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_CompressFailureSerializer_H
+#define SWIFTEN_CompressFailureSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/CompressFailure.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class CompressFailureSerializer : public GenericElementSerializer<CompressFailure> {
+ public:
+ CompressFailureSerializer() : GenericElementSerializer<CompressFailure>() {
+ }
+
+ virtual String serialize(boost::shared_ptr<Element>) const {
+ return XMLElement("failure", "http://jabber.org/protocol/compress").serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/CompressRequestSerializer.cpp b/Swiften/Serializer/CompressRequestSerializer.cpp
new file mode 100644
index 0000000..4d3310b
--- /dev/null
+++ b/Swiften/Serializer/CompressRequestSerializer.cpp
@@ -0,0 +1,19 @@
+#include "Swiften/Serializer/CompressRequestSerializer.h"
+
+#include "Swiften/Elements/CompressRequest.h"
+
+namespace Swift {
+
+CompressRequestSerializer::CompressRequestSerializer() {
+}
+
+String CompressRequestSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<CompressRequest> compressRequest(boost::dynamic_pointer_cast<CompressRequest>(element));
+ return "<compress xmlns='http://jabber.org/protocol/compress'><method>" + compressRequest->getMethod() + "</method></compress>";
+}
+
+bool CompressRequestSerializer::canSerialize(boost::shared_ptr<Element> element) const {
+ return dynamic_cast<CompressRequest*>(element.get()) != 0;
+}
+
+}
diff --git a/Swiften/Serializer/CompressRequestSerializer.h b/Swiften/Serializer/CompressRequestSerializer.h
new file mode 100644
index 0000000..b6a493c
--- /dev/null
+++ b/Swiften/Serializer/CompressRequestSerializer.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_COMPRESSREQUESTSERIALIZER_H
+#define SWIFTEN_COMPRESSREQUESTSERIALIZER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/ElementSerializer.h"
+
+namespace Swift {
+ class CompressRequestSerializer : public ElementSerializer {
+ public:
+ CompressRequestSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const;
+ virtual bool canSerialize(boost::shared_ptr<Element> element) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/ElementSerializer.cpp b/Swiften/Serializer/ElementSerializer.cpp
new file mode 100644
index 0000000..22c64a6
--- /dev/null
+++ b/Swiften/Serializer/ElementSerializer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Serializer/ElementSerializer.h"
+
+namespace Swift {
+
+ElementSerializer::~ElementSerializer() {
+}
+
+}
diff --git a/Swiften/Serializer/ElementSerializer.h b/Swiften/Serializer/ElementSerializer.h
new file mode 100644
index 0000000..d2f5611
--- /dev/null
+++ b/Swiften/Serializer/ElementSerializer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_ELEMENTSERIALIZER_H
+#define SWIFTEN_ELEMENTSERIALIZER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Element.h"
+
+namespace Swift {
+ class ElementSerializer {
+ public:
+ virtual ~ElementSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const = 0;
+ virtual bool canSerialize(boost::shared_ptr<Element> element) const = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/GenericElementSerializer.h b/Swiften/Serializer/GenericElementSerializer.h
new file mode 100644
index 0000000..7ccecb2
--- /dev/null
+++ b/Swiften/Serializer/GenericElementSerializer.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_GenericElementSerializer_H
+#define SWIFTEN_GenericElementSerializer_H
+
+#include "Swiften/Serializer/ElementSerializer.h"
+
+namespace Swift {
+ template<typename T>
+ class GenericElementSerializer : public ElementSerializer {
+ public:
+ virtual String serialize(boost::shared_ptr<Element> element) const = 0;
+
+ virtual bool canSerialize(boost::shared_ptr<Element> element) const {
+ return dynamic_cast<T*>(element.get()) != 0;
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/GenericPayloadSerializer.h b/Swiften/Serializer/GenericPayloadSerializer.h
new file mode 100644
index 0000000..c4a45c3
--- /dev/null
+++ b/Swiften/Serializer/GenericPayloadSerializer.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_GenericPayloadSerializer_H
+#define SWIFTEN_GenericPayloadSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/PayloadSerializer.h"
+#include "Swiften/Elements/Body.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class GenericPayloadSerializer : public PayloadSerializer {
+ public:
+ virtual String serialize(boost::shared_ptr<Payload> element) const {
+ return serializePayload(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(element));
+ }
+
+ virtual bool canSerialize(boost::shared_ptr<Payload> element) const {
+ return dynamic_cast<PAYLOAD_TYPE*>(element.get());
+ }
+
+ virtual String serializePayload(boost::shared_ptr<PAYLOAD_TYPE>) const = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/GenericStanzaSerializer.h b/Swiften/Serializer/GenericStanzaSerializer.h
new file mode 100644
index 0000000..c59f735
--- /dev/null
+++ b/Swiften/Serializer/GenericStanzaSerializer.h
@@ -0,0 +1,29 @@
+#ifndef SWIFTEN_GENERICSTANZASERIALIZER_H
+#define SWIFTEN_GENERICSTANZASERIALIZER_H
+
+#include "Swiften/Serializer/StanzaSerializer.h"
+
+namespace Swift {
+ template<typename STANZA_TYPE>
+ class GenericStanzaSerializer : public StanzaSerializer {
+ public:
+ GenericStanzaSerializer(const String& tag, PayloadSerializerCollection* payloadSerializers) : StanzaSerializer(tag, payloadSerializers) {}
+
+ virtual bool canSerialize(boost::shared_ptr<Element> element) const {
+ return dynamic_cast<STANZA_TYPE*>(element.get()) != 0;
+ }
+
+ virtual void setStanzaSpecificAttributes(
+ boost::shared_ptr<Element> stanza,
+ XMLElement& element) const {
+ setStanzaSpecificAttributesGeneric(
+ boost::dynamic_pointer_cast<STANZA_TYPE>(stanza), element);
+ }
+
+ virtual void setStanzaSpecificAttributesGeneric(
+ boost::shared_ptr<STANZA_TYPE>,
+ XMLElement&) const = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/IQSerializer.h b/Swiften/Serializer/IQSerializer.h
new file mode 100644
index 0000000..3623f24
--- /dev/null
+++ b/Swiften/Serializer/IQSerializer.h
@@ -0,0 +1,30 @@
+#ifndef SWIFTEN_IQSerializer_H
+#define SWIFTEN_IQSerializer_H
+
+#include <cassert>
+
+#include "Swiften/Serializer/GenericStanzaSerializer.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class IQSerializer : public GenericStanzaSerializer<IQ> {
+ public:
+ IQSerializer(PayloadSerializerCollection* payloadSerializers) :
+ GenericStanzaSerializer<IQ>("iq", payloadSerializers) {}
+
+ private:
+ virtual void setStanzaSpecificAttributesGeneric(
+ boost::shared_ptr<IQ> iq,
+ XMLElement& element) const {
+ switch (iq->getType()) {
+ case IQ::Get: element.setAttribute("type","get"); break;
+ case IQ::Set: element.setAttribute("type","set"); break;
+ case IQ::Result: element.setAttribute("type","result"); break;
+ case IQ::Error: element.setAttribute("type","error"); break;
+ }
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/MessageSerializer.cpp b/Swiften/Serializer/MessageSerializer.cpp
new file mode 100644
index 0000000..a3cf2ad
--- /dev/null
+++ b/Swiften/Serializer/MessageSerializer.cpp
@@ -0,0 +1,27 @@
+#include "Swiften/Serializer/MessageSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+
+MessageSerializer::MessageSerializer(PayloadSerializerCollection* payloadSerializers) :
+ GenericStanzaSerializer<Message>("message", payloadSerializers) {
+}
+
+void MessageSerializer::setStanzaSpecificAttributesGeneric(
+ boost::shared_ptr<Message> message,
+ XMLElement& element) const {
+ if (message->getType() == Message::Chat) {
+ element.setAttribute("type", "chat");
+ }
+ else if (message->getType() == Message::Groupchat) {
+ element.setAttribute("type", "groupchat");
+ }
+ else if (message->getType() == Message::Headline) {
+ element.setAttribute("type", "headline");
+ }
+ else if (message->getType() == Message::Error) {
+ element.setAttribute("type", "error");
+ }
+}
+
+}
diff --git a/Swiften/Serializer/MessageSerializer.h b/Swiften/Serializer/MessageSerializer.h
new file mode 100644
index 0000000..8cb32c7
--- /dev/null
+++ b/Swiften/Serializer/MessageSerializer.h
@@ -0,0 +1,21 @@
+#ifndef SWIFTEN_MessageSerializer_H
+#define SWIFTEN_MessageSerializer_H
+
+#include "Swiften/Serializer/GenericStanzaSerializer.h"
+#include "Swiften/Elements/Message.h"
+
+namespace Swift {
+ class XMLElement;
+
+ class MessageSerializer : public GenericStanzaSerializer<Message> {
+ public:
+ MessageSerializer(PayloadSerializerCollection* payloadSerializers);
+
+ private:
+ void setStanzaSpecificAttributesGeneric(
+ boost::shared_ptr<Message> message,
+ XMLElement& element) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializer.cpp
new file mode 100644
index 0000000..47295ac
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Serializer/PayloadSerializer.h"
+
+namespace Swift {
+
+PayloadSerializer::~PayloadSerializer() {
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializer.h b/Swiften/Serializer/PayloadSerializer.h
new file mode 100644
index 0000000..935ff4b
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_PAYLOADSERIALIZER_H
+#define SWIFTEN_PAYLOADSERIALIZER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class PayloadSerializer {
+ public:
+ virtual ~PayloadSerializer();
+
+ virtual bool canSerialize(boost::shared_ptr<Payload>) const = 0;
+ virtual String serialize(boost::shared_ptr<Payload>) const = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializerCollection.cpp
new file mode 100644
index 0000000..db86eea
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializerCollection.cpp
@@ -0,0 +1,27 @@
+#include <boost/bind.hpp>
+#include <algorithm>
+
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+#include "Swiften/Serializer/PayloadSerializer.h"
+
+namespace Swift {
+
+PayloadSerializerCollection::PayloadSerializerCollection() {
+}
+
+void PayloadSerializerCollection::addSerializer(PayloadSerializer* serializer) {
+ serializers_.push_back(serializer);
+}
+
+void PayloadSerializerCollection::removeSerializer(PayloadSerializer* serializer) {
+ serializers_.erase(remove(serializers_.begin(), serializers_.end(), serializer), serializers_.end());
+}
+
+PayloadSerializer* PayloadSerializerCollection::getPayloadSerializer(boost::shared_ptr<Payload> payload) const {
+ std::vector<PayloadSerializer*>::const_iterator i = std::find_if(
+ serializers_.begin(), serializers_.end(),
+ boost::bind(&PayloadSerializer::canSerialize, _1, payload));
+ return (i != serializers_.end() ? *i : NULL);
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializerCollection.h b/Swiften/Serializer/PayloadSerializerCollection.h
new file mode 100644
index 0000000..63ccabe
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializerCollection.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class PayloadSerializer;
+ class String;
+
+ class PayloadSerializerCollection {
+ public:
+ PayloadSerializerCollection();
+
+ void addSerializer(PayloadSerializer* factory);
+ void removeSerializer(PayloadSerializer* factory);
+ PayloadSerializer* getPayloadSerializer(boost::shared_ptr<Payload>) const;
+
+ private:
+ std::vector<PayloadSerializer*> serializers_;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/BodySerializer.h b/Swiften/Serializer/PayloadSerializers/BodySerializer.h
new file mode 100644
index 0000000..7fba561
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/BodySerializer.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_BodySerializer_H
+#define SWIFTEN_BodySerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Elements/Body.h"
+
+namespace Swift {
+ class BodySerializer : public GenericPayloadSerializer<Body> {
+ public:
+ BodySerializer() : GenericPayloadSerializer<Body>() {}
+
+ virtual String serializePayload(boost::shared_ptr<Body> body) const {
+ XMLTextNode textNode(body->getText());
+ return "<body>" + textNode.serialize() + "</body>";
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.cpp
new file mode 100644
index 0000000..fbb72af
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.cpp
@@ -0,0 +1,20 @@
+#include "Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+
+CapsInfoSerializer::CapsInfoSerializer() : GenericPayloadSerializer<CapsInfo>() {
+}
+
+String CapsInfoSerializer::serializePayload(boost::shared_ptr<CapsInfo> capsInfo) const {
+ XMLElement capsElement("c", "http://jabber.org/protocol/caps");
+ capsElement.setAttribute("node", capsInfo->getNode());
+ capsElement.setAttribute("hash", capsInfo->getHash());
+ capsElement.setAttribute("ver", capsInfo->getVersion());
+ return capsElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h b/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h
new file mode 100644
index 0000000..cb4fa33
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_CapsInfoSerializer_H
+#define SWIFTEN_CapsInfoSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/CapsInfo.h"
+
+namespace Swift {
+ class CapsInfoSerializer : public GenericPayloadSerializer<CapsInfo> {
+ public:
+ CapsInfoSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<CapsInfo>) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp
new file mode 100644
index 0000000..0507092
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h"
+
+namespace Swift {
+
+ChatStateSerializer::ChatStateSerializer() : GenericPayloadSerializer<ChatState>() {
+}
+
+String ChatStateSerializer::serializePayload(boost::shared_ptr<ChatState> chatState) const {
+ String result("<");
+ switch (chatState->getChatState()) {
+ case ChatState::Active: result += "active"; break;
+ case ChatState::Composing: result += "composing"; break;
+ case ChatState::Paused: result += "paused"; break;
+ case ChatState::Inactive: result += "inactive"; break;
+ case ChatState::Gone: result += "gone"; break;
+ default: result += "gone"; break;
+ }
+ result += " xmlns=\"http://jabber.org/protocol/chatstates\"/>";
+ return result;
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h
new file mode 100644
index 0000000..e99b8b6
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/ChatState.h"
+
+namespace Swift {
+ class ChatStateSerializer : public GenericPayloadSerializer<ChatState> {
+ public:
+ ChatStateSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<ChatState> error) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.cpp b/Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.cpp
new file mode 100644
index 0000000..4c39574
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.cpp
@@ -0,0 +1,36 @@
+#include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+
+DiscoInfoSerializer::DiscoInfoSerializer() : GenericPayloadSerializer<DiscoInfo>() {
+}
+
+String DiscoInfoSerializer::serializePayload(boost::shared_ptr<DiscoInfo> discoInfo) const {
+ XMLElement queryElement("query", "http://jabber.org/protocol/disco#info");
+ if (!discoInfo->getNode().isEmpty()) {
+ queryElement.setAttribute("node", discoInfo->getNode());
+ }
+ foreach(const DiscoInfo::Identity& identity, discoInfo->getIdentities()) {
+ boost::shared_ptr<XMLElement> identityElement(new XMLElement("identity"));
+ if (!identity.getLanguage().isEmpty()) {
+ identityElement->setAttribute("xml:lang", identity.getLanguage());
+ }
+ identityElement->setAttribute("category", identity.getCategory());
+ identityElement->setAttribute("name", identity.getName());
+ identityElement->setAttribute("type", identity.getType());
+ queryElement.addNode(identityElement);
+ }
+ foreach(const String& feature, discoInfo->getFeatures()) {
+ boost::shared_ptr<XMLElement> featureElement(new XMLElement("feature"));
+ featureElement->setAttribute("var", feature);
+ queryElement.addNode(featureElement);
+ }
+ return queryElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h b/Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h
new file mode 100644
index 0000000..929a455
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_DiscoInfoSerializer_H
+#define SWIFTEN_DiscoInfoSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class DiscoInfoSerializer : public GenericPayloadSerializer<DiscoInfo> {
+ public:
+ DiscoInfoSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<DiscoInfo>) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/ErrorSerializer.cpp b/Swiften/Serializer/PayloadSerializers/ErrorSerializer.cpp
new file mode 100644
index 0000000..f5ce478
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ErrorSerializer.cpp
@@ -0,0 +1,56 @@
+#include "Swiften/Serializer/PayloadSerializers/ErrorSerializer.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+namespace Swift {
+
+ErrorSerializer::ErrorSerializer() : GenericPayloadSerializer<ErrorPayload>() {
+}
+
+String ErrorSerializer::serializePayload(boost::shared_ptr<ErrorPayload> error) const {
+ String result("<error type=\"");
+ switch (error->getType()) {
+ case ErrorPayload::Continue: result += "continue"; break;
+ case ErrorPayload::Modify: result += "modify"; break;
+ case ErrorPayload::Auth: result += "auth"; break;
+ case ErrorPayload::Wait: result += "wait"; break;
+ default: result += "cancel"; break;
+ }
+ result += "\">";
+
+ String conditionElement;
+ switch (error->getCondition()) {
+ case ErrorPayload::BadRequest: conditionElement = "bad-request"; break;
+ case ErrorPayload::Conflict: conditionElement = "conflict"; break;
+ case ErrorPayload::FeatureNotImplemented: conditionElement = "feature-not-implemented"; break;
+ case ErrorPayload::Forbidden: conditionElement = "forbidden"; break;
+ case ErrorPayload::Gone: conditionElement = "gone"; break;
+ case ErrorPayload::InternalServerError: conditionElement = "internal-server-error"; break;
+ case ErrorPayload::ItemNotFound: conditionElement = "item-not-found"; break;
+ case ErrorPayload::JIDMalformed: conditionElement = "jid-malformed"; break;
+ case ErrorPayload::NotAcceptable: conditionElement = "not-acceptable"; break;
+ case ErrorPayload::NotAllowed: conditionElement = "not-allowed"; break;
+ case ErrorPayload::NotAuthorized: conditionElement = "not-authorized"; break;
+ case ErrorPayload::PaymentRequired: conditionElement = "payment-required"; break;
+ case ErrorPayload::RecipientUnavailable: conditionElement = "recipient-unavailable"; break;
+ case ErrorPayload::Redirect: conditionElement = "redirect"; break;
+ case ErrorPayload::RegistrationRequired: conditionElement = "registration-required"; break;
+ case ErrorPayload::RemoteServerNotFound: conditionElement = "remote-server-not-found"; break;
+ case ErrorPayload::RemoteServerTimeout: conditionElement = "remote-server-timeout"; break;
+ case ErrorPayload::ResourceConstraint: conditionElement = "resource-constraint"; break;
+ case ErrorPayload::ServiceUnavailable: conditionElement = "service-unavailable"; break;
+ case ErrorPayload::SubscriptionRequired: conditionElement = "subscription-required"; break;
+ case ErrorPayload::UnexpectedRequest: conditionElement = "unexpected-request"; break;
+ default: conditionElement = "undefined-condition"; break;
+ }
+ result += "<" + conditionElement + " xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>";
+
+ if (!error->getText().isEmpty()) {
+ XMLTextNode textNode(error->getText());
+ result += "<text xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">" + textNode.serialize() + "</text>";
+ }
+
+ result += "</error>";
+ return result;
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/ErrorSerializer.h b/Swiften/Serializer/PayloadSerializers/ErrorSerializer.h
new file mode 100644
index 0000000..931596f
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ErrorSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_ErrorSerializer_H
+#define SWIFTEN_ErrorSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ class ErrorSerializer : public GenericPayloadSerializer<ErrorPayload> {
+ public:
+ ErrorSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<ErrorPayload> error) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
new file mode 100644
index 0000000..accf6c6
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -0,0 +1,61 @@
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/PayloadSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/BodySerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/PrioritySerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/ErrorSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/RosterSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/StatusSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/RawXMLPayloadSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/StorageSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.h"
+
+namespace Swift {
+
+FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
+ serializers_.push_back(new BodySerializer());
+ serializers_.push_back(new ChatStateSerializer());
+ serializers_.push_back(new PrioritySerializer());
+ serializers_.push_back(new ErrorSerializer());
+ serializers_.push_back(new RosterSerializer());
+ serializers_.push_back(new MUCPayloadSerializer());
+ serializers_.push_back(new SoftwareVersionSerializer());
+ serializers_.push_back(new StatusSerializer());
+ serializers_.push_back(new StatusShowSerializer());
+ serializers_.push_back(new DiscoInfoSerializer());
+ serializers_.push_back(new CapsInfoSerializer());
+ serializers_.push_back(new ResourceBindSerializer());
+ serializers_.push_back(new StartSessionSerializer());
+ serializers_.push_back(new SecurityLabelSerializer());
+ serializers_.push_back(new SecurityLabelsCatalogSerializer());
+ serializers_.push_back(new VCardSerializer());
+ serializers_.push_back(new VCardUpdateSerializer());
+ serializers_.push_back(new RawXMLPayloadSerializer());
+ serializers_.push_back(new StorageSerializer());
+ serializers_.push_back(new PrivateStorageSerializer(this));
+ foreach(PayloadSerializer* serializer, serializers_) {
+ addSerializer(serializer);
+ }
+}
+
+FullPayloadSerializerCollection::~FullPayloadSerializerCollection() {
+ foreach(PayloadSerializer* serializer, serializers_) {
+ removeSerializer(serializer);
+ delete serializer;
+ }
+ serializers_.clear();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h
new file mode 100644
index 0000000..e1655b5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_FULLPAYLOADSERIALIZERCOLLECTION_H
+#define SWIFTEN_FULLPAYLOADSERIALIZERCOLLECTION_H
+
+#include <vector>
+
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+
+namespace Swift {
+ class FullPayloadSerializerCollection : public PayloadSerializerCollection {
+ public:
+ FullPayloadSerializerCollection();
+ ~FullPayloadSerializerCollection();
+
+ private:
+ std::vector<PayloadSerializer*> serializers_;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
new file mode 100644
index 0000000..9c701cc
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
@@ -0,0 +1,13 @@
+#include "Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h"
+
+namespace Swift {
+
+MUCPayloadSerializer::MUCPayloadSerializer() : GenericPayloadSerializer<MUCPayload>() {
+}
+
+String MUCPayloadSerializer::serializePayload(boost::shared_ptr<MUCPayload>) const {
+ String result("<x xmlns='http://jabber.org/protocol/muc'/>");
+ return result;
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h
new file mode 100644
index 0000000..504c969
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_MUCPayloadSerializer_H
+#define SWIFTEN_MUCPayloadSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/MUCPayload.h"
+
+namespace Swift {
+ class MUCPayloadSerializer : public GenericPayloadSerializer<MUCPayload> {
+ public:
+ MUCPayloadSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<MUCPayload> version) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/PrioritySerializer.h b/Swiften/Serializer/PayloadSerializers/PrioritySerializer.h
new file mode 100644
index 0000000..f132b6e
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/PrioritySerializer.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_PrioritySerializer_H
+#define SWIFTEN_PrioritySerializer_H
+
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/Priority.h"
+
+namespace Swift {
+ class PrioritySerializer : public GenericPayloadSerializer<Priority> {
+ public:
+ PrioritySerializer() : GenericPayloadSerializer<Priority>() {}
+
+ virtual String serializePayload(boost::shared_ptr<Priority> priority) const {
+ return "<priority>" + boost::lexical_cast<std::string>(priority->getPriority()) + "</priority>";
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.cpp b/Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.cpp
new file mode 100644
index 0000000..999252c
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Serializer/XML/XMLRawTextNode.h"
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+
+namespace Swift {
+
+PrivateStorageSerializer::PrivateStorageSerializer(PayloadSerializerCollection* serializers) : serializers(serializers) {
+}
+
+String PrivateStorageSerializer::serializePayload(boost::shared_ptr<PrivateStorage> storage) const {
+ XMLElement storageElement("query", "jabber:iq:private");
+ boost::shared_ptr<Payload> payload = storage->getPayload();
+ if (payload) {
+ PayloadSerializer* serializer = serializers->getPayloadSerializer(payload);
+ if (serializer) {
+ storageElement.addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(serializer->serialize(payload))));
+ }
+ }
+ return storageElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.h b/Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.h
new file mode 100644
index 0000000..c655634
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/PrivateStorageSerializer.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/PrivateStorage.h"
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class PrivateStorageSerializer : public GenericPayloadSerializer<PrivateStorage> {
+ public:
+ PrivateStorageSerializer(PayloadSerializerCollection* serializers);
+
+ virtual String serializePayload(boost::shared_ptr<PrivateStorage>) const;
+
+ private:
+ PayloadSerializerCollection* serializers;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/RawXMLPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/RawXMLPayloadSerializer.h
new file mode 100644
index 0000000..f980174
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/RawXMLPayloadSerializer.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/RawXMLPayload.h"
+
+namespace Swift {
+ class RawXMLPayloadSerializer : public GenericPayloadSerializer<RawXMLPayload> {
+ public:
+ RawXMLPayloadSerializer() : GenericPayloadSerializer<RawXMLPayload>() {}
+
+ virtual String serializePayload(boost::shared_ptr<RawXMLPayload> p) const {
+ return p->getRawXML();
+ }
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.cpp b/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.cpp
new file mode 100644
index 0000000..93ab136
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+namespace Swift {
+
+ResourceBindSerializer::ResourceBindSerializer() : GenericPayloadSerializer<ResourceBind>() {
+}
+
+String ResourceBindSerializer::serializePayload(boost::shared_ptr<ResourceBind> resourceBind) const {
+ XMLElement bindElement("bind", "urn:ietf:params:xml:ns:xmpp-bind");
+ if (resourceBind->getJID().isValid()) {
+ boost::shared_ptr<XMLElement> jidNode(new XMLElement("jid"));
+ jidNode->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(resourceBind->getJID().toString())));
+ bindElement.addNode(jidNode);
+ }
+ else if (!resourceBind->getResource().isEmpty()) {
+ boost::shared_ptr<XMLElement> resourceNode(new XMLElement("resource"));
+ resourceNode->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(resourceBind->getResource())));
+ bindElement.addNode(resourceNode);
+ }
+ return bindElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h b/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h
new file mode 100644
index 0000000..61cf539
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_ResourceBindSerializer_H
+#define SWIFTEN_ResourceBindSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/ResourceBind.h"
+
+namespace Swift {
+ class ResourceBindSerializer : public GenericPayloadSerializer<ResourceBind> {
+ public:
+ ResourceBindSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<ResourceBind>) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp b/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp
new file mode 100644
index 0000000..6329f86
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp
@@ -0,0 +1,45 @@
+#include "Swiften/Serializer/PayloadSerializers/RosterSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+
+RosterSerializer::RosterSerializer() : GenericPayloadSerializer<RosterPayload>() {
+}
+
+String RosterSerializer::serializePayload(boost::shared_ptr<RosterPayload> roster) const {
+ XMLElement queryElement("query", "jabber:iq:roster");
+ foreach(const RosterItemPayload& item, roster->getItems()) {
+ boost::shared_ptr<XMLElement> itemElement(new XMLElement("item"));
+ itemElement->setAttribute("jid", item.getJID());
+ itemElement->setAttribute("name", item.getName());
+
+ switch (item.getSubscription()) {
+ case RosterItemPayload::To: itemElement->setAttribute("subscription", "to"); break;
+ case RosterItemPayload::From: itemElement->setAttribute("subscription", "from"); break;
+ case RosterItemPayload::Both: itemElement->setAttribute("subscription", "both"); break;
+ case RosterItemPayload::Remove: itemElement->setAttribute("subscription", "remove"); break;
+ case RosterItemPayload::None: itemElement->setAttribute("subscription", "none"); break;
+ }
+
+ if (item.getSubscriptionRequested()) {
+ itemElement->setAttribute("ask", "subscribe");
+ }
+
+ foreach(const String& group, item.getGroups()) {
+ boost::shared_ptr<XMLElement> groupElement(new XMLElement("group"));
+ groupElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(group)));
+ itemElement->addNode(groupElement);
+ }
+
+ queryElement.addNode(itemElement);
+ }
+
+ return queryElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/RosterSerializer.h b/Swiften/Serializer/PayloadSerializers/RosterSerializer.h
new file mode 100644
index 0000000..fb6a713
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/RosterSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_RosterSerializer_H
+#define SWIFTEN_RosterSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/RosterPayload.h"
+
+namespace Swift {
+ class RosterSerializer : public GenericPayloadSerializer<RosterPayload> {
+ public:
+ RosterSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<RosterPayload>) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.cpp b/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.cpp
new file mode 100644
index 0000000..dcce4dc
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.cpp
@@ -0,0 +1,38 @@
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLRawTextNode.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+
+SecurityLabelSerializer::SecurityLabelSerializer() : GenericPayloadSerializer<SecurityLabel>() {
+}
+
+String SecurityLabelSerializer::serializePayload(boost::shared_ptr<SecurityLabel> label) const {
+ XMLElement element("securitylabel", "urn:xmpp:sec-label:0");
+ if (!label->getDisplayMarking().isEmpty()) {
+ boost::shared_ptr<XMLElement> displayMarking(new XMLElement("displaymarking"));
+ if (!label->getForegroundColor().isEmpty()) {
+ displayMarking->setAttribute("fgcolor", label->getForegroundColor());
+ }
+ if (!label->getBackgroundColor().isEmpty()) {
+ displayMarking->setAttribute("bgcolor", label->getBackgroundColor());
+ }
+ displayMarking->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(label->getDisplayMarking())));
+ element.addNode(displayMarking);
+ }
+
+ boost::shared_ptr<XMLElement> labelElement(new XMLElement("label"));
+ labelElement->addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(label->getLabel())));
+ element.addNode(labelElement);
+
+ foreach(const String& equivalentLabel, label->getEquivalentLabels()) {
+ boost::shared_ptr<XMLElement> equivalentLabelElement(new XMLElement("equivalentlabel"));
+ equivalentLabelElement->addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(equivalentLabel)));
+ element.addNode(equivalentLabelElement);
+ }
+ return element.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h b/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h
new file mode 100644
index 0000000..b0b0804
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_SecurityLabelSerializer_H
+#define SWIFTEN_SecurityLabelSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/SecurityLabel.h"
+
+namespace Swift {
+ class SecurityLabelSerializer : public GenericPayloadSerializer<SecurityLabel> {
+ public:
+ SecurityLabelSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<SecurityLabel> version) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp b/Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp
new file mode 100644
index 0000000..bc6a41f
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp
@@ -0,0 +1,30 @@
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLRawTextNode.h"
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h"
+
+namespace Swift {
+
+SecurityLabelsCatalogSerializer::SecurityLabelsCatalogSerializer() : GenericPayloadSerializer<SecurityLabelsCatalog>() {
+}
+
+String SecurityLabelsCatalogSerializer::serializePayload(boost::shared_ptr<SecurityLabelsCatalog> catalog) const {
+ XMLElement element("catalog", "urn:xmpp:sec-label:catalog:0");
+ if (!catalog->getName().isEmpty()) {
+ element.setAttribute("name", catalog->getName());
+ }
+ if (catalog->getTo().isValid()) {
+ element.setAttribute("to", catalog->getTo());
+ }
+ if (!catalog->getDescription().isEmpty()) {
+ element.setAttribute("desc", catalog->getDescription());
+ }
+ foreach (const SecurityLabel& label, catalog->getLabels()) {
+ String serializedLabel = SecurityLabelSerializer().serialize(boost::shared_ptr<SecurityLabel>(new SecurityLabel(label)));
+ element.addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(serializedLabel)));
+ }
+ return element.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h b/Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h
new file mode 100644
index 0000000..d086c79
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_SecurityLabelsCatalogSerializer_H
+#define SWIFTEN_SecurityLabelsCatalogSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/SecurityLabelsCatalog.h"
+
+namespace Swift {
+ class SecurityLabelsCatalogSerializer : public GenericPayloadSerializer<SecurityLabelsCatalog> {
+ public:
+ SecurityLabelsCatalogSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<SecurityLabelsCatalog> version) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp b/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp
new file mode 100644
index 0000000..71ad9a2
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp
@@ -0,0 +1,23 @@
+#include "Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h"
+
+namespace Swift {
+
+SoftwareVersionSerializer::SoftwareVersionSerializer() : GenericPayloadSerializer<SoftwareVersion>() {
+}
+
+String SoftwareVersionSerializer::serializePayload(boost::shared_ptr<SoftwareVersion> version) const {
+ String result("<query xmlns=\"jabber:iq:version\">");
+ if (!version->getName().isEmpty()) {
+ result += "<name>" + version->getName() + "</name>";
+ }
+ if (!version->getVersion().isEmpty()) {
+ result += "<version>" + version->getVersion() + "</version>";
+ }
+ if (!version->getOS().isEmpty()) {
+ result += "<os>" + version->getOS() + "</os>";
+ }
+ result += "</query>";
+ return result;
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h b/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h
new file mode 100644
index 0000000..50242f2
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_SoftwareVersionSerializer_H
+#define SWIFTEN_SoftwareVersionSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/SoftwareVersion.h"
+
+namespace Swift {
+ class SoftwareVersionSerializer : public GenericPayloadSerializer<SoftwareVersion> {
+ public:
+ SoftwareVersionSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<SoftwareVersion> version) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h b/Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h
new file mode 100644
index 0000000..df35054
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_StartSessionSerializer_H
+#define SWIFTEN_StartSessionSerializer_H
+
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/StartSession.h"
+
+namespace Swift {
+ class StartSessionSerializer : public GenericPayloadSerializer<StartSession> {
+ public:
+ StartSessionSerializer() : GenericPayloadSerializer<StartSession>() {}
+
+ virtual String serializePayload(boost::shared_ptr<StartSession>) const {
+ return XMLElement("session", "urn:ietf:params:xml:ns:xmpp-session").serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/StatusSerializer.h b/Swiften/Serializer/PayloadSerializers/StatusSerializer.h
new file mode 100644
index 0000000..dc5f6d1
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StatusSerializer.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_StatusSerializer_H
+#define SWIFTEN_StatusSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Elements/Status.h"
+
+namespace Swift {
+ class StatusSerializer : public GenericPayloadSerializer<Status> {
+ public:
+ StatusSerializer() : GenericPayloadSerializer<Status>() {}
+
+ virtual String serializePayload(boost::shared_ptr<Status> status) const {
+ XMLElement element("status");
+ element.addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(status->getText())));
+ return element.serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h b/Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h
new file mode 100644
index 0000000..f66c09c
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_StatusShowSerializer_H
+#define SWIFTEN_StatusShowSerializer_H
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/StatusShow.h"
+
+namespace Swift {
+ class StatusShowSerializer : public GenericPayloadSerializer<StatusShow> {
+ public:
+ StatusShowSerializer() : GenericPayloadSerializer<StatusShow>() {}
+
+ virtual String serializePayload(boost::shared_ptr<StatusShow> statusShow) const {
+ if (statusShow->getType () == StatusShow::Online || statusShow->getType() == StatusShow::None) {
+ return "";
+ }
+ else {
+ String result("<show>");
+ switch (statusShow->getType()) {
+ case StatusShow::Away: result += "away"; break;
+ case StatusShow::XA: result += "xa"; break;
+ case StatusShow::FFC: result += "chat"; break;
+ case StatusShow::DND: result += "dnd"; break;
+ case StatusShow::Online: assert(false); break;
+ case StatusShow::None: assert(false); break;
+ }
+ result += "</show>";
+ return result;
+ }
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp
new file mode 100644
index 0000000..4268381
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp
@@ -0,0 +1,36 @@
+#include "Swiften/Serializer/PayloadSerializers/StorageSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+namespace Swift {
+
+StorageSerializer::StorageSerializer() : GenericPayloadSerializer<Storage>() {
+}
+
+String StorageSerializer::serializePayload(boost::shared_ptr<Storage> storage) const {
+ XMLElement storageElement("storage", "storage:bookmarks");
+ foreach(const Storage::Conference& conference, storage->getConferences()) {
+ boost::shared_ptr<XMLElement> conferenceElement(new XMLElement("conference"));
+ conferenceElement->setAttribute("name", conference.name);
+ conferenceElement->setAttribute("jid", conference.jid);
+ conferenceElement->setAttribute("autojoin", conference.autoJoin ? "1" : "0");
+ if (!conference.nick.isEmpty()) {
+ boost::shared_ptr<XMLElement> nickElement(new XMLElement("nick"));
+ nickElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(conference.nick)));
+ conferenceElement->addNode(nickElement);
+ }
+ if (!conference.password.isEmpty()) {
+ boost::shared_ptr<XMLElement> passwordElement(new XMLElement("password"));
+ passwordElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(conference.password)));
+ conferenceElement->addNode(passwordElement);
+ }
+ storageElement.addNode(conferenceElement);
+ }
+ return storageElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StorageSerializer.h b/Swiften/Serializer/PayloadSerializers/StorageSerializer.h
new file mode 100644
index 0000000..e657376
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StorageSerializer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/Storage.h"
+
+namespace Swift {
+ class StorageSerializer : public GenericPayloadSerializer<Storage> {
+ public:
+ StorageSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<Storage>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp
new file mode 100644
index 0000000..650a7ee
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp
@@ -0,0 +1,25 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h"
+
+using namespace Swift;
+
+class CapsInfoSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(CapsInfoSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ CapsInfoSerializerTest() {}
+
+ void testSerialize() {
+ CapsInfoSerializer testling;
+ boost::shared_ptr<CapsInfo> priority(new CapsInfo("http://swift.im", "myversion", "sha-1"));
+
+ CPPUNIT_ASSERT_EQUAL(String("<c hash=\"sha-1\" node=\"http://swift.im\" ver=\"myversion\" xmlns=\"http://jabber.org/protocol/caps\"/>"), testling.serialize(priority));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CapsInfoSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp
new file mode 100644
index 0000000..d3e247f
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp
@@ -0,0 +1,38 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h"
+
+using namespace Swift;
+
+class DiscoInfoSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(DiscoInfoSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ DiscoInfoSerializerTest() {}
+
+ void testSerialize() {
+ DiscoInfoSerializer testling;
+ boost::shared_ptr<DiscoInfo> discoInfo(new DiscoInfo());
+ discoInfo->addIdentity(DiscoInfo::Identity("Swift", "client", "pc"));
+ discoInfo->addIdentity(DiscoInfo::Identity("Vlug", "client", "pc", "nl"));
+ discoInfo->addFeature("http://jabber.org/protocol/caps");
+ discoInfo->addFeature("http://jabber.org/protocol/disco#info");
+ discoInfo->setNode("http://swift.im#bla");
+
+ String expectedResult =
+ "<query node=\"http://swift.im#bla\" xmlns=\"http://jabber.org/protocol/disco#info\">"
+ "<identity category=\"client\" name=\"Swift\" type=\"pc\"/>"
+ "<identity category=\"client\" name=\"Vlug\" type=\"pc\" xml:lang=\"nl\"/>"
+ "<feature var=\"http://jabber.org/protocol/caps\"/>"
+ "<feature var=\"http://jabber.org/protocol/disco#info\"/>"
+ "</query>";
+
+ CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(discoInfo));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp
new file mode 100644
index 0000000..ecd904a
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp
@@ -0,0 +1,25 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/ErrorSerializer.h"
+
+using namespace Swift;
+
+class ErrorSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ErrorSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ErrorSerializerTest() {}
+
+ void testSerialize() {
+ ErrorSerializer testling;
+ boost::shared_ptr<ErrorPayload> error(new ErrorPayload(ErrorPayload::BadRequest, ErrorPayload::Cancel, "My Error"));
+
+ CPPUNIT_ASSERT_EQUAL(String("<error type=\"cancel\"><bad-request xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/><text xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">My Error</text></error>"), testling.serialize(error));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ErrorSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp
new file mode 100644
index 0000000..23ff4a0
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp
@@ -0,0 +1,21 @@
+#include "Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Swiften/Serializer/PayloadSerializer.h"
+
+namespace Swift {
+
+String PayloadsSerializer::serialize(boost::shared_ptr<Payload> payload) {
+ PayloadSerializer* serializer = serializers.getPayloadSerializer(payload);
+ if (serializer) {
+ return serializer->serialize(payload);
+ }
+ else {
+ CPPUNIT_ASSERT(false);
+ return "";
+ }
+}
+
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h b/Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h
new file mode 100644
index 0000000..9fca334
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+namespace Swift {
+ class PayloadsSerializer {
+ public:
+ String serialize(boost::shared_ptr<Payload> payload);
+
+ private:
+ FullPayloadSerializerCollection serializers;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp
new file mode 100644
index 0000000..5f6432b
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp
@@ -0,0 +1,25 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/PrioritySerializer.h"
+
+using namespace Swift;
+
+class PrioritySerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(PrioritySerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PrioritySerializerTest() {}
+
+ void testSerialize() {
+ PrioritySerializer testling;
+ boost::shared_ptr<Priority> priority(new Priority(-113));
+
+ CPPUNIT_ASSERT_EQUAL(String("<priority>-113</priority>"), testling.serialize(priority));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PrioritySerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp
new file mode 100644
index 0000000..7f0d5c9
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp
@@ -0,0 +1,44 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h"
+#include "Swiften/Elements/PrivateStorage.h"
+#include "Swiften/Elements/Storage.h"
+
+using namespace Swift;
+
+class PrivateStorageSerializerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PrivateStorageSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ PrivateStorageSerializerTest() {}
+
+ void testSerialize() {
+ PayloadsSerializer serializer;
+
+ boost::shared_ptr<PrivateStorage> privateStorage(new PrivateStorage());
+ boost::shared_ptr<Storage> storage(new Storage());
+ Storage::Conference conference;
+ conference.name = "Swift";
+ conference.jid = JID("swift@rooms.swift.im");
+ conference.nick = "Alice";
+ storage->addConference(conference);
+ privateStorage->setPayload(storage);
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<query xmlns=\"jabber:iq:private\">"
+ "<storage xmlns=\"storage:bookmarks\">"
+ "<conference "
+ "autojoin=\"0\" "
+ "jid=\"swift@rooms.swift.im\" "
+ "name=\"Swift\">"
+ "<nick>Alice</nick>"
+ "</conference>"
+ "</storage>"
+ "</query>"), serializer.serialize(privateStorage));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PrivateStorageSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp
new file mode 100644
index 0000000..ff09966
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp
@@ -0,0 +1,49 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h"
+
+using namespace Swift;
+
+class ResourceBindSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ResourceBindSerializerTest);
+ CPPUNIT_TEST(testSerialize_JID);
+ CPPUNIT_TEST(testSerialize_Resource);
+ CPPUNIT_TEST(testSerialize_Empty);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ResourceBindSerializerTest() {}
+
+ void testSerialize_JID() {
+ ResourceBindSerializer testling;
+ boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind());
+ resourceBind->setJID(JID("somenode@example.com/someresource"));
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">"
+ "<jid>somenode@example.com/someresource</jid>"
+ "</bind>"), testling.serialize(resourceBind));
+ }
+
+ void testSerialize_Resource() {
+ ResourceBindSerializer testling;
+ boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind());
+ resourceBind->setResource("someresource");
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">"
+ "<resource>someresource</resource>"
+ "</bind>"), testling.serialize(resourceBind));
+ }
+
+ void testSerialize_Empty() {
+ ResourceBindSerializer testling;
+ boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind());
+
+ CPPUNIT_ASSERT_EQUAL(String("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"), testling.serialize(resourceBind));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ResourceBindSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp
new file mode 100644
index 0000000..81fdc09
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp
@@ -0,0 +1,48 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/RosterSerializer.h"
+
+using namespace Swift;
+
+class RosterSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RosterSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ RosterSerializerTest() {}
+
+ void testSerialize() {
+ RosterSerializer testling;
+ boost::shared_ptr<RosterPayload> roster(new RosterPayload());
+
+ RosterItemPayload item1;
+ item1.setJID(JID("foo@bar.com"));
+ item1.setName("Foo @ Bar");
+ item1.setSubscription(RosterItemPayload::From);
+ item1.addGroup("Group 1");
+ item1.addGroup("Group 2");
+ item1.setSubscriptionRequested();
+ roster->addItem(item1);
+
+ RosterItemPayload item2;
+ item2.setJID(JID("baz@blo.com"));
+ item2.setName("Baz");
+ roster->addItem(item2);
+
+ String expectedResult =
+ "<query xmlns=\"jabber:iq:roster\">"
+ "<item ask=\"subscribe\" jid=\"foo@bar.com\" name=\"Foo @ Bar\" subscription=\"from\">"
+ "<group>Group 1</group>"
+ "<group>Group 2</group>"
+ "</item>"
+ "<item jid=\"baz@blo.com\" name=\"Baz\" subscription=\"none\"/>"
+ "</query>";
+
+ CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(RosterSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp
new file mode 100644
index 0000000..e75695f
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp
@@ -0,0 +1,54 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h"
+
+using namespace Swift;
+
+class SecurityLabelSerializerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SecurityLabelSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST(testSerialize_EmptyLabel);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testSerialize() {
+ SecurityLabelSerializer testling;
+ boost::shared_ptr<SecurityLabel> securityLabel(new SecurityLabel());
+ securityLabel->setDisplayMarking("SECRET");
+ securityLabel->setForegroundColor("black");
+ securityLabel->setBackgroundColor("red");
+ securityLabel->setLabel("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQYCAQQGASk=</esssecuritylabel>");
+ securityLabel->addEquivalentLabel("<icismlabel xmlns=\"http://example.gov/IC-ISM/0\" classification=\"S\" ownerProducer=\"USA\" disseminationControls=\"FOUO\"/>");
+ securityLabel->addEquivalentLabel("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MRUCAgD9DA9BcXVhIChvYnNvbGV0ZSk=</esssecuritylabel>");
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<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>"
+ "<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>"), testling.serialize(securityLabel));
+ }
+
+ void testSerialize_EmptyLabel() {
+ SecurityLabelSerializer testling;
+ boost::shared_ptr<SecurityLabel> securityLabel(new SecurityLabel());
+ securityLabel->setDisplayMarking("SECRET");
+ securityLabel->setLabel("");
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<securitylabel xmlns=\"urn:xmpp:sec-label:0\">"
+ "<displaymarking>SECRET</displaymarking>"
+ "<label></label>"
+ "</securitylabel>"), testling.serialize(securityLabel));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SecurityLabelSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp
new file mode 100644
index 0000000..3055aca
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp
@@ -0,0 +1,52 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h"
+
+using namespace Swift;
+
+class SecurityLabelsCatalogSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SecurityLabelsCatalogSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SecurityLabelsCatalogSerializerTest() {}
+
+ void testSerialize() {
+ SecurityLabelsCatalogSerializer testling;
+ boost::shared_ptr<SecurityLabelsCatalog> catalog(new SecurityLabelsCatalog());
+ catalog->setTo(JID("example.com"));
+ catalog->setName("Default");
+ catalog->setDescription("an example set of labels");
+
+ SecurityLabel securityLabel1;
+ securityLabel1.setDisplayMarking("SECRET");
+ securityLabel1.setForegroundColor("black");
+ securityLabel1.setBackgroundColor("red");
+ securityLabel1.setLabel("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQYCAQQGASk=</esssecuritylabel>");
+ catalog->addLabel(securityLabel1);
+
+ SecurityLabel securityLabel2;
+ securityLabel2.setDisplayMarking("CONFIDENTIAL");
+ securityLabel2.setForegroundColor("black");
+ securityLabel2.setBackgroundColor("navy");
+ securityLabel2.setLabel("<esssecuritylabel xmlns=\"urn:xmpp:sec-label:ess:0\">MQMGASk=</esssecuritylabel>");
+ catalog->addLabel(securityLabel2);
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<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>"), testling.serialize(catalog));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SecurityLabelsCatalogSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp
new file mode 100644
index 0000000..fd5dba5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp
@@ -0,0 +1,25 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h"
+
+using namespace Swift;
+
+class SoftwareVersionSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SoftwareVersionSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ SoftwareVersionSerializerTest() {}
+
+ void testSerialize() {
+ SoftwareVersionSerializer testling;
+ boost::shared_ptr<SoftwareVersion> softwareVersion(new SoftwareVersion("Swift", "0.1", "Mac OS X"));
+
+ CPPUNIT_ASSERT_EQUAL(String("<query xmlns=\"jabber:iq:version\"><name>Swift</name><version>0.1</version><os>Mac OS X</os></query>"), testling.serialize(softwareVersion));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SoftwareVersionSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp
new file mode 100644
index 0000000..6dedacd
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp
@@ -0,0 +1,25 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/StatusSerializer.h"
+
+using namespace Swift;
+
+class StatusSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StatusSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StatusSerializerTest() {}
+
+ void testSerialize() {
+ StatusSerializer testling;
+ boost::shared_ptr<Status> status(new Status("I am away"));
+
+ CPPUNIT_ASSERT_EQUAL(String("<status>I am away</status>"), testling.serialize(status));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StatusSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp
new file mode 100644
index 0000000..42e1c7c
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp
@@ -0,0 +1,58 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h"
+
+using namespace Swift;
+
+class StatusShowSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StatusShowSerializerTest);
+ CPPUNIT_TEST(testSerialize_Online);
+ CPPUNIT_TEST(testSerialize_Away);
+ CPPUNIT_TEST(testSerialize_FFC);
+ CPPUNIT_TEST(testSerialize_XA);
+ CPPUNIT_TEST(testSerialize_DND);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StatusShowSerializerTest() {}
+
+ void testSerialize_Online() {
+ StatusShowSerializer testling;
+ boost::shared_ptr<StatusShow> statusShow(new StatusShow(StatusShow::Online));
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.serialize(statusShow));
+ }
+
+ void testSerialize_Away() {
+ StatusShowSerializer testling;
+ boost::shared_ptr<StatusShow> statusShow(new StatusShow(StatusShow::Away));
+ CPPUNIT_ASSERT_EQUAL(String("<show>away</show>"), testling.serialize(statusShow));
+ }
+
+ void testSerialize_FFC() {
+ StatusShowSerializer testling;
+ boost::shared_ptr<StatusShow> statusShow(new StatusShow(StatusShow::FFC));
+ CPPUNIT_ASSERT_EQUAL(String("<show>chat</show>"), testling.serialize(statusShow));
+ }
+
+ void testSerialize_XA() {
+ StatusShowSerializer testling;
+ boost::shared_ptr<StatusShow> statusShow(new StatusShow(StatusShow::XA));
+ CPPUNIT_ASSERT_EQUAL(String("<show>xa</show>"), testling.serialize(statusShow));
+ }
+
+ void testSerialize_DND() {
+ StatusShowSerializer testling;
+ boost::shared_ptr<StatusShow> statusShow(new StatusShow(StatusShow::DND));
+ CPPUNIT_ASSERT_EQUAL(String("<show>dnd</show>"), testling.serialize(statusShow));
+ }
+
+ void testSerialize_None() {
+ StatusShowSerializer testling;
+ boost::shared_ptr<StatusShow> statusShow(new StatusShow(StatusShow::None));
+ CPPUNIT_ASSERT_EQUAL(String(""), testling.serialize(statusShow));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StatusShowSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp
new file mode 100644
index 0000000..daf43c5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp
@@ -0,0 +1,60 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.h"
+#include "Swiften/Elements/Storage.h"
+
+using namespace Swift;
+
+class StorageSerializerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(StorageSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST(testSerialize_NoNickOrPassword);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StorageSerializerTest() {}
+
+ void testSerialize() {
+ PayloadsSerializer serializer;
+ boost::shared_ptr<Storage> storage(new Storage());
+ Storage::Conference conference;
+ conference.name = "Council of Oberon";
+ conference.autoJoin = true;
+ conference.jid = JID("council@conference.underhill.org");
+ conference.nick = "Puck";
+ conference.password = "MyPass";
+ storage->addConference(conference);
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<storage xmlns=\"storage:bookmarks\">"
+ "<conference "
+ "autojoin=\"1\" "
+ "jid=\"council@conference.underhill.org\" "
+ "name=\"Council of Oberon\">"
+ "<nick>Puck</nick>"
+ "<password>MyPass</password>"
+ "</conference>"
+ "</storage>"), serializer.serialize(storage));
+ }
+
+ void testSerialize_NoNickOrPassword() {
+ PayloadsSerializer serializer;
+ boost::shared_ptr<Storage> storage(new Storage());
+ Storage::Conference conference;
+ conference.name = "Council of Oberon";
+ conference.autoJoin = true;
+ conference.jid = JID("council@conference.underhill.org");
+ storage->addConference(conference);
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<storage xmlns=\"storage:bookmarks\">"
+ "<conference "
+ "autojoin=\"1\" "
+ "jid=\"council@conference.underhill.org\" "
+ "name=\"Council of Oberon\"/>"
+ "</storage>"), serializer.serialize(storage));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StorageSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp
new file mode 100644
index 0000000..23988d7
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp
@@ -0,0 +1,31 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h"
+
+using namespace Swift;
+
+class VCardUpdateSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(VCardUpdateSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ VCardUpdateSerializerTest() {}
+
+ void testSerialize() {
+ VCardUpdateSerializer testling;
+ boost::shared_ptr<VCardUpdate> update(new VCardUpdate());
+ update->setPhotoHash("sha1-hash-of-image");
+
+ String expectedResult =
+ "<x xmlns=\"vcard-temp:x:update\">"
+ "<photo>sha1-hash-of-image</photo>"
+ "</x>";
+
+ CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(update));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(VCardUpdateSerializerTest);
diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
new file mode 100644
index 0000000..fbb0274
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
@@ -0,0 +1,50 @@
+#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+namespace Swift {
+
+VCardSerializer::VCardSerializer() : GenericPayloadSerializer<VCard>() {
+}
+
+String VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard) const {
+ XMLElement queryElement("vCard", "vcard-temp");
+ if (!vcard->getFullName().isEmpty()) {
+ boost::shared_ptr<XMLElement> fullNameElement(new XMLElement("FN"));
+ fullNameElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getFullName())));
+ queryElement.addNode(fullNameElement);
+ }
+ if (!vcard->getGivenName().isEmpty() || !vcard->getFamilyName().isEmpty()) {
+ boost::shared_ptr<XMLElement> nameElement(new XMLElement("N"));
+ if (!vcard->getFamilyName().isEmpty()) {
+ boost::shared_ptr<XMLElement> familyNameElement(new XMLElement("FAMILY"));
+ familyNameElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getFamilyName())));
+ nameElement->addNode(familyNameElement);
+ }
+ if (!vcard->getGivenName().isEmpty()) {
+ boost::shared_ptr<XMLElement> givenNameElement(new XMLElement("GIVEN"));
+ givenNameElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getGivenName())));
+ nameElement->addNode(givenNameElement);
+ }
+ queryElement.addNode(nameElement);
+ }
+ if (!vcard->getEMail().isEmpty()) {
+ boost::shared_ptr<XMLElement> emailElement(new XMLElement("EMAIL"));
+ boost::shared_ptr<XMLElement> userIDElement(new XMLElement("USERID"));
+ userIDElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getEMail())));
+ emailElement->addNode(userIDElement);
+ queryElement.addNode(emailElement);
+ }
+ if (!vcard->getNickname().isEmpty()) {
+ boost::shared_ptr<XMLElement> nickElement(new XMLElement("NICKNAME"));
+ nickElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getNickname())));
+ queryElement.addNode(nickElement);
+ }
+ // TODO
+ return queryElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.h b/Swiften/Serializer/PayloadSerializers/VCardSerializer.h
new file mode 100644
index 0000000..baf5947
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/VCard.h"
+
+namespace Swift {
+ class VCardSerializer : public GenericPayloadSerializer<VCard> {
+ public:
+ VCardSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<VCard>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.cpp b/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.cpp
new file mode 100644
index 0000000..540b48d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.cpp
@@ -0,0 +1,21 @@
+#include "Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+namespace Swift {
+
+VCardUpdateSerializer::VCardUpdateSerializer() : GenericPayloadSerializer<VCardUpdate>() {
+}
+
+String VCardUpdateSerializer::serializePayload(boost::shared_ptr<VCardUpdate> vcardUpdate) const {
+ XMLElement updateElement("x", "vcard-temp:x:update");
+ boost::shared_ptr<XMLElement> photoElement(new XMLElement("photo"));
+ photoElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcardUpdate->getPhotoHash())));
+ updateElement.addNode(photoElement);
+ return updateElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h b/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h
new file mode 100644
index 0000000..e1373a9
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/VCardUpdate.h"
+
+namespace Swift {
+ class VCardUpdateSerializer : public GenericPayloadSerializer<VCardUpdate> {
+ public:
+ VCardUpdateSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<VCardUpdate>) const;
+ };
+}
diff --git a/Swiften/Serializer/PresenceSerializer.cpp b/Swiften/Serializer/PresenceSerializer.cpp
new file mode 100644
index 0000000..f7585d5
--- /dev/null
+++ b/Swiften/Serializer/PresenceSerializer.cpp
@@ -0,0 +1,27 @@
+#include "Swiften/Serializer/PresenceSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+#include "boost/shared_ptr.hpp"
+
+namespace Swift {
+
+PresenceSerializer::PresenceSerializer(PayloadSerializerCollection* payloadSerializers) :
+ GenericStanzaSerializer<Presence>("presence", payloadSerializers) {
+}
+
+void PresenceSerializer::setStanzaSpecificAttributesGeneric(
+ boost::shared_ptr<Presence> presence,
+ XMLElement& element) const {
+ switch (presence->getType()) {
+ case Presence::Unavailable: element.setAttribute("type","unavailable"); break;
+ case Presence::Probe: element.setAttribute("type","probe"); break;
+ case Presence::Subscribe: element.setAttribute("type","subscribe"); break;
+ case Presence::Subscribed: element.setAttribute("type","subscribed"); break;
+ case Presence::Unsubscribe: element.setAttribute("type","unsubscribe"); break;
+ case Presence::Unsubscribed: element.setAttribute("type","unsubscribed"); break;
+ case Presence::Error: element.setAttribute("type","error"); break;
+ case Presence::Available: break;
+ }
+}
+
+}
diff --git a/Swiften/Serializer/PresenceSerializer.h b/Swiften/Serializer/PresenceSerializer.h
new file mode 100644
index 0000000..158d4f2
--- /dev/null
+++ b/Swiften/Serializer/PresenceSerializer.h
@@ -0,0 +1,21 @@
+#ifndef SWIFTEN_PresenceSerializer_H
+#define SWIFTEN_PresenceSerializer_H
+
+#include <cassert>
+
+#include "Swiften/Serializer/GenericStanzaSerializer.h"
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+ class PresenceSerializer : public GenericStanzaSerializer<Presence> {
+ public:
+ PresenceSerializer(PayloadSerializerCollection* payloadSerializers);
+
+ private:
+ virtual void setStanzaSpecificAttributesGeneric(
+ boost::shared_ptr<Presence> presence,
+ XMLElement& element) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/StanzaSerializer.cpp b/Swiften/Serializer/StanzaSerializer.cpp
new file mode 100644
index 0000000..d940634
--- /dev/null
+++ b/Swiften/Serializer/StanzaSerializer.cpp
@@ -0,0 +1,50 @@
+#include "Swiften/Serializer/StanzaSerializer.h"
+
+#include <sstream>
+#include <typeinfo>
+#include <iostream>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLRawTextNode.h"
+#include "Swiften/Serializer/PayloadSerializer.h"
+#include "Swiften/Serializer/PayloadSerializerCollection.h"
+#include "Swiften/Elements/Stanza.h"
+
+namespace Swift {
+
+StanzaSerializer::StanzaSerializer(const String& tag, PayloadSerializerCollection* payloadSerializers) : tag_(tag), payloadSerializers_(payloadSerializers) {
+}
+
+String StanzaSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<Stanza> stanza(boost::dynamic_pointer_cast<Stanza>(element));
+
+ XMLElement stanzaElement(tag_);
+ if (stanza->getFrom().isValid()) {
+ stanzaElement.setAttribute("from", stanza->getFrom());
+ }
+ if (stanza->getTo().isValid()) {
+ stanzaElement.setAttribute("to", stanza->getTo());
+ }
+ if (!stanza->getID().isEmpty()) {
+ stanzaElement.setAttribute("id", stanza->getID());
+ }
+ setStanzaSpecificAttributes(stanza, stanzaElement);
+
+ String serializedPayloads;
+ foreach (const boost::shared_ptr<Payload>& payload, stanza->getPayloads()) {
+ PayloadSerializer* serializer = payloadSerializers_->getPayloadSerializer(payload);
+ if (serializer) {
+ serializedPayloads += serializer->serialize(payload);
+ }
+ else {
+ std::cerr << "Could not find serializer for " << typeid(*(payload.get())).name() << std::endl;
+ }
+ }
+ if (!serializedPayloads.isEmpty()) {
+ stanzaElement.addNode(boost::shared_ptr<XMLNode>(new XMLRawTextNode(serializedPayloads)));
+ }
+
+ return stanzaElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/StanzaSerializer.h b/Swiften/Serializer/StanzaSerializer.h
new file mode 100644
index 0000000..0f7abaf
--- /dev/null
+++ b/Swiften/Serializer/StanzaSerializer.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_STANZASERIALIZER_H
+#define SWIFTEN_STANZASERIALIZER_H
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Serializer/ElementSerializer.h"
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class StanzaSerializer : public ElementSerializer {
+ public:
+ StanzaSerializer(const String& tag, PayloadSerializerCollection* payloadSerializers);
+
+ virtual String serialize(boost::shared_ptr<Element>) const;
+ virtual void setStanzaSpecificAttributes(boost::shared_ptr<Element>, XMLElement&) const = 0;
+
+ private:
+ String tag_;
+ PayloadSerializerCollection* payloadSerializers_;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/StartTLSFailureSerializer.h b/Swiften/Serializer/StartTLSFailureSerializer.h
new file mode 100644
index 0000000..472fea0
--- /dev/null
+++ b/Swiften/Serializer/StartTLSFailureSerializer.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_StartTLSFailureSerializer_H
+#define SWIFTEN_StartTLSFailureSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/StartTLSFailure.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class StartTLSFailureSerializer : public GenericElementSerializer<StartTLSFailure> {
+ public:
+ StartTLSFailureSerializer() : GenericElementSerializer<StartTLSFailure>() {
+ }
+
+ virtual String serialize(boost::shared_ptr<Element>) const {
+ return XMLElement("failure", "urn:ietf:params:xml:ns:xmpp-tls").serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/StartTLSRequestSerializer.h b/Swiften/Serializer/StartTLSRequestSerializer.h
new file mode 100644
index 0000000..fa85fe2
--- /dev/null
+++ b/Swiften/Serializer/StartTLSRequestSerializer.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_StartTLSRequestSerializer_H
+#define SWIFTEN_StartTLSRequestSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/StartTLSRequest.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class StartTLSRequestSerializer : public GenericElementSerializer<StartTLSRequest> {
+ public:
+ StartTLSRequestSerializer() : GenericElementSerializer<StartTLSRequest>() {
+ }
+
+ virtual String serialize(boost::shared_ptr<Element>) const {
+ return XMLElement("starttls", "urn:ietf:params:xml:ns:xmpp-tls").serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/StreamFeaturesSerializer.cpp b/Swiften/Serializer/StreamFeaturesSerializer.cpp
new file mode 100644
index 0000000..49e62c4
--- /dev/null
+++ b/Swiften/Serializer/StreamFeaturesSerializer.cpp
@@ -0,0 +1,46 @@
+#include "Swiften/Serializer/StreamFeaturesSerializer.h"
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+StreamFeaturesSerializer::StreamFeaturesSerializer() {
+}
+
+String StreamFeaturesSerializer::serialize(boost::shared_ptr<Element> element) const {
+ boost::shared_ptr<StreamFeatures> streamFeatures(boost::dynamic_pointer_cast<StreamFeatures>(element));
+
+ XMLElement streamFeaturesElement("stream:features");
+ if (streamFeatures->hasStartTLS()) {
+ streamFeaturesElement.addNode(boost::shared_ptr<XMLElement>(new XMLElement("starttls", "urn:ietf:params:xml:ns:xmpp-tls")));
+ }
+ if (!streamFeatures->getCompressionMethods().empty()) {
+ boost::shared_ptr<XMLElement> compressionElement(new XMLElement("compression", "http://jabber.org/features/compress"));
+ foreach(const String& method, streamFeatures->getCompressionMethods()) {
+ boost::shared_ptr<XMLElement> methodElement(new XMLElement("method"));
+ methodElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(method)));
+ compressionElement->addNode(methodElement);
+ }
+ streamFeaturesElement.addNode(compressionElement);
+ }
+ if (!streamFeatures->getAuthenticationMechanisms().empty()) {
+ boost::shared_ptr<XMLElement> mechanismsElement(new XMLElement("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl"));
+ foreach(const String& mechanism, streamFeatures->getAuthenticationMechanisms()) {
+ boost::shared_ptr<XMLElement> mechanismElement(new XMLElement("mechanism"));
+ mechanismElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(mechanism)));
+ mechanismsElement->addNode(mechanismElement);
+ }
+ streamFeaturesElement.addNode(mechanismsElement);
+ }
+ if (streamFeatures->hasResourceBind()) {
+ streamFeaturesElement.addNode(boost::shared_ptr<XMLElement>(new XMLElement("bind", "urn:ietf:params:xml:ns:xmpp-bind")));
+ }
+ if (streamFeatures->hasSession()) {
+ streamFeaturesElement.addNode(boost::shared_ptr<XMLElement>(new XMLElement("session", "urn:ietf:params:xml:ns:xmpp-session")));
+ }
+ return streamFeaturesElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/StreamFeaturesSerializer.h b/Swiften/Serializer/StreamFeaturesSerializer.h
new file mode 100644
index 0000000..f2da1bf
--- /dev/null
+++ b/Swiften/Serializer/StreamFeaturesSerializer.h
@@ -0,0 +1,18 @@
+#ifndef SWIFTEN_StreamFeaturesSerializer_H
+#define SWIFTEN_StreamFeaturesSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+
+namespace Swift {
+ class StreamFeaturesSerializer : public GenericElementSerializer<StreamFeatures> {
+ public:
+ StreamFeaturesSerializer();
+
+ virtual String serialize(boost::shared_ptr<Element> element) const;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/TLSProceedSerializer.h b/Swiften/Serializer/TLSProceedSerializer.h
new file mode 100644
index 0000000..d3829ac
--- /dev/null
+++ b/Swiften/Serializer/TLSProceedSerializer.h
@@ -0,0 +1,22 @@
+#ifndef SWIFTEN_TLSProceedSerializer_H
+#define SWIFTEN_TLSProceedSerializer_H
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/TLSProceed.h"
+#include "Swiften/Serializer/GenericElementSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+ class TLSProceedSerializer : public GenericElementSerializer<TLSProceed> {
+ public:
+ TLSProceedSerializer() : GenericElementSerializer<TLSProceed>() {
+ }
+
+ virtual String serialize(boost::shared_ptr<Element>) const {
+ return XMLElement("proceed", "urn:ietf:params:xml:ns:xmpp-tls").serialize();
+ }
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp b/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp
new file mode 100644
index 0000000..187fe64
--- /dev/null
+++ b/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp
@@ -0,0 +1,46 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/StreamFeaturesSerializer.h"
+#include "Swiften/Elements/StreamFeatures.h"
+
+using namespace Swift;
+
+class StreamFeaturesSerializerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StreamFeaturesSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StreamFeaturesSerializerTest() {}
+
+ void testSerialize() {
+ StreamFeaturesSerializer testling;
+ boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
+ streamFeatures->setHasStartTLS();
+ streamFeatures->addCompressionMethod("zlib");
+ streamFeatures->addCompressionMethod("lzw");
+ streamFeatures->addAuthenticationMechanism("DIGEST-MD5");
+ streamFeatures->addAuthenticationMechanism("PLAIN");
+ streamFeatures->setHasResourceBind();
+ streamFeatures->setHasSession();
+
+ CPPUNIT_ASSERT_EQUAL(String(
+ "<stream:features>"
+ "<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>"), testling.serialize(streamFeatures));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StreamFeaturesSerializerTest);
diff --git a/Swiften/Serializer/XML/UnitTest/XMLElementTest.cpp b/Swiften/Serializer/XML/UnitTest/XMLElementTest.cpp
new file mode 100644
index 0000000..49eb109
--- /dev/null
+++ b/Swiften/Serializer/XML/UnitTest/XMLElementTest.cpp
@@ -0,0 +1,62 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+using namespace Swift;
+
+class XMLElementTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMLElementTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST(testSerialize_NoChildren);
+ CPPUNIT_TEST(testSerialize_SpecialAttributeCharacters);
+ CPPUNIT_TEST(testSerialize_EmptyAttributeValue);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ XMLElementTest() {}
+
+ void testSerialize() {
+ XMLElement testling("foo", "http://example.com");
+ testling.setAttribute("myatt", "myval");
+ boost::shared_ptr<XMLElement> barElement(new XMLElement("bar"));
+ barElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode("Blo")));
+ testling.addNode(barElement);
+ boost::shared_ptr<XMLElement> bazElement(new XMLElement("baz"));
+ bazElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode("Bli")));
+ testling.addNode(bazElement);
+
+ String result = testling.serialize();
+ String expectedResult =
+ "<foo myatt=\"myval\" xmlns=\"http://example.com\">"
+ "<bar>Blo</bar>"
+ "<baz>Bli</baz>"
+ "</foo>";
+
+ CPPUNIT_ASSERT_EQUAL(expectedResult, result);
+ }
+
+ void testSerialize_NoChildren() {
+ XMLElement testling("foo", "http://example.com");
+
+ CPPUNIT_ASSERT_EQUAL(String("<foo xmlns=\"http://example.com\"/>"), testling.serialize());
+ }
+
+ void testSerialize_SpecialAttributeCharacters() {
+ XMLElement testling("foo");
+ testling.setAttribute("myatt", "<\"'&>");
+
+ CPPUNIT_ASSERT_EQUAL(String("<foo myatt=\"&lt;&quot;&apos;&amp;&gt;\"/>"), testling.serialize());
+ }
+
+ void testSerialize_EmptyAttributeValue() {
+ XMLElement testling("foo");
+ testling.setAttribute("myatt", "");
+
+ CPPUNIT_ASSERT_EQUAL(String("<foo myatt=\"\"/>"), testling.serialize());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XMLElementTest);
diff --git a/Swiften/Serializer/XML/XMLElement.cpp b/Swiften/Serializer/XML/XMLElement.cpp
new file mode 100644
index 0000000..71f4d0c
--- /dev/null
+++ b/Swiften/Serializer/XML/XMLElement.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+XMLElement::XMLElement(const String& tag, const String& xmlns) :
+ tag_(tag) {
+ if (!xmlns.isEmpty()) {
+ setAttribute("xmlns", xmlns);
+ }
+}
+
+String XMLElement::serialize() {
+ String result;
+ result += "<" + tag_;
+ typedef std::pair<String,String> Pair;
+ foreach(const Pair& p, attributes_) {
+ result += " " + p.first + "=\"" + p.second + "\"";
+ }
+
+ if (childNodes_.size() > 0) {
+ result += ">";
+ foreach (boost::shared_ptr<XMLNode> node, childNodes_) {
+ result += node->serialize();
+ }
+ result += "</" + tag_ + ">";
+ }
+ else {
+ result += "/>";
+ }
+ return result;
+}
+
+void XMLElement::setAttribute(const String& attribute, const String& value) {
+ String escapedValue(value);
+ escapedValue.replaceAll('&', "&amp;");
+ escapedValue.replaceAll('<', "&lt;");
+ escapedValue.replaceAll('>', "&gt;");
+ escapedValue.replaceAll('\'', "&apos;");
+ escapedValue.replaceAll('"', "&quot;");
+ attributes_[attribute] = escapedValue;
+}
+
+void XMLElement::addNode(boost::shared_ptr<XMLNode> node) {
+ childNodes_.push_back(node);
+}
+
+}
diff --git a/Swiften/Serializer/XML/XMLElement.h b/Swiften/Serializer/XML/XMLElement.h
new file mode 100644
index 0000000..f2eb8bf
--- /dev/null
+++ b/Swiften/Serializer/XML/XMLElement.h
@@ -0,0 +1,27 @@
+#ifndef SWIFTEN_XMLElement_H
+#define SWIFTEN_XMLElement_H
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <map>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Serializer/XML/XMLNode.h"
+
+namespace Swift {
+ class XMLElement : public XMLNode {
+ public:
+ XMLElement(const String& tag, const String& xmlns = "");
+
+ void setAttribute(const String& attribute, const String& value);
+ void addNode(boost::shared_ptr<XMLNode> node);
+
+ virtual String serialize();
+
+ private:
+ String tag_;
+ std::map<String, String> attributes_;
+ std::vector< boost::shared_ptr<XMLNode> > childNodes_;
+ };
+}
+#endif
diff --git a/Swiften/Serializer/XML/XMLNode.cpp b/Swiften/Serializer/XML/XMLNode.cpp
new file mode 100644
index 0000000..1bef64a
--- /dev/null
+++ b/Swiften/Serializer/XML/XMLNode.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Serializer/XML/XMLNode.h"
+
+namespace Swift {
+
+XMLNode::~XMLNode() {
+}
+
+}
diff --git a/Swiften/Serializer/XML/XMLNode.h b/Swiften/Serializer/XML/XMLNode.h
new file mode 100644
index 0000000..b31c0d6
--- /dev/null
+++ b/Swiften/Serializer/XML/XMLNode.h
@@ -0,0 +1,15 @@
+#ifndef SWIFTEN_XMLNode_H
+#define SWIFTEN_XMLNode_H
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class XMLNode {
+ public:
+ virtual ~XMLNode();
+
+ virtual String serialize() = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/XML/XMLRawTextNode.h b/Swiften/Serializer/XML/XMLRawTextNode.h
new file mode 100644
index 0000000..e5800c3
--- /dev/null
+++ b/Swiften/Serializer/XML/XMLRawTextNode.h
@@ -0,0 +1,21 @@
+#ifndef SWIFTEN_XMLRawTextNode_H
+#define SWIFTEN_XMLRawTextNode_H
+
+#include "Swiften/Serializer/XML/XMLNode.h"
+
+namespace Swift {
+ class XMLRawTextNode : public XMLNode {
+ public:
+ XMLRawTextNode(const String& text) : text_(text) {
+ }
+
+ String serialize() {
+ return text_;
+ }
+
+ private:
+ String text_;
+ };
+}
+
+#endif
diff --git a/Swiften/Serializer/XML/XMLTextNode.h b/Swiften/Serializer/XML/XMLTextNode.h
new file mode 100644
index 0000000..87dda53
--- /dev/null
+++ b/Swiften/Serializer/XML/XMLTextNode.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Swiften/Serializer/XML/XMLNode.h"
+
+namespace Swift {
+ class XMLTextNode : public XMLNode {
+ public:
+ XMLTextNode(const String& text) : text_(text) {
+ text_.replaceAll('&', "&amp;"); // Should come first
+ text_.replaceAll('<', "&lt;");
+ text_.replaceAll('>', "&gt;");
+ }
+
+ String serialize() {
+ return text_;
+ }
+
+ private:
+ String text_;
+ };
+}
diff --git a/Swiften/Serializer/XMPPSerializer.cpp b/Swiften/Serializer/XMPPSerializer.cpp
new file mode 100644
index 0000000..082cdf3
--- /dev/null
+++ b/Swiften/Serializer/XMPPSerializer.cpp
@@ -0,0 +1,77 @@
+#include "Swiften/Serializer/XMPPSerializer.h"
+
+#include <boost/bind.hpp>
+#include <iostream>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/CompressRequestSerializer.h"
+#include "Swiften/Serializer/CompressFailureSerializer.h"
+#include "Swiften/Serializer/StreamFeaturesSerializer.h"
+#include "Swiften/Serializer/AuthRequestSerializer.h"
+#include "Swiften/Serializer/AuthFailureSerializer.h"
+#include "Swiften/Serializer/AuthSuccessSerializer.h"
+#include "Swiften/Serializer/AuthChallengeSerializer.h"
+#include "Swiften/Serializer/AuthResponseSerializer.h"
+#include "Swiften/Serializer/StartTLSRequestSerializer.h"
+#include "Swiften/Serializer/StartTLSFailureSerializer.h"
+#include "Swiften/Serializer/TLSProceedSerializer.h"
+#include "Swiften/Serializer/MessageSerializer.h"
+#include "Swiften/Serializer/PresenceSerializer.h"
+#include "Swiften/Serializer/IQSerializer.h"
+
+namespace Swift {
+
+XMPPSerializer::XMPPSerializer(PayloadSerializerCollection* payloadSerializers) {
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new PresenceSerializer(payloadSerializers)));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new IQSerializer(payloadSerializers)));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new MessageSerializer(payloadSerializers)));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new CompressRequestSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new CompressFailureSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new AuthRequestSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new AuthFailureSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new AuthSuccessSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new AuthChallengeSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new AuthResponseSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new StartTLSRequestSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new StartTLSFailureSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new TLSProceedSerializer()));
+ serializers_.push_back(boost::shared_ptr<ElementSerializer>(new StreamFeaturesSerializer()));
+}
+
+String XMPPSerializer::serializeHeader(const ProtocolHeader& header) const {
+ String result = "<?xml version=\"1.0\"?><stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\"";
+ if (!header.getFrom().isEmpty()) {
+ result += " from=\"" + header.getFrom() + "\"";
+ }
+ if (!header.getTo().isEmpty()) {
+ result += " to=\"" + header.getTo() + "\"";
+ }
+ if (!header.getID().isEmpty()) {
+ result += " id=\"" + header.getID() + "\"";
+ }
+ if (!header.getVersion().isEmpty()) {
+ result += " version=\"" + header.getVersion() + "\"";
+ }
+ result += ">";
+ return result;
+}
+
+String XMPPSerializer::serializeElement(boost::shared_ptr<Element> element) const {
+ std::vector< boost::shared_ptr<ElementSerializer> >::const_iterator i = std::find_if(
+ serializers_.begin(), serializers_.end(),
+ boost::bind(&ElementSerializer::canSerialize, _1, element));
+ if (i != serializers_.end()) {
+ return (*i)->serialize(element);
+ }
+ else {
+ std::cerr << "Could not find serializer for " << typeid(*(element.get())).name() << std::endl;
+ return "";
+ }
+}
+
+String XMPPSerializer::serializeFooter() const {
+ return "</stream:stream>";
+}
+
+}
diff --git a/Swiften/Serializer/XMPPSerializer.h b/Swiften/Serializer/XMPPSerializer.h
new file mode 100644
index 0000000..38ba3ff
--- /dev/null
+++ b/Swiften/Serializer/XMPPSerializer.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Serializer/ElementSerializer.h"
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class CompressRequestSerializer;
+ class ProtocolHeader;
+
+ class XMPPSerializer {
+ public:
+ XMPPSerializer(PayloadSerializerCollection*);
+
+ String serializeHeader(const ProtocolHeader&) const;
+ String serializeElement(boost::shared_ptr<Element> stanza) const;
+ String serializeFooter() const;
+
+ private:
+ std::vector< boost::shared_ptr<ElementSerializer> > serializers_;
+ };
+}
diff --git a/Swiften/Server/ServerFromClientSession.cpp b/Swiften/Server/ServerFromClientSession.cpp
new file mode 100644
index 0000000..c974e18
--- /dev/null
+++ b/Swiften/Server/ServerFromClientSession.cpp
@@ -0,0 +1,97 @@
+#include "Swiften/Server/ServerFromClientSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Server/UserRegistry.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/ResourceBind.h"
+#include "Swiften/Elements/StartSession.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Elements/AuthFailure.h"
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/SASL/PLAINMessage.h"
+
+namespace Swift {
+
+ServerFromClientSession::ServerFromClientSession(
+ const String& id,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers,
+ UserRegistry* userRegistry) :
+ Session(connection, payloadParserFactories, payloadSerializers),
+ id_(id),
+ userRegistry_(userRegistry),
+ authenticated_(false),
+ initialized(false) {
+}
+
+
+void ServerFromClientSession::handleElement(boost::shared_ptr<Element> element) {
+ if (isInitialized()) {
+ onElementReceived(element);
+ }
+ else {
+ if (AuthRequest* authRequest = dynamic_cast<AuthRequest*>(element.get())) {
+ if (authRequest->getMechanism() != "PLAIN") {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
+ finishSession(NoSupportedAuthMechanismsError);
+ }
+ else {
+ PLAINMessage plainMessage(authRequest->getMessage());
+ if (userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), plainMessage.getPassword())) {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
+ user_ = plainMessage.getAuthenticationID();
+ authenticated_ = true;
+ getXMPPLayer()->resetParser();
+ }
+ else {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
+ finishSession(AuthenticationFailedError);
+ }
+ }
+ }
+ else if (IQ* iq = dynamic_cast<IQ*>(element.get())) {
+ if (boost::shared_ptr<ResourceBind> resourceBind = iq->getPayload<ResourceBind>()) {
+ setRemoteJID(JID(user_, getLocalJID().getDomain(), resourceBind->getResource()));
+ boost::shared_ptr<ResourceBind> resultResourceBind(new ResourceBind());
+ resultResourceBind->setJID(getRemoteJID());
+ getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind));
+ }
+ else if (iq->getPayload<StartSession>()) {
+ getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID()));
+ setInitialized();
+ }
+ }
+ }
+}
+
+void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) {
+ setLocalJID(JID("", incomingHeader.getTo()));
+ ProtocolHeader header;
+ header.setFrom(incomingHeader.getTo());
+ header.setID(id_);
+ getXMPPLayer()->writeHeader(header);
+
+ boost::shared_ptr<StreamFeatures> features(new StreamFeatures());
+ if (!authenticated_) {
+ features->addAuthenticationMechanism("PLAIN");
+ }
+ else {
+ features->setHasResourceBind();
+ features->setHasSession();
+ }
+ getXMPPLayer()->writeElement(features);
+}
+
+void ServerFromClientSession::setInitialized() {
+ initialized = true;
+ onSessionStarted();
+}
+
+
+}
diff --git a/Swiften/Server/ServerFromClientSession.h b/Swiften/Server/ServerFromClientSession.h
new file mode 100644
index 0000000..33826a4
--- /dev/null
+++ b/Swiften/Server/ServerFromClientSession.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Session/Session.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Network/Connection.h"
+
+namespace Swift {
+ class ProtocolHeader;
+ class Element;
+ class Stanza;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+ class StreamStack;
+ class UserRegistry;
+ class XMPPLayer;
+ class ConnectionLayer;
+ class Connection;
+ class ByteArray;
+
+ class ServerFromClientSession : public Session {
+ public:
+ ServerFromClientSession(
+ const String& id,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers,
+ UserRegistry* userRegistry);
+
+ boost::signal<void ()> onSessionStarted;
+
+ private:
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamStart(const ProtocolHeader& header);
+
+ void setInitialized();
+ bool isInitialized() const {
+ return initialized;
+ }
+
+ private:
+ String id_;
+ UserRegistry* userRegistry_;
+ bool authenticated_;
+ bool initialized;
+ String user_;
+ };
+}
diff --git a/Swiften/Server/ServerSession.cpp b/Swiften/Server/ServerSession.cpp
new file mode 100644
index 0000000..e62957c
--- /dev/null
+++ b/Swiften/Server/ServerSession.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Server/ServerSession.h"
+
+namespace Swift {
+
+ServerSession::~ServerSession() {
+}
+
+}
diff --git a/Swiften/Server/ServerSession.h b/Swiften/Server/ServerSession.h
new file mode 100644
index 0000000..1ebf68e
--- /dev/null
+++ b/Swiften/Server/ServerSession.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Stanza.h"
+
+namespace Swift {
+ class ServerSession {
+ public:
+ virtual ~ServerSession();
+
+ virtual const JID& getJID() const = 0;
+ virtual int getPriority() const = 0;
+
+ virtual void sendStanza(boost::shared_ptr<Stanza>) = 0;
+ };
+}
diff --git a/Swiften/Server/ServerStanzaRouter.cpp b/Swiften/Server/ServerStanzaRouter.cpp
new file mode 100644
index 0000000..5661de5
--- /dev/null
+++ b/Swiften/Server/ServerStanzaRouter.cpp
@@ -0,0 +1,67 @@
+#include "Swiften/Server/ServerStanzaRouter.h"
+#include "Swiften/Server/ServerSession.h"
+
+#include <cassert>
+#include <algorithm>
+
+namespace Swift {
+
+namespace {
+ struct PriorityLessThan {
+ bool operator()(const ServerSession* s1, const ServerSession* s2) const {
+ return s1->getPriority() < s2->getPriority();
+ }
+ };
+
+ struct HasJID {
+ HasJID(const JID& jid) : jid(jid) {}
+ bool operator()(const ServerSession* session) const {
+ return session->getJID().equals(jid, JID::WithResource);
+ }
+ JID jid;
+ };
+}
+
+ServerStanzaRouter::ServerStanzaRouter() {
+}
+
+bool ServerStanzaRouter::routeStanza(boost::shared_ptr<Stanza> stanza) {
+ JID to = stanza->getTo();
+ assert(to.isValid());
+
+ // For a full JID, first try to route to a session with the full JID
+ if (!to.isBare()) {
+ std::vector<ServerSession*>::const_iterator i = std::find_if(clientSessions_.begin(), clientSessions_.end(), HasJID(to));
+ if (i != clientSessions_.end()) {
+ (*i)->sendStanza(stanza);
+ return true;
+ }
+ }
+
+ // Look for candidate sessions
+ to = to.toBare();
+ std::vector<ServerSession*> candidateSessions;
+ for (std::vector<ServerSession*>::const_iterator i = clientSessions_.begin(); i != clientSessions_.end(); ++i) {
+ if ((*i)->getJID().equals(to, JID::WithoutResource) && (*i)->getPriority() >= 0) {
+ candidateSessions.push_back(*i);
+ }
+ }
+ if (candidateSessions.empty()) {
+ return false;
+ }
+
+ // Find the session with the highest priority
+ std::vector<ServerSession*>::const_iterator i = std::max_element(clientSessions_.begin(), clientSessions_.end(), PriorityLessThan());
+ (*i)->sendStanza(stanza);
+ return true;
+}
+
+void ServerStanzaRouter::addClientSession(ServerSession* clientSession) {
+ clientSessions_.push_back(clientSession);
+}
+
+void ServerStanzaRouter::removeClientSession(ServerSession* clientSession) {
+ clientSessions_.erase(std::remove(clientSessions_.begin(), clientSessions_.end(), clientSession), clientSessions_.end());
+}
+
+}
diff --git a/Swiften/Server/ServerStanzaRouter.h b/Swiften/Server/ServerStanzaRouter.h
new file mode 100644
index 0000000..057a2ea
--- /dev/null
+++ b/Swiften/Server/ServerStanzaRouter.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/Stanza.h"
+
+namespace Swift {
+ class ServerSession;
+
+ class ServerStanzaRouter {
+ public:
+ ServerStanzaRouter();
+
+ bool routeStanza(boost::shared_ptr<Stanza>);
+
+ void addClientSession(ServerSession*);
+ void removeClientSession(ServerSession*);
+
+ private:
+ std::vector<ServerSession*> clientSessions_;
+ };
+}
diff --git a/Swiften/Server/SimpleUserRegistry.cpp b/Swiften/Server/SimpleUserRegistry.cpp
new file mode 100644
index 0000000..1a6743a
--- /dev/null
+++ b/Swiften/Server/SimpleUserRegistry.cpp
@@ -0,0 +1,17 @@
+#include "Swiften/Server/SimpleUserRegistry.h"
+
+namespace Swift {
+
+SimpleUserRegistry::SimpleUserRegistry() {
+}
+
+bool SimpleUserRegistry::isValidUserPassword(const JID& user, const String& password) const {
+ std::map<JID,String>::const_iterator i = users.find(user);
+ return i != users.end() ? i->second == password : false;
+}
+
+void SimpleUserRegistry::addUser(const JID& user, const String& password) {
+ users.insert(std::make_pair(user, password));
+}
+
+}
diff --git a/Swiften/Server/SimpleUserRegistry.h b/Swiften/Server/SimpleUserRegistry.h
new file mode 100644
index 0000000..253025d
--- /dev/null
+++ b/Swiften/Server/SimpleUserRegistry.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <map>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Server/UserRegistry.h"
+
+namespace Swift {
+ class String;
+
+ class SimpleUserRegistry : public UserRegistry {
+ public:
+ SimpleUserRegistry();
+
+ virtual bool isValidUserPassword(const JID& user, const String& password) const;
+ void addUser(const JID& user, const String& password);
+
+ private:
+ std::map<JID, String> users;
+ };
+}
diff --git a/Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp b/Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp
new file mode 100644
index 0000000..03a607a
--- /dev/null
+++ b/Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp
@@ -0,0 +1,144 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Server/ServerStanzaRouter.h"
+#include "Swiften/Server/ServerSession.h"
+
+using namespace Swift;
+
+class ServerStanzaRouterTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ServerStanzaRouterTest);
+ CPPUNIT_TEST(testRouteStanza_FullJID);
+ CPPUNIT_TEST(testRouteStanza_FullJIDWithNegativePriority);
+ CPPUNIT_TEST(testRouteStanza_FullJIDWithOnlyBareJIDMatchingSession);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithoutMatchingSession);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithMultipleSessions);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithOnlyNegativePriorities);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithChangingPresence);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ServerStanzaRouterTest() {}
+
+ void setUp() {
+ }
+
+ void tearDown() {
+ }
+
+ void testRouteStanza_FullJID() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Bla"), 0);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Baz"), 0);
+ testling.addClientSession(&session2);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com/Baz"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session2.sentStanzas.size()));
+ }
+
+ void testRouteStanza_FullJIDWithNegativePriority() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Bla"), -1);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Baz"), 0);
+ testling.addClientSession(&session2);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com/Bla"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session2.sentStanzas.size()));
+ }
+
+ void testRouteStanza_FullJIDWithOnlyBareJIDMatchingSession() {
+ ServerStanzaRouter testling;
+ MockServerSession session(JID("foo@bar.com/Bla"), 0);
+ testling.addClientSession(&session);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com/Baz"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session.sentStanzas.size()));
+ }
+
+ void testRouteStanza_BareJIDWithoutMatchingSession() {
+ ServerStanzaRouter testling;
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testRouteStanza_BareJIDWithMultipleSessions() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Bla"), 1);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Baz"), 8);
+ testling.addClientSession(&session2);
+ MockServerSession session3(JID("foo@bar.com/Bar"), 5);
+ testling.addClientSession(&session3);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session2.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session3.sentStanzas.size()));
+ }
+
+ void testRouteStanza_BareJIDWithOnlyNegativePriorities() {
+ ServerStanzaRouter testling;
+ MockServerSession session(JID("foo@bar.com/Bla"), -1);
+ testling.addClientSession(&session);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testRouteStanza_BareJIDWithChangingPresence() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Baz"), 8);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Bar"), 5);
+ testling.addClientSession(&session2);
+
+ session1.priority = 3;
+ session2.priority = 4;
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session2.sentStanzas.size()));
+ }
+
+ private:
+ boost::shared_ptr<Message> createMessageTo(const String& recipient) {
+ boost::shared_ptr<Message> message(new Message());
+ message->setTo(JID(recipient));
+ return message;
+ }
+
+ class MockServerSession : public ServerSession {
+ public:
+ MockServerSession(const JID& jid, int priority) : jid(jid), priority(priority) {}
+
+ virtual const JID& getJID() const { return jid; }
+ virtual int getPriority() const { return priority; }
+
+ virtual void sendStanza(boost::shared_ptr<Stanza> stanza) {
+ sentStanzas.push_back(stanza);
+ }
+
+ JID jid;
+ int priority;
+ std::vector< boost::shared_ptr<Stanza> > sentStanzas;
+ };
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ServerStanzaRouterTest);
diff --git a/Swiften/Server/UserRegistry.cpp b/Swiften/Server/UserRegistry.cpp
new file mode 100644
index 0000000..d1a509f
--- /dev/null
+++ b/Swiften/Server/UserRegistry.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Server/UserRegistry.h"
+
+namespace Swift {
+
+UserRegistry::~UserRegistry() {
+}
+
+}
diff --git a/Swiften/Server/UserRegistry.h b/Swiften/Server/UserRegistry.h
new file mode 100644
index 0000000..5ced019
--- /dev/null
+++ b/Swiften/Server/UserRegistry.h
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace Swift {
+ class String;
+ class JID;
+
+ class UserRegistry {
+ public:
+ virtual ~UserRegistry();
+
+ virtual bool isValidUserPassword(const JID& user, const String& password) const = 0;
+ };
+}
diff --git a/Swiften/Session/BasicSessionStream.cpp b/Swiften/Session/BasicSessionStream.cpp
new file mode 100644
index 0000000..ed7f1eb
--- /dev/null
+++ b/Swiften/Session/BasicSessionStream.cpp
@@ -0,0 +1,140 @@
+#include "Swiften/Session/BasicSessionStream.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/StreamStack.h"
+#include "Swiften/StreamStack/ConnectionLayer.h"
+#include "Swiften/StreamStack/WhitespacePingLayer.h"
+#include "Swiften/StreamStack/CompressionLayer.h"
+#include "Swiften/StreamStack/TLSLayer.h"
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+
+namespace Swift {
+
+BasicSessionStream::BasicSessionStream(boost::shared_ptr<Connection> connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, TLSLayerFactory* tlsLayerFactory, TimerFactory* timerFactory) : available(false), connection(connection), payloadParserFactories(payloadParserFactories), payloadSerializers(payloadSerializers), tlsLayerFactory(tlsLayerFactory), timerFactory(timerFactory) {
+}
+
+void BasicSessionStream::initialize() {
+ xmppLayer = boost::shared_ptr<XMPPLayer>(
+ new XMPPLayer(payloadParserFactories, payloadSerializers));
+ xmppLayer->onStreamStart.connect(boost::bind(&BasicSessionStream::handleStreamStartReceived, shared_from_this(), _1));
+ xmppLayer->onElement.connect(boost::bind(&BasicSessionStream::handleElementReceived, shared_from_this(), _1));
+ xmppLayer->onError.connect(boost::bind(
+ &BasicSessionStream::handleXMPPError, shared_from_this()));
+ xmppLayer->onDataRead.connect(boost::bind(&BasicSessionStream::handleDataRead, shared_from_this(), _1));
+ xmppLayer->onWriteData.connect(boost::bind(&BasicSessionStream::handleDataWritten, shared_from_this(), _1));
+
+ connection->onDisconnected.connect(boost::bind(&BasicSessionStream::handleConnectionError, shared_from_this(), _1));
+ connectionLayer = boost::shared_ptr<ConnectionLayer>(
+ new ConnectionLayer(connection));
+
+ streamStack = new StreamStack(xmppLayer, connectionLayer);
+
+ available = true;
+}
+
+BasicSessionStream::~BasicSessionStream() {
+ delete streamStack;
+}
+
+void BasicSessionStream::writeHeader(const ProtocolHeader& header) {
+ assert(available);
+ xmppLayer->writeHeader(header);
+}
+
+void BasicSessionStream::writeElement(boost::shared_ptr<Element> element) {
+ assert(available);
+ xmppLayer->writeElement(element);
+}
+
+void BasicSessionStream::writeFooter() {
+ assert(available);
+ xmppLayer->writeFooter();
+}
+
+bool BasicSessionStream::isAvailable() {
+ return available;
+}
+
+bool BasicSessionStream::supportsTLSEncryption() {
+ return tlsLayerFactory && tlsLayerFactory->canCreate();
+}
+
+void BasicSessionStream::addTLSEncryption() {
+ assert(available);
+ tlsLayer = tlsLayerFactory->createTLSLayer();
+ if (hasTLSCertificate() && !tlsLayer->setClientCertificate(getTLSCertificate())) {
+ onError(boost::shared_ptr<Error>(new Error(Error::InvalidTLSCertificateError)));
+ }
+ else {
+ streamStack->addLayer(tlsLayer);
+ tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, shared_from_this()));
+ tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, shared_from_this()));
+ tlsLayer->connect();
+ }
+}
+
+void BasicSessionStream::addZLibCompression() {
+ boost::shared_ptr<CompressionLayer> compressionLayer(new CompressionLayer());
+ streamStack->addLayer(compressionLayer);
+}
+
+void BasicSessionStream::setWhitespacePingEnabled(bool enabled) {
+ if (enabled) {
+ if (!whitespacePingLayer) {
+ whitespacePingLayer = boost::shared_ptr<WhitespacePingLayer>(new WhitespacePingLayer(timerFactory));
+ streamStack->addLayer(whitespacePingLayer);
+ }
+ whitespacePingLayer->setActive();
+ }
+ else if (whitespacePingLayer) {
+ whitespacePingLayer->setInactive();
+ }
+}
+
+void BasicSessionStream::resetXMPPParser() {
+ xmppLayer->resetParser();
+}
+
+void BasicSessionStream::handleStreamStartReceived(const ProtocolHeader& header) {
+ onStreamStartReceived(header);
+}
+
+void BasicSessionStream::handleElementReceived(boost::shared_ptr<Element> element) {
+ onElementReceived(element);
+}
+
+void BasicSessionStream::handleXMPPError() {
+ available = false;
+ onError(boost::shared_ptr<Error>(new Error(Error::ParseError)));
+}
+
+void BasicSessionStream::handleTLSConnected() {
+ onTLSEncrypted();
+}
+
+void BasicSessionStream::handleTLSError() {
+ available = false;
+ onError(boost::shared_ptr<Error>(new Error(Error::TLSError)));
+}
+
+void BasicSessionStream::handleConnectionError(const boost::optional<Connection::Error>& error) {
+ available = false;
+ if (error == Connection::ReadError) {
+ onError(boost::shared_ptr<Error>(new Error(Error::ConnectionReadError)));
+ }
+ else {
+ onError(boost::shared_ptr<Error>(new Error(Error::ConnectionWriteError)));
+ }
+}
+
+void BasicSessionStream::handleDataRead(const ByteArray& data) {
+ onDataRead(String(data.getData(), data.getSize()));
+}
+
+void BasicSessionStream::handleDataWritten(const ByteArray& data) {
+ onDataWritten(String(data.getData(), data.getSize()));
+}
+
+};
diff --git a/Swiften/Session/BasicSessionStream.h b/Swiften/Session/BasicSessionStream.h
new file mode 100644
index 0000000..8618458
--- /dev/null
+++ b/Swiften/Session/BasicSessionStream.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Session/SessionStream.h"
+
+namespace Swift {
+ class TLSLayerFactory;
+ class TLSLayer;
+ class TimerFactory;
+ class WhitespacePingLayer;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+ class StreamStack;
+ class XMPPLayer;
+ class ConnectionLayer;
+ class CompressionLayer;
+
+ class BasicSessionStream :
+ public SessionStream,
+ public boost::enable_shared_from_this<BasicSessionStream> {
+ public:
+ BasicSessionStream(
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers,
+ TLSLayerFactory* tlsLayerFactory,
+ TimerFactory* whitespacePingLayerFactory
+ );
+ ~BasicSessionStream();
+
+ void initialize();
+
+ virtual bool isAvailable();
+
+ virtual void writeHeader(const ProtocolHeader& header);
+ virtual void writeElement(boost::shared_ptr<Element>);
+ virtual void writeFooter();
+
+ virtual void addZLibCompression();
+
+ virtual bool supportsTLSEncryption();
+ virtual void addTLSEncryption();
+
+ virtual void setWhitespacePingEnabled(bool);
+
+ virtual void resetXMPPParser();
+
+ private:
+ void handleConnectionError(const boost::optional<Connection::Error>& error);
+ void handleXMPPError();
+ void handleTLSConnected();
+ void handleTLSError();
+ void handleStreamStartReceived(const ProtocolHeader&);
+ void handleElementReceived(boost::shared_ptr<Element>);
+ void handleDataRead(const ByteArray& data);
+ void handleDataWritten(const ByteArray& data);
+
+ private:
+ bool available;
+ boost::shared_ptr<Connection> connection;
+ PayloadParserFactoryCollection* payloadParserFactories;
+ PayloadSerializerCollection* payloadSerializers;
+ TLSLayerFactory* tlsLayerFactory;
+ TimerFactory* timerFactory;
+ boost::shared_ptr<XMPPLayer> xmppLayer;
+ boost::shared_ptr<ConnectionLayer> connectionLayer;
+ StreamStack* streamStack;
+ boost::shared_ptr<CompressionLayer> compressionLayer;
+ boost::shared_ptr<TLSLayer> tlsLayer;
+ boost::shared_ptr<WhitespacePingLayer> whitespacePingLayer;
+ };
+}
diff --git a/Swiften/Session/Session.cpp b/Swiften/Session/Session.cpp
new file mode 100644
index 0000000..64456db
--- /dev/null
+++ b/Swiften/Session/Session.cpp
@@ -0,0 +1,82 @@
+#include "Swiften/Session/Session.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/StreamStack.h"
+
+namespace Swift {
+
+Session::Session(
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers) :
+ connection(connection),
+ payloadParserFactories(payloadParserFactories),
+ payloadSerializers(payloadSerializers),
+ streamStack(0),
+ finishing(false) {
+}
+
+Session::~Session() {
+ delete streamStack;
+}
+
+void Session::startSession() {
+ initializeStreamStack();
+ handleSessionStarted();
+}
+
+void Session::finishSession() {
+ finishing = true;
+ connection->disconnect();
+ handleSessionFinished(boost::optional<SessionError>());
+ finishing = false;
+ onSessionFinished(boost::optional<SessionError>());
+}
+
+void Session::finishSession(const SessionError& error) {
+ finishing = true;
+ connection->disconnect();
+ handleSessionFinished(boost::optional<SessionError>(error));
+ finishing = false;
+ onSessionFinished(boost::optional<SessionError>(error));
+}
+
+void Session::initializeStreamStack() {
+ xmppLayer = boost::shared_ptr<XMPPLayer>(
+ new XMPPLayer(payloadParserFactories, payloadSerializers));
+ xmppLayer->onStreamStart.connect(
+ boost::bind(&Session::handleStreamStart, shared_from_this(), _1));
+ xmppLayer->onElement.connect(boost::bind(&Session::handleElement, shared_from_this(), _1));
+ xmppLayer->onError.connect(
+ boost::bind(&Session::finishSession, shared_from_this(), XMLError));
+ xmppLayer->onDataRead.connect(boost::bind(boost::ref(onDataRead), _1));
+ xmppLayer->onWriteData.connect(boost::bind(boost::ref(onDataWritten), _1));
+ connection->onDisconnected.connect(
+ boost::bind(&Session::handleDisconnected, shared_from_this(), _1));
+ connectionLayer = boost::shared_ptr<ConnectionLayer>(new ConnectionLayer(connection));
+ streamStack = new StreamStack(xmppLayer, connectionLayer);
+}
+
+void Session::sendElement(boost::shared_ptr<Element> stanza) {
+ xmppLayer->writeElement(stanza);
+}
+
+void Session::handleDisconnected(const boost::optional<Connection::Error>& connectionError) {
+ if (connectionError) {
+ switch (*connectionError) {
+ case Connection::ReadError:
+ finishSession(ConnectionReadError);
+ break;
+ case Connection::WriteError:
+ finishSession(ConnectionWriteError);
+ break;
+ }
+ }
+ else {
+ finishSession();
+ }
+}
+
+}
diff --git a/Swiften/Session/Session.h b/Swiften/Session/Session.h
new file mode 100644
index 0000000..d63254a
--- /dev/null
+++ b/Swiften/Session/Session.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <boost/optional.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/StreamStack/ConnectionLayer.h"
+
+namespace Swift {
+ class ProtocolHeader;
+ class StreamStack;
+ class JID;
+ class Element;
+ class ByteArray;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+ class XMPPLayer;
+
+ class Session : public boost::enable_shared_from_this<Session> {
+ public:
+ enum SessionError {
+ ConnectionReadError,
+ ConnectionWriteError,
+ XMLError,
+ AuthenticationFailedError,
+ NoSupportedAuthMechanismsError,
+ UnexpectedElementError,
+ ResourceBindError,
+ SessionStartError,
+ TLSError,
+ ClientCertificateLoadError,
+ ClientCertificateError
+ };
+
+ Session(
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers);
+ virtual ~Session();
+
+ void startSession();
+ void finishSession();
+
+ void sendElement(boost::shared_ptr<Element>);
+
+ const JID& getLocalJID() const {
+ return localJID;
+ }
+
+ const JID& getRemoteJID() const {
+ return remoteJID;
+ }
+
+ boost::signal<void (boost::shared_ptr<Element>)> onElementReceived;
+ boost::signal<void (const boost::optional<SessionError>&)> onSessionFinished;
+ boost::signal<void (const ByteArray&)> onDataWritten;
+ boost::signal<void (const ByteArray&)> onDataRead;
+
+ protected:
+ void setRemoteJID(const JID& j) {
+ remoteJID = j;
+ }
+
+ void setLocalJID(const JID& j) {
+ localJID = j;
+ }
+
+ void finishSession(const SessionError&);
+
+ virtual void handleSessionStarted() {}
+ virtual void handleSessionFinished(const boost::optional<SessionError>&) {}
+ virtual void handleElement(boost::shared_ptr<Element>) = 0;
+ virtual void handleStreamStart(const ProtocolHeader&) = 0;
+
+ void initializeStreamStack();
+
+ boost::shared_ptr<XMPPLayer> getXMPPLayer() const {
+ return xmppLayer;
+ }
+
+ StreamStack* getStreamStack() const {
+ return streamStack;
+ }
+
+ void setFinished();
+
+ private:
+ void handleDisconnected(const boost::optional<Connection::Error>& error);
+
+ private:
+ JID localJID;
+ JID remoteJID;
+ boost::shared_ptr<Connection> connection;
+ PayloadParserFactoryCollection* payloadParserFactories;
+ PayloadSerializerCollection* payloadSerializers;
+ boost::shared_ptr<XMPPLayer> xmppLayer;
+ boost::shared_ptr<ConnectionLayer> connectionLayer;
+ StreamStack* streamStack;
+ bool finishing;
+ };
+}
diff --git a/Swiften/Session/SessionStream.cpp b/Swiften/Session/SessionStream.cpp
new file mode 100644
index 0000000..1d73d0f
--- /dev/null
+++ b/Swiften/Session/SessionStream.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Session/SessionStream.h"
+
+namespace Swift {
+
+SessionStream::~SessionStream() {
+}
+
+};
diff --git a/Swiften/Session/SessionStream.h b/Swiften/Session/SessionStream.h
new file mode 100644
index 0000000..8c64ccf
--- /dev/null
+++ b/Swiften/Session/SessionStream.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Base/Error.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+namespace Swift {
+ class SessionStream {
+ public:
+ class Error : public Swift::Error {
+ public:
+ enum Type {
+ ParseError,
+ TLSError,
+ InvalidTLSCertificateError,
+ ConnectionReadError,
+ ConnectionWriteError
+ };
+
+ Error(Type type) : type(type) {}
+
+ Type type;
+ };
+
+ virtual ~SessionStream();
+
+ virtual bool isAvailable() = 0;
+
+ virtual void writeHeader(const ProtocolHeader& header) = 0;
+ virtual void writeFooter() = 0;
+ virtual void writeElement(boost::shared_ptr<Element>) = 0;
+
+ virtual void addZLibCompression() = 0;
+
+ virtual bool supportsTLSEncryption() = 0;
+ virtual void addTLSEncryption() = 0;
+ virtual void setWhitespacePingEnabled(bool enabled) = 0;
+
+ virtual void resetXMPPParser() = 0;
+
+ void setTLSCertificate(const PKCS12Certificate& cert) {
+ certificate = cert;
+ }
+
+ virtual bool hasTLSCertificate() {
+ return !certificate.isNull();
+ }
+
+
+ boost::signal<void (const ProtocolHeader&)> onStreamStartReceived;
+ boost::signal<void (boost::shared_ptr<Element>)> onElementReceived;
+ boost::signal<void (boost::shared_ptr<Error>)> onError;
+ boost::signal<void ()> onTLSEncrypted;
+ boost::signal<void (const String&)> onDataRead;
+ boost::signal<void (const String&)> onDataWritten;
+
+ protected:
+ const PKCS12Certificate& getTLSCertificate() const {
+ return certificate;
+ }
+
+ private:
+ PKCS12Certificate certificate;
+ };
+}
diff --git a/Swiften/Session/SessionTracer.h b/Swiften/Session/SessionTracer.h
new file mode 100644
index 0000000..29a07e0
--- /dev/null
+++ b/Swiften/Session/SessionTracer.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <iostream>
+
+#include "Swiften/Session/Session.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class SessionTracer {
+ public:
+ SessionTracer(boost::shared_ptr<Session> session) : session(session) {
+ session->onDataRead.connect(boost::bind(&SessionTracer::printData, this, '<', _1));
+ session->onDataWritten.connect(boost::bind(&SessionTracer::printData, this, '>', _1));
+ }
+
+ private:
+ void printData(char direction, const ByteArray& data) {
+ std::cerr << direction << direction << " " << session->getLocalJID() << " ";
+ for (unsigned int i = 0; i < 72 - session->getLocalJID().toString().getLength() - session->getRemoteJID().toString().getLength(); ++i) {
+ std::cerr << direction;
+ }
+ std::cerr << " " << session->getRemoteJID()<< " " << direction << direction << std::endl;
+ std::cerr << String(data.getData(), data.getSize()) << std::endl;
+ }
+
+ boost::shared_ptr<Session> session;
+ };
+}
diff --git a/Swiften/Settings/SettingsProvider.h b/Swiften/Settings/SettingsProvider.h
new file mode 100644
index 0000000..d07e790
--- /dev/null
+++ b/Swiften/Settings/SettingsProvider.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_SettingsProvider_H
+#define SWIFTEN_SettingsProvider_H
+
+#include "Swiften/Base/String.h"
+
+#include <vector>
+
+namespace Swift {
+
+class SettingsProvider {
+ public:
+ virtual ~SettingsProvider() {}
+ virtual String getStringSetting(const String &settingPath) = 0;
+ virtual void storeString(const String &settingPath, const String &settingValue) = 0;
+ virtual bool getBoolSetting(const String &settingPath, bool defaultValue) = 0;
+ virtual void storeBool(const String &settingPath, bool settingValue) = 0;
+ virtual std::vector<String> getAvailableProfiles() = 0;
+ virtual void createProfile(const String& profile) = 0;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/StreamStack/CompressionLayer.h b/Swiften/StreamStack/CompressionLayer.h
new file mode 100644
index 0000000..6a8f2b3
--- /dev/null
+++ b/Swiften/StreamStack/CompressionLayer.h
@@ -0,0 +1,48 @@
+#ifndef SWIFTEN_COMPRESSIONLAYER_H
+#define SWIFTEN_COMPRESSIONLAYER_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+#include "Swiften/Compress/ZLibException.h"
+#include "Swiften/Compress/ZLibCompressor.h"
+#include "Swiften/Compress/ZLibDecompressor.h"
+
+namespace Swift {
+ class ZLibCompressor;
+ class ZLibDecompressor;
+
+ class CompressionLayer : public StreamLayer, boost::noncopyable {
+ public:
+ CompressionLayer() {}
+
+ virtual void writeData(const ByteArray& data) {
+ try {
+ onWriteData(compressor_.process(data));
+ }
+ catch (const ZLibException& e) {
+ onError();
+ }
+ }
+
+ virtual void handleDataRead(const ByteArray& data) {
+ try {
+ onDataRead(decompressor_.process(data));
+ }
+ catch (const ZLibException& e) {
+ onError();
+ }
+ }
+
+ public:
+ boost::signal<void ()> onError;
+
+ private:
+ ZLibCompressor compressor_;
+ ZLibDecompressor decompressor_;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/ConnectionLayer.h b/Swiften/StreamStack/ConnectionLayer.h
new file mode 100644
index 0000000..7688f78
--- /dev/null
+++ b/Swiften/StreamStack/ConnectionLayer.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/Network/Connection.h"
+
+namespace Swift {
+ class ConnectionLayer : public LowLayer {
+ public:
+ ConnectionLayer(boost::shared_ptr<Connection> connection) : connection(connection) {
+ connection->onDataRead.connect(onDataRead);
+ }
+
+ void writeData(const ByteArray& data) {
+ connection->write(data);
+ }
+
+ private:
+ boost::shared_ptr<Connection> connection;
+ };
+}
diff --git a/Swiften/StreamStack/HighLayer.cpp b/Swiften/StreamStack/HighLayer.cpp
new file mode 100644
index 0000000..2f5e1df
--- /dev/null
+++ b/Swiften/StreamStack/HighLayer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/StreamStack/HighLayer.h"
+
+namespace Swift {
+
+HighLayer::~HighLayer() {
+}
+
+}
diff --git a/Swiften/StreamStack/HighLayer.h b/Swiften/StreamStack/HighLayer.h
new file mode 100644
index 0000000..bd6c6e6
--- /dev/null
+++ b/Swiften/StreamStack/HighLayer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_HIGHLAYER_H
+#define SWIFTEN_HIGHLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class HighLayer {
+ public:
+ virtual ~HighLayer();
+
+ virtual void handleDataRead(const ByteArray& data) = 0;
+
+ boost::signal<void (const ByteArray&)> onWriteData;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/LowLayer.cpp b/Swiften/StreamStack/LowLayer.cpp
new file mode 100644
index 0000000..24aa7a2
--- /dev/null
+++ b/Swiften/StreamStack/LowLayer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/StreamStack/LowLayer.h"
+
+namespace Swift {
+
+LowLayer::~LowLayer() {
+}
+
+}
diff --git a/Swiften/StreamStack/LowLayer.h b/Swiften/StreamStack/LowLayer.h
new file mode 100644
index 0000000..763cfc4
--- /dev/null
+++ b/Swiften/StreamStack/LowLayer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_LOWLAYER_H
+#define SWIFTEN_LOWLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class LowLayer {
+ public:
+ virtual ~LowLayer();
+
+ virtual void writeData(const ByteArray& data) = 0;
+
+ boost::signal<void (const ByteArray&)> onDataRead;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/OpenSSLLayer.cpp b/Swiften/StreamStack/OpenSSLLayer.cpp
new file mode 100644
index 0000000..da93e4e
--- /dev/null
+++ b/Swiften/StreamStack/OpenSSLLayer.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/StreamStack/OpenSSLLayer.h"
+
+namespace Swift {
+
+OpenSSLLayer::OpenSSLLayer() {
+ context_.onDataForNetwork.connect(onWriteData);
+ context_.onDataForApplication.connect(onDataRead);
+ context_.onConnected.connect(onConnected);
+ context_.onError.connect(onError);
+}
+
+void OpenSSLLayer::connect() {
+ context_.connect();
+}
+
+void OpenSSLLayer::writeData(const ByteArray& data) {
+ context_.handleDataFromApplication(data);
+}
+
+void OpenSSLLayer::handleDataRead(const ByteArray& data) {
+ context_.handleDataFromNetwork(data);
+}
+
+bool OpenSSLLayer::setClientCertificate(const PKCS12Certificate& certificate) {
+ return context_.setClientCertificate(certificate);
+}
+
+}
diff --git a/Swiften/StreamStack/OpenSSLLayer.h b/Swiften/StreamStack/OpenSSLLayer.h
new file mode 100644
index 0000000..615c75e
--- /dev/null
+++ b/Swiften/StreamStack/OpenSSLLayer.h
@@ -0,0 +1,27 @@
+#ifndef SWIFTEN_OpenSSLLayer_H
+#define SWIFTEN_OpenSSLLayer_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/TLSLayer.h"
+#include "Swiften/TLS/OpenSSL/OpenSSLContext.h"
+
+namespace Swift {
+ class OpenSSLLayer : public TLSLayer, boost::noncopyable {
+ public:
+ OpenSSLLayer();
+
+ virtual void connect();
+ virtual bool setClientCertificate(const PKCS12Certificate&);
+
+ virtual void writeData(const ByteArray& data);
+ virtual void handleDataRead(const ByteArray& data);
+
+ private:
+ OpenSSLContext context_;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.cpp b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp
new file mode 100644
index 0000000..cdcb8a7
--- /dev/null
+++ b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp
@@ -0,0 +1,31 @@
+#include "Swiften/StreamStack/PlatformTLSLayerFactory.h"
+
+#include <cassert>
+
+#ifdef HAVE_OPENSSL
+#include "Swiften/StreamStack/OpenSSLLayer.h"
+#endif
+
+namespace Swift {
+
+PlatformTLSLayerFactory::PlatformTLSLayerFactory() {
+}
+
+bool PlatformTLSLayerFactory::canCreate() const {
+#ifdef HAVE_OPENSSL
+ return true;
+#else
+ return false;
+#endif
+}
+
+boost::shared_ptr<TLSLayer> PlatformTLSLayerFactory::createTLSLayer() {
+#ifdef HAVE_OPENSSL
+ return boost::shared_ptr<TLSLayer>(new OpenSSLLayer());
+#else
+ assert(false);
+ return boost::shared_ptr<TLSLayer>();
+#endif
+}
+
+}
diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.h b/Swiften/StreamStack/PlatformTLSLayerFactory.h
new file mode 100644
index 0000000..6c343b0
--- /dev/null
+++ b/Swiften/StreamStack/PlatformTLSLayerFactory.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+
+namespace Swift {
+ class PlatformTLSLayerFactory : public TLSLayerFactory {
+ public:
+ PlatformTLSLayerFactory();
+
+ bool canCreate() const;
+ virtual boost::shared_ptr<TLSLayer> createTLSLayer();
+ };
+}
diff --git a/Swiften/StreamStack/SConscript b/Swiften/StreamStack/SConscript
new file mode 100644
index 0000000..449a39b
--- /dev/null
+++ b/Swiften/StreamStack/SConscript
@@ -0,0 +1,21 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env["OPENSSL_FLAGS"])
+
+sources = [
+ "HighLayer.cpp",
+ "LowLayer.cpp",
+ "PlatformTLSLayerFactory.cpp",
+ "StreamStack.cpp",
+ "TLSLayerFactory.cpp",
+ "WhitespacePingLayer.cpp",
+ "XMPPLayer.cpp",
+ ]
+
+if myenv.get("HAVE_OPENSSL", 0) :
+ myenv.Append(CPPDEFINES = "HAVE_OPENSSL")
+ sources += ["OpenSSLLayer.cpp"]
+
+objects = myenv.StaticObject(sources)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/StreamStack/StreamLayer.h b/Swiften/StreamStack/StreamLayer.h
new file mode 100644
index 0000000..6ea2051
--- /dev/null
+++ b/Swiften/StreamStack/StreamLayer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_STREAMLAYER_H
+#define SWIFTEN_STREAMLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/HighLayer.h"
+
+namespace Swift {
+ class StreamLayer : public LowLayer, public HighLayer {
+ public:
+ StreamLayer() {}
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/StreamStack.cpp b/Swiften/StreamStack/StreamStack.cpp
new file mode 100644
index 0000000..9eb9b4e
--- /dev/null
+++ b/Swiften/StreamStack/StreamStack.cpp
@@ -0,0 +1,40 @@
+#include "Swiften/StreamStack/StreamStack.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+
+namespace Swift {
+
+StreamStack::StreamStack(boost::shared_ptr<XMPPLayer> xmppLayer, boost::shared_ptr<LowLayer> physicalLayer) : xmppLayer_(xmppLayer), physicalLayer_(physicalLayer) {
+ xmppReadSlotConnection_ = physicalLayer_->onDataRead.connect(boost::bind(&XMPPLayer::parseData, xmppLayer_, _1));
+ xmppWriteSignalConnection_ = xmppLayer_->onWriteData.connect(boost::bind(&LowLayer::writeData, physicalLayer_, _1));
+}
+
+StreamStack::~StreamStack() {
+ // Disconnect the write signal connections to break cyclic signal
+ // dependencies. The read signal connections have
+ // to remain, since these can be reached from the main event loop.
+ xmppWriteSignalConnection_.disconnect();
+ foreach(const boost::bsignals::connection& connection, writeSignalConnections_) {
+ connection.disconnect();
+ }
+}
+
+void StreamStack::addLayer(boost::shared_ptr<StreamLayer> newLayer) {
+ xmppReadSlotConnection_.disconnect();
+ xmppWriteSignalConnection_.disconnect();
+
+ boost::shared_ptr<LowLayer> lowLayer = (layers_.empty() ? physicalLayer_ : *layers_.rbegin());
+
+ lowLayer->onDataRead.connect(boost::bind(&HighLayer::handleDataRead, newLayer, _1), boost::bsignals::at_front);
+ writeSignalConnections_.push_back(newLayer->onWriteData.connect(boost::bind(&LowLayer::writeData, lowLayer, _1), boost::bsignals::at_front));
+ xmppWriteSignalConnection_ = xmppLayer_->onWriteData.connect(boost::bind(&LowLayer::writeData, newLayer, _1), boost::bsignals::at_front);
+ xmppReadSlotConnection_ = newLayer->onDataRead.connect(boost::bind(&XMPPLayer::parseData, xmppLayer_, _1), boost::bsignals::at_front);
+ layers_.push_back(newLayer);
+}
+
+}
diff --git a/Swiften/StreamStack/StreamStack.h b/Swiften/StreamStack/StreamStack.h
new file mode 100644
index 0000000..87c522d
--- /dev/null
+++ b/Swiften/StreamStack/StreamStack.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <vector>
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+ class XMPPLayer;
+ class LowLayer;
+ class StreamLayer;
+
+ class StreamStack {
+ public:
+ StreamStack(boost::shared_ptr<XMPPLayer> xmppLayer, boost::shared_ptr<LowLayer> physicalLayer);
+ ~StreamStack();
+
+ void addLayer(boost::shared_ptr<StreamLayer>);
+
+ boost::shared_ptr<XMPPLayer> getXMPPLayer() const {
+ return xmppLayer_;
+ }
+
+ template<typename T> boost::shared_ptr<T> getLayer() {
+ foreach(const boost::shared_ptr<StreamLayer>& streamLayer, layers_) {
+ boost::shared_ptr<T> layer = boost::dynamic_pointer_cast<T>(streamLayer);
+ if (layer) {
+ return layer;
+ }
+ }
+ return boost::shared_ptr<T>();
+ }
+
+ private:
+ boost::shared_ptr<XMPPLayer> xmppLayer_;
+ boost::shared_ptr<LowLayer> physicalLayer_;
+ std::vector< boost::shared_ptr<StreamLayer> > layers_;
+ boost::bsignals::connection xmppReadSlotConnection_;
+ boost::bsignals::connection xmppWriteSignalConnection_;
+ std::vector< boost::bsignals::connection > writeSignalConnections_;
+ };
+}
diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h
new file mode 100644
index 0000000..490e16c
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_TLSLayer_H
+#define SWIFTEN_TLSLayer_H
+
+#include "Swiften/StreamStack/StreamLayer.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+namespace Swift {
+ class TLSLayer : public StreamLayer {
+ public:
+ virtual void connect() = 0;
+ virtual bool setClientCertificate(const PKCS12Certificate&) = 0;
+
+ public:
+ boost::signal<void ()> onError;
+ boost::signal<void ()> onConnected;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/TLSLayerFactory.cpp b/Swiften/StreamStack/TLSLayerFactory.cpp
new file mode 100644
index 0000000..15de455
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayerFactory.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+
+namespace Swift {
+
+TLSLayerFactory::~TLSLayerFactory() {
+}
+
+}
diff --git a/Swiften/StreamStack/TLSLayerFactory.h b/Swiften/StreamStack/TLSLayerFactory.h
new file mode 100644
index 0000000..1b0bfc1
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayerFactory.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class TLSLayer;
+
+ class TLSLayerFactory {
+ public:
+ virtual ~TLSLayerFactory();
+ virtual bool canCreate() const = 0;
+
+ virtual boost::shared_ptr<TLSLayer> createTLSLayer() = 0;
+ };
+}
diff --git a/Swiften/StreamStack/UnitTest/StreamStackTest.cpp b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp
new file mode 100644
index 0000000..6b97c0d
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp
@@ -0,0 +1,165 @@
+#include <vector>
+#include <boost/bind.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/StreamStack.h"
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+using namespace Swift;
+
+class StreamStackTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StreamStackTest);
+ CPPUNIT_TEST(testWriteData_NoIntermediateStreamStack);
+ CPPUNIT_TEST(testWriteData_OneIntermediateStream);
+ CPPUNIT_TEST(testWriteData_TwoIntermediateStreamStack);
+ CPPUNIT_TEST(testReadData_NoIntermediateStreamStack);
+ CPPUNIT_TEST(testReadData_OneIntermediateStream);
+ CPPUNIT_TEST(testReadData_TwoIntermediateStreamStack);
+ CPPUNIT_TEST(testAddLayer_ExistingOnWriteDataSlot);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StreamStackTest() {}
+
+ void setUp() {
+ physicalStream_ = boost::shared_ptr<TestLowLayer>(new TestLowLayer());
+ xmppStream_ = boost::shared_ptr<XMPPLayer>(new XMPPLayer(&parserFactories_, &serializers_));
+ elementsReceived_ = 0;
+ dataWriteReceived_ = 0;
+ }
+
+ void tearDown() {
+ }
+
+ void testWriteData_NoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("foo"), physicalStream_->data_[0]);
+ }
+
+ void testWriteData_OneIntermediateStream() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ testling.addLayer(xStream);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("Xfoo"), physicalStream_->data_[0]);
+ }
+
+ void testWriteData_TwoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ boost::shared_ptr<MyStreamLayer> yStream(new MyStreamLayer("Y"));
+ testling.addLayer(xStream);
+ testling.addLayer(yStream);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("XYfoo"), physicalStream_->data_[0]);
+ }
+
+ void testReadData_NoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1));
+
+ physicalStream_->onDataRead(ByteArray("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ }
+
+ void testReadData_OneIntermediateStream() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1));
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("<"));
+ testling.addLayer(xStream);
+
+ physicalStream_->onDataRead(ByteArray("stream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ }
+
+ void testReadData_TwoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1));
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("s"));
+ boost::shared_ptr<MyStreamLayer> yStream(new MyStreamLayer("<"));
+ testling.addLayer(xStream);
+ testling.addLayer(yStream);
+
+ physicalStream_->onDataRead(ByteArray("tream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ }
+
+ void testAddLayer_ExistingOnWriteDataSlot() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onWriteData.connect(boost::bind(&StreamStackTest::handleWriteData, this, _1));
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ testling.addLayer(xStream);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(1, dataWriteReceived_);
+ }
+
+ void handleElement(boost::shared_ptr<Element>) {
+ ++elementsReceived_;
+ }
+
+ void handleWriteData(ByteArray) {
+ ++dataWriteReceived_;
+ }
+
+ private:
+ class MyStreamLayer : public StreamLayer {
+ public:
+ MyStreamLayer(const String& prepend) : prepend_(prepend) {
+ }
+
+ virtual void writeData(const ByteArray& data) {
+ onWriteData(ByteArray(prepend_) + data);
+ }
+
+ virtual void handleDataRead(const ByteArray& data) {
+ onDataRead(ByteArray(prepend_) + data);
+ }
+
+ private:
+ String prepend_;
+ };
+
+ class TestLowLayer : public LowLayer {
+ public:
+ TestLowLayer() {
+ }
+
+ virtual void writeData(const ByteArray& data) {
+ data_.push_back(data);
+ }
+
+ std::vector<ByteArray> data_;
+ };
+
+ private:
+ FullPayloadParserFactoryCollection parserFactories_;
+ FullPayloadSerializerCollection serializers_;
+ boost::shared_ptr<TestLowLayer> physicalStream_;
+ boost::shared_ptr<XMPPLayer> xmppStream_;
+ int elementsReceived_;
+ int dataWriteReceived_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StreamStackTest);
diff --git a/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp
new file mode 100644
index 0000000..e284ba9
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp
@@ -0,0 +1,119 @@
+#include <vector>
+#include <boost/bind.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+using namespace Swift;
+
+class XMPPLayerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMPPLayerTest);
+ CPPUNIT_TEST(testParseData_Error);
+ CPPUNIT_TEST(testResetParser);
+ CPPUNIT_TEST(testResetParser_FromSlot);
+ CPPUNIT_TEST(testWriteHeader);
+ CPPUNIT_TEST(testWriteElement);
+ CPPUNIT_TEST(testWriteFooter);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ XMPPLayerTest() {}
+
+ void setUp() {
+ testling_ = new XMPPLayer(&parserFactories_, &serializers_);
+ elementsReceived_ = 0;
+ dataReceived_ = "";
+ errorReceived_ = 0;
+ }
+
+ void tearDown() {
+ delete testling_;
+ }
+
+ void testParseData_Error() {
+ testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this));
+
+ testling_->parseData("<iq>");
+
+ CPPUNIT_ASSERT_EQUAL(1, errorReceived_);
+ }
+
+ void testResetParser() {
+ testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElement, this, _1));
+ testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this));
+
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >");
+ testling_->resetParser();
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >");
+ testling_->parseData("<presence/>");
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, errorReceived_);
+ }
+
+ void testResetParser_FromSlot() {
+ testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElementAndReset, this, _1));
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" ><presence/>");
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" ><presence/>");
+
+ CPPUNIT_ASSERT_EQUAL(2, elementsReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, errorReceived_);
+ }
+
+ void testWriteHeader() {
+ testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1));
+ ProtocolHeader header;
+ header.setTo("example.com");
+ testling_->writeHeader(header);
+
+ CPPUNIT_ASSERT_EQUAL(String("<?xml version=\"1.0\"?><stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" to=\"example.com\" version=\"1.0\">"), dataReceived_);
+ }
+
+ void testWriteElement() {
+ testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1));
+ testling_->writeElement(boost::shared_ptr<Presence>(new Presence()));
+
+ CPPUNIT_ASSERT_EQUAL(String("<presence/>"), dataReceived_);
+ }
+
+ void testWriteFooter() {
+ testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1));
+ testling_->writeFooter();
+
+ CPPUNIT_ASSERT_EQUAL(String("</stream:stream>"), dataReceived_);
+ }
+
+ void handleElement(boost::shared_ptr<Element>) {
+ ++elementsReceived_;
+ }
+
+ void handleElementAndReset(boost::shared_ptr<Element>) {
+ ++elementsReceived_;
+ testling_->resetParser();
+ }
+
+ void handleWriteData(ByteArray ba) {
+ dataReceived_ += std::string(ba.getData(), ba.getSize());
+ }
+
+ void handleError() {
+ ++errorReceived_;
+ }
+
+ private:
+ FullPayloadParserFactoryCollection parserFactories_;
+ FullPayloadSerializerCollection serializers_;
+ XMPPLayer* testling_;
+ int elementsReceived_;
+ String dataReceived_;
+ int errorReceived_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPLayerTest);
diff --git a/Swiften/StreamStack/WhitespacePingLayer.cpp b/Swiften/StreamStack/WhitespacePingLayer.cpp
new file mode 100644
index 0000000..5c1eb00
--- /dev/null
+++ b/Swiften/StreamStack/WhitespacePingLayer.cpp
@@ -0,0 +1,39 @@
+#include "Swiften/StreamStack/WhitespacePingLayer.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Network/TimerFactory.h"
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+
+static const int TIMEOUT_MILLISECONDS = 60000;
+
+WhitespacePingLayer::WhitespacePingLayer(TimerFactory* timerFactory) : isActive(false) {
+ timer = timerFactory->createTimer(TIMEOUT_MILLISECONDS);
+ timer->onTick.connect(boost::bind(&WhitespacePingLayer::handleTimerTick, this));
+}
+
+void WhitespacePingLayer::writeData(const ByteArray& data) {
+ onWriteData(data);
+}
+
+void WhitespacePingLayer::handleDataRead(const ByteArray& data) {
+ onDataRead(data);
+}
+
+void WhitespacePingLayer::handleTimerTick() {
+ onWriteData(" ");
+}
+
+void WhitespacePingLayer::setActive() {
+ isActive = true;
+ timer->start();
+}
+
+void WhitespacePingLayer::setInactive() {
+ timer->stop();
+ isActive = false;
+}
+
+}
diff --git a/Swiften/StreamStack/WhitespacePingLayer.h b/Swiften/StreamStack/WhitespacePingLayer.h
new file mode 100644
index 0000000..18ea39a
--- /dev/null
+++ b/Swiften/StreamStack/WhitespacePingLayer.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/StreamStack/StreamLayer.h"
+
+namespace Swift {
+ class Timer;
+ class TimerFactory;
+
+ class WhitespacePingLayer : public StreamLayer, boost::noncopyable {
+ public:
+ WhitespacePingLayer(TimerFactory* timerFactory);
+
+ void setActive();
+ void setInactive();
+ void writeData(const ByteArray& data);
+ void handleDataRead(const ByteArray& data);
+
+ bool getIsActive() const {
+ return isActive;
+ }
+
+ private:
+ void handleTimerTick();
+
+ private:
+ bool isActive;
+ boost::shared_ptr<Timer> timer;
+ };
+}
diff --git a/Swiften/StreamStack/XMPPLayer.cpp b/Swiften/StreamStack/XMPPLayer.cpp
new file mode 100644
index 0000000..b87cb4a
--- /dev/null
+++ b/Swiften/StreamStack/XMPPLayer.cpp
@@ -0,0 +1,80 @@
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Parser/XMPPParser.h"
+#include "Swiften/Serializer/XMPPSerializer.h"
+#include "Swiften/Elements/ProtocolHeader.h"
+
+namespace Swift {
+
+XMPPLayer::XMPPLayer(
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers) :
+ payloadParserFactories_(payloadParserFactories),
+ payloadSerializers_(payloadSerializers),
+ resetParserAfterParse_(false),
+ inParser_(false) {
+ xmppParser_ = new XMPPParser(this, payloadParserFactories_);
+ xmppSerializer_ = new XMPPSerializer(payloadSerializers_);
+}
+
+XMPPLayer::~XMPPLayer() {
+ delete xmppSerializer_;
+ delete xmppParser_;
+}
+
+void XMPPLayer::writeHeader(const ProtocolHeader& header) {
+ onWriteData(ByteArray(xmppSerializer_->serializeHeader(header)));
+}
+
+void XMPPLayer::writeFooter() {
+ onWriteData(ByteArray(xmppSerializer_->serializeFooter()));
+}
+
+void XMPPLayer::writeElement(boost::shared_ptr<Element> element) {
+ onWriteData(ByteArray(xmppSerializer_->serializeElement(element)));
+}
+
+void XMPPLayer::writeData(const String& data) {
+ onWriteData(ByteArray(data));
+}
+
+void XMPPLayer::parseData(ByteArray data) {
+ onDataRead(data);
+ inParser_ = true;
+ if (!xmppParser_->parse(String(data.getData(), data.getSize()))) {
+ inParser_ = false;
+ onError();
+ return;
+ }
+ inParser_ = false;
+ if (resetParserAfterParse_) {
+ doResetParser();
+ }
+}
+
+void XMPPLayer::doResetParser() {
+ delete xmppParser_;
+ xmppParser_ = new XMPPParser(this, payloadParserFactories_);
+ resetParserAfterParse_ = false;
+}
+
+void XMPPLayer::handleStreamStart(const ProtocolHeader& header) {
+ onStreamStart(header);
+}
+
+void XMPPLayer::handleElement(boost::shared_ptr<Element> stanza) {
+ onElement(stanza);
+}
+
+void XMPPLayer::handleStreamEnd() {
+}
+
+void XMPPLayer::resetParser() {
+ if (inParser_) {
+ resetParserAfterParse_ = true;
+ }
+ else {
+ doResetParser();
+ }
+}
+
+}
diff --git a/Swiften/StreamStack/XMPPLayer.h b/Swiften/StreamStack/XMPPLayer.h
new file mode 100644
index 0000000..7974811
--- /dev/null
+++ b/Swiften/StreamStack/XMPPLayer.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Parser/XMPPParserClient.h"
+
+namespace Swift {
+ class ProtocolHeader;
+ class XMPPParser;
+ class PayloadParserFactoryCollection;
+ class XMPPSerializer;
+ class PayloadSerializerCollection;
+
+ class XMPPLayer : public XMPPParserClient, boost::noncopyable {
+ public:
+ XMPPLayer(
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers);
+ ~XMPPLayer();
+
+ void writeHeader(const ProtocolHeader& header);
+ void writeFooter();
+ void writeElement(boost::shared_ptr<Element>);
+ void writeData(const String& data);
+
+ void parseData(ByteArray data);
+ void resetParser();
+
+ public:
+ boost::signal<void (const ProtocolHeader&)> onStreamStart;
+ boost::signal<void (boost::shared_ptr<Element>)> onElement;
+ boost::signal<void (const ByteArray&)> onWriteData;
+ boost::signal<void (const ByteArray&)> onDataRead;
+ boost::signal<void ()> onError;
+
+ private:
+ void handleStreamStart(const ProtocolHeader&);
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamEnd();
+
+ void doResetParser();
+
+ private:
+ PayloadParserFactoryCollection* payloadParserFactories_;
+ XMPPParser* xmppParser_;
+ PayloadSerializerCollection* payloadSerializers_;
+ XMPPSerializer* xmppSerializer_;
+ bool resetParserAfterParse_;
+ bool inParser_;
+ };
+}
diff --git a/Swiften/StringCodecs/Base64.cpp b/Swiften/StringCodecs/Base64.cpp
new file mode 100644
index 0000000..03b0896
--- /dev/null
+++ b/Swiften/StringCodecs/Base64.cpp
@@ -0,0 +1,109 @@
+#include <boost/numeric/conversion/cast.hpp>
+
+#include "Swiften/StringCodecs/Base64.h"
+
+namespace Swift {
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+String Base64::encode(const ByteArray &s) {
+ int i;
+ int len = s.getSize();
+ char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ int a, b, c;
+
+ std::string p;
+ p.resize((len+2)/3*4);
+ int at = 0;
+ for( i = 0; i < len; i += 3 ) {
+ a = ((unsigned char) (s[i]) & 3) << 4;
+ if(i + 1 < len) {
+ a += (unsigned char) (s[i + 1]) >> 4;
+ b = ((unsigned char) (s[i + 1]) & 0xF) << 2;
+ if(i + 2 < len) {
+ b += (unsigned char) (s[i + 2]) >> 6;
+ c = (unsigned char) (s[i + 2]) & 0x3F;
+ }
+ else
+ c = 64;
+ }
+ else {
+ b = c = 64;
+ }
+
+ p[at++] = tbl[(unsigned char) (s[i]) >> 2];
+ p[at++] = tbl[a];
+ p[at++] = tbl[b];
+ p[at++] = tbl[c];
+ }
+ return p;
+}
+
+ByteArray Base64::decode(const String& input) {
+ String inputWithoutNewlines(input);
+ inputWithoutNewlines.removeAll('\n');
+
+ const std::string& s = inputWithoutNewlines.getUTF8String();
+ ByteArray p;
+
+ // -1 specifies invalid
+ // 64 specifies eof
+ // everything else specifies data
+
+ char tbl[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ };
+
+ // this should be a multiple of 4
+ int len = s.size();
+
+ if(len % 4) {
+ return p;
+ }
+
+ p.resize(len / 4 * 3);
+
+ int i;
+ int at = 0;
+
+ int a, b, c, d;
+ c = d = 0;
+
+ for( i = 0; i < len; i += 4 ) {
+ a = tbl[boost::numeric_cast<int>(s[i])];
+ b = tbl[boost::numeric_cast<int>(s[i + 1])];
+ c = tbl[boost::numeric_cast<int>(s[i + 2])];
+ d = tbl[boost::numeric_cast<int>(s[i + 3])];
+ if((a == 64 || b == 64) || (a < 0 || b < 0 || c < 0 || d < 0)) {
+ p.resize(0);
+ return p;
+ }
+ p[at++] = ((a & 0x3F) << 2) | ((b >> 4) & 0x03);
+ p[at++] = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F);
+ p[at++] = ((c & 0x03) << 6) | ((d >> 0) & 0x3F);
+ }
+
+ if(c & 64)
+ p.resize(at - 2);
+ else if(d & 64)
+ p.resize(at - 1);
+
+ return p;
+}
+
+}
diff --git a/Swiften/StringCodecs/Base64.h b/Swiften/StringCodecs/Base64.h
new file mode 100644
index 0000000..1ea378c
--- /dev/null
+++ b/Swiften/StringCodecs/Base64.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <vector>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class Base64 {
+ public:
+ static String encode(const ByteArray& s);
+ static ByteArray decode(const String &s);
+ };
+}
diff --git a/Swiften/StringCodecs/HMACSHA1.cpp b/Swiften/StringCodecs/HMACSHA1.cpp
new file mode 100644
index 0000000..f4066cb
--- /dev/null
+++ b/Swiften/StringCodecs/HMACSHA1.cpp
@@ -0,0 +1,39 @@
+#include "Swiften/StringCodecs/HMACSHA1.h"
+
+#include <cassert>
+
+#include "Swiften/StringCodecs/SHA1.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+
+static const unsigned int B = 64;
+
+ByteArray HMACSHA1::getResult(const ByteArray& key, const ByteArray& data) {
+ assert(key.getSize() <= B);
+
+ // Create the padded key
+ ByteArray paddedKey(key);
+ paddedKey.resize(B);
+ for (unsigned int i = key.getSize(); i < paddedKey.getSize(); ++i) {
+ paddedKey[i] = 0x0;
+ }
+
+ // Create the first value
+ ByteArray x(paddedKey);
+ for (unsigned int i = 0; i < x.getSize(); ++i) {
+ x[i] ^= 0x36;
+ }
+ x += data;
+
+ // Create the second value
+ ByteArray y(paddedKey);
+ for (unsigned int i = 0; i < y.getSize(); ++i) {
+ y[i] ^= 0x5c;
+ }
+ y += SHA1::getHash(x);
+
+ return SHA1::getHash(y);
+}
+
+}
diff --git a/Swiften/StringCodecs/HMACSHA1.h b/Swiften/StringCodecs/HMACSHA1.h
new file mode 100644
index 0000000..698e4bc
--- /dev/null
+++ b/Swiften/StringCodecs/HMACSHA1.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace Swift {
+ class ByteArray;
+
+ class HMACSHA1 {
+ public:
+ static ByteArray getResult(const ByteArray& key, const ByteArray& data);
+ };
+}
diff --git a/Swiften/StringCodecs/Hexify.cpp b/Swiften/StringCodecs/Hexify.cpp
new file mode 100644
index 0000000..504e314
--- /dev/null
+++ b/Swiften/StringCodecs/Hexify.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/StringCodecs/Hexify.h"
+
+#include <sstream>
+#include <iomanip>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+
+String Hexify::hexify(const ByteArray& data) {
+ std::ostringstream result;
+ result << std::hex;
+
+ for (unsigned int i = 0; i < data.getSize(); ++i) {
+ result << std::setw(2) << std::setfill('0') << boost::numeric_cast<unsigned int>(static_cast<unsigned char>(data[i]));
+ }
+ return String(result.str());
+}
+
+}
diff --git a/Swiften/StringCodecs/Hexify.h b/Swiften/StringCodecs/Hexify.h
new file mode 100644
index 0000000..7bd5bee
--- /dev/null
+++ b/Swiften/StringCodecs/Hexify.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace Swift {
+ class String;
+ class ByteArray;
+
+ class Hexify {
+ public:
+ static String hexify(const ByteArray& data);
+ };
+}
diff --git a/Swiften/StringCodecs/MD5.cpp b/Swiften/StringCodecs/MD5.cpp
new file mode 100644
index 0000000..937d128
--- /dev/null
+++ b/Swiften/StringCodecs/MD5.cpp
@@ -0,0 +1,359 @@
+/*
+ * This implementation is shamelessly copied from L. Peter Deutsch's
+ * implementation, and altered to use our own defines and datastructures.
+ * Original license below.
+ */
+
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+ */
+
+#include "Swiften/StringCodecs/MD5.h"
+
+#include <cassert>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Base/Platform.h"
+
+namespace Swift {
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) {
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#ifdef SWIFTEN_BIG_ENDIAN
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#ifdef SWIFTEN_LITTLE_ENDIAN
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#else
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+ByteArray MD5::getHash(const ByteArray& data) {
+ ByteArray digest;
+ digest.resize(16);
+
+ md5_state_t state;
+ md5_init(&state);
+ md5_append(&state, reinterpret_cast<const md5_byte_t*>(data.getData()), data.getSize());
+ md5_finish(&state, reinterpret_cast<md5_byte_t*>(digest.getData()));
+
+ return digest;
+}
+
+}
diff --git a/Swiften/StringCodecs/MD5.h b/Swiften/StringCodecs/MD5.h
new file mode 100644
index 0000000..8773409
--- /dev/null
+++ b/Swiften/StringCodecs/MD5.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace Swift {
+ class ByteArray;
+
+ class MD5 {
+ public:
+ static ByteArray getHash(const ByteArray& data);
+ };
+}
diff --git a/Swiften/StringCodecs/PBKDF2.cpp b/Swiften/StringCodecs/PBKDF2.cpp
new file mode 100644
index 0000000..c09c10e
--- /dev/null
+++ b/Swiften/StringCodecs/PBKDF2.cpp
@@ -0,0 +1,20 @@
+#include "Swiften/StringCodecs/PBKDF2.h"
+#include "Swiften/StringCodecs/HMACSHA1.h"
+
+namespace Swift {
+
+ByteArray PBKDF2::encode(const ByteArray& password, const ByteArray& salt, int iterations) {
+ ByteArray u = HMACSHA1::getResult(password, salt + ByteArray("\0\0\0\1", 4));
+ ByteArray result = u;
+ int i = 1;
+ while (i < iterations) {
+ u = HMACSHA1::getResult(password, u);
+ for (unsigned int j = 0; j < u.getSize(); ++j) {
+ result[j] ^= u[j];
+ }
+ ++i;
+ }
+ return result;
+}
+
+}
diff --git a/Swiften/StringCodecs/PBKDF2.h b/Swiften/StringCodecs/PBKDF2.h
new file mode 100644
index 0000000..e87380d
--- /dev/null
+++ b/Swiften/StringCodecs/PBKDF2.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class PBKDF2 {
+ public:
+ static ByteArray encode(const ByteArray& password, const ByteArray& salt, int iterations);
+ };
+}
diff --git a/Swiften/StringCodecs/SHA1.cpp b/Swiften/StringCodecs/SHA1.cpp
new file mode 100644
index 0000000..ef99d9a
--- /dev/null
+++ b/Swiften/StringCodecs/SHA1.cpp
@@ -0,0 +1,197 @@
+#include "Swiften/Base/Platform.h"
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <boost/cstdint.hpp>
+#include <stdio.h>
+#include <string.h>
+
+typedef struct {
+ boost::uint32_t state[5];
+ boost::uint32_t count[2];
+ boost::uint8_t buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(boost::uint32_t state[5], boost::uint8_t buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, boost::uint8_t* data, unsigned int len);
+void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifdef SWIFTEN_LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(boost::uint32_t state[5], boost::uint8_t buffer[64])
+{
+boost::uint32_t a, b, c, d, e;
+typedef union {
+ boost::uint8_t c[64];
+ boost::uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+static boost::uint8_t workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+#else
+ block = reinterpret_cast<CHAR64LONG16*>(buffer);
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, boost::uint8_t* data, unsigned int len)
+{
+unsigned int i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context)
+{
+boost::uint32_t i, j;
+boost::uint8_t finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (boost::uint8_t) ((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (boost::uint8_t *)("\200"), 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (boost::uint8_t *)("\0"), 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (boost::uint8_t)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */
+ SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+// -----------------------------------------------------------------------------
+
+#include "Swiften/StringCodecs/SHA1.h"
+
+namespace Swift {
+
+ByteArray SHA1::getHash(const ByteArray& input) {
+ ByteArray inputCopy(input);
+ ByteArray digest;
+ digest.resize(20);
+ SHA1_CTX context;
+ SHA1Init(&context);
+ SHA1Update(&context, (boost::uint8_t*) inputCopy.getData(), inputCopy.getSize());
+ SHA1Final((boost::uint8_t*) digest.getData(), &context);
+ return digest;
+}
+
+}
diff --git a/Swiften/StringCodecs/SHA1.h b/Swiften/StringCodecs/SHA1.h
new file mode 100644
index 0000000..9f54e8e
--- /dev/null
+++ b/Swiften/StringCodecs/SHA1.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class SHA1 {
+ public:
+ static ByteArray getHash(const ByteArray& data);
+ };
+}
diff --git a/Swiften/StringCodecs/UnitTest/Base64Test.cpp b/Swiften/StringCodecs/UnitTest/Base64Test.cpp
new file mode 100644
index 0000000..a28a9ab
--- /dev/null
+++ b/Swiften/StringCodecs/UnitTest/Base64Test.cpp
@@ -0,0 +1,44 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/StringCodecs/Base64.h"
+
+using namespace Swift;
+
+class Base64Test : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(Base64Test);
+ CPPUNIT_TEST(testEncode);
+ CPPUNIT_TEST(testEncode_NonAscii);
+ CPPUNIT_TEST(testEncode_NoData);
+ CPPUNIT_TEST(testDecode);
+ CPPUNIT_TEST(testDecode_NoData);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testEncode() {
+ String result(Base64::encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"));
+ CPPUNIT_ASSERT_EQUAL(String("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejEyMzQ1Njc4OTA="), result);
+ }
+
+ void testEncode_NonAscii() {
+ String result(Base64::encode(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed")));
+ CPPUNIT_ASSERT_EQUAL(String("QgayPKawpkPSDYmwT/WM94uAlu0="), result);
+ }
+
+ void testEncode_NoData() {
+ String result(Base64::encode(ByteArray()));
+ CPPUNIT_ASSERT_EQUAL(String(""), result);
+ }
+
+ void testDecode() {
+ ByteArray result(Base64::decode("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejEyMzQ1Njc4OTA="));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"), result);
+ }
+
+ void testDecode_NoData() {
+ ByteArray result(Base64::decode(""));
+ CPPUNIT_ASSERT_EQUAL(ByteArray(), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Base64Test);
diff --git a/Swiften/StringCodecs/UnitTest/HMACSHA1Test.cpp b/Swiften/StringCodecs/UnitTest/HMACSHA1Test.cpp
new file mode 100644
index 0000000..edfae10
--- /dev/null
+++ b/Swiften/StringCodecs/UnitTest/HMACSHA1Test.cpp
@@ -0,0 +1,21 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StringCodecs/HMACSHA1.h"
+
+using namespace Swift;
+
+class HMACSHA1Test : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(HMACSHA1Test);
+ CPPUNIT_TEST(testGetResult);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetResult() {
+ ByteArray result(HMACSHA1::getResult("foo", "foobar"));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\xa4\xee\xba\x8e\x63\x3d\x77\x88\x69\xf5\x68\xd0\x5a\x1b\x3d\xc7\x2b\xfd\x4\xdd"), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(HMACSHA1Test);
diff --git a/Swiften/StringCodecs/UnitTest/HexifyTest.cpp b/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
new file mode 100644
index 0000000..bf032a3
--- /dev/null
+++ b/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
@@ -0,0 +1,21 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/StringCodecs/Hexify.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+
+using namespace Swift;
+
+class HexifyTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(HexifyTest);
+ CPPUNIT_TEST(testHexify);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testHexify() {
+ CPPUNIT_ASSERT_EQUAL(String("4206b23ca6b0a643d20d89b04ff58cf78b8096ed"), Hexify::hexify(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed")));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(HexifyTest);
diff --git a/Swiften/StringCodecs/UnitTest/MD5Test.cpp b/Swiften/StringCodecs/UnitTest/MD5Test.cpp
new file mode 100644
index 0000000..cad8754
--- /dev/null
+++ b/Swiften/StringCodecs/UnitTest/MD5Test.cpp
@@ -0,0 +1,29 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/StringCodecs/MD5.h"
+#include "Swiften/Base/ByteArray.h"
+
+using namespace Swift;
+
+class MD5Test : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(MD5Test);
+ CPPUNIT_TEST(testGetHash_Empty);
+ CPPUNIT_TEST(testGetHash_Alphabet);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetHash_Empty() {
+ ByteArray result(MD5::getHash(""));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e", 16), result);
+ }
+
+ void testGetHash_Alphabet() {
+ ByteArray result(MD5::getHash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MD5Test);
diff --git a/Swiften/StringCodecs/UnitTest/PBKDF2Test.cpp b/Swiften/StringCodecs/UnitTest/PBKDF2Test.cpp
new file mode 100644
index 0000000..92f4fe9
--- /dev/null
+++ b/Swiften/StringCodecs/UnitTest/PBKDF2Test.cpp
@@ -0,0 +1,36 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StringCodecs/PBKDF2.h"
+
+using namespace Swift;
+
+class PBKDF2Test : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PBKDF2Test);
+ CPPUNIT_TEST(testGetResult_I1);
+ CPPUNIT_TEST(testGetResult_I2);
+ CPPUNIT_TEST(testGetResult_I4096);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetResult_I1() {
+ ByteArray result(PBKDF2::encode("password", "salt", 1));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6"), result);
+ }
+
+ void testGetResult_I2() {
+ ByteArray result(PBKDF2::encode("password", "salt", 2));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\xea\x6c\x1\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57"), result);
+ }
+
+ void testGetResult_I4096() {
+ ByteArray result(PBKDF2::encode("password", "salt", 4096));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x4b\x00\x79\x1\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1", 20), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PBKDF2Test);
diff --git a/Swiften/StringCodecs/UnitTest/SHA1Test.cpp b/Swiften/StringCodecs/UnitTest/SHA1Test.cpp
new file mode 100644
index 0000000..3dbf341
--- /dev/null
+++ b/Swiften/StringCodecs/UnitTest/SHA1Test.cpp
@@ -0,0 +1,37 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/StringCodecs/SHA1.h"
+
+using namespace Swift;
+
+class SHA1Test : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SHA1Test);
+ CPPUNIT_TEST(testGetHash);
+ CPPUNIT_TEST(testGetHash_Twice);
+ CPPUNIT_TEST(testGetHash_NoData);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetHash() {
+ ByteArray result(SHA1::getHash("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);
+ }
+
+
+ void testGetHash_Twice() {
+ ByteArray input("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<");
+ SHA1::getHash(input);
+ ByteArray result(SHA1::getHash(input));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);
+ }
+
+ void testGetHash_NoData() {
+ ByteArray result(SHA1::getHash(ByteArray()));
+
+ CPPUNIT_ASSERT_EQUAL(ByteArray("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), result);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SHA1Test);
diff --git a/Swiften/StringPrep/SConscript b/Swiften/StringPrep/SConscript
new file mode 100644
index 0000000..480d81a
--- /dev/null
+++ b/Swiften/StringPrep/SConscript
@@ -0,0 +1,9 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"])
+
+objects = myenv.StaticObject([
+ "StringPrep.cpp"
+ ])
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/StringPrep/StringPrep.cpp b/Swiften/StringPrep/StringPrep.cpp
new file mode 100644
index 0000000..a4af208
--- /dev/null
+++ b/Swiften/StringPrep/StringPrep.cpp
@@ -0,0 +1,33 @@
+#include "Swiften/StringPrep/StringPrep.h"
+
+#include <stringprep.h>
+#include <vector>
+
+namespace Swift {
+
+static const int MAX_STRINGPREP_SIZE = 1024;
+
+const Stringprep_profile* getLibIDNProfile(StringPrep::Profile profile) {
+ switch(profile) {
+ case StringPrep::NamePrep: return stringprep_nameprep; break;
+ case StringPrep::XMPPNodePrep: return stringprep_xmpp_nodeprep; break;
+ case StringPrep::XMPPResourcePrep: return stringprep_xmpp_resourceprep; break;
+ case StringPrep::SASLPrep: return stringprep_saslprep; break;
+ }
+ assert(false);
+ return 0;
+}
+
+String StringPrep::getPrepared(const String& s, 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), getLibIDNProfile(profile)) == 0) {
+ return String(&input[0]);
+ }
+ else {
+ return "";
+ }
+}
+
+}
diff --git a/Swiften/StringPrep/StringPrep.h b/Swiften/StringPrep/StringPrep.h
new file mode 100644
index 0000000..7dbb03a
--- /dev/null
+++ b/Swiften/StringPrep/StringPrep.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+ class StringPrep {
+ public:
+ enum Profile {
+ NamePrep,
+ XMPPNodePrep,
+ XMPPResourcePrep,
+ SASLPrep,
+ };
+
+ static String getPrepared(const String& s, Profile profile);
+ };
+}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
new file mode 100644
index 0000000..3ce8d54
--- /dev/null
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -0,0 +1,159 @@
+#include <vector>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+
+#include "Swiften/TLS/OpenSSL/OpenSSLContext.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+namespace Swift {
+
+static const int SSL_READ_BUFFERSIZE = 8192;
+
+void freeX509Stack(STACK_OF(X509)* stack) {
+ sk_X509_free(stack);
+}
+
+OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) {
+ ensureLibraryInitialized();
+ context_ = SSL_CTX_new(TLSv1_client_method());
+}
+
+OpenSSLContext::~OpenSSLContext() {
+ SSL_free(handle_);
+ SSL_CTX_free(context_);
+}
+
+void OpenSSLContext::ensureLibraryInitialized() {
+ static bool isLibraryInitialized = false;
+ if (!isLibraryInitialized) {
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ isLibraryInitialized = true;
+ }
+}
+
+void OpenSSLContext::connect() {
+ handle_ = SSL_new(context_);
+ // Ownership of BIOs is ransferred
+ readBIO_ = BIO_new(BIO_s_mem());
+ writeBIO_ = BIO_new(BIO_s_mem());
+ SSL_set_bio(handle_, readBIO_, writeBIO_);
+
+ state_ = Connecting;
+ doConnect();
+}
+
+void OpenSSLContext::doConnect() {
+ int connectResult = SSL_connect(handle_);
+ int error = SSL_get_error(handle_, connectResult);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ state_ = Connected;
+ onConnected();
+ //X509* x = SSL_get_peer_certificate(handle_);
+ //std::cout << x->name << std::endl;
+ //const char* comp = SSL_get_current_compression(handle_);
+ //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
+ break;
+ case SSL_ERROR_WANT_READ:
+ sendPendingDataToNetwork();
+ break;
+ default:
+ state_ = Error;
+ onError();
+ }
+}
+
+void OpenSSLContext::sendPendingDataToNetwork() {
+ int size = BIO_pending(writeBIO_);
+ if (size > 0) {
+ ByteArray data;
+ data.resize(size);
+ BIO_read(writeBIO_, data.getData(), size);
+ onDataForNetwork(data);
+ }
+}
+
+void OpenSSLContext::handleDataFromNetwork(const ByteArray& data) {
+ BIO_write(readBIO_, data.getData(), data.getSize());
+ switch (state_) {
+ case Connecting:
+ doConnect();
+ break;
+ case Connected:
+ sendPendingDataToApplication();
+ break;
+ case Start: assert(false); break;
+ case Error: assert(false); break;
+ }
+}
+
+void OpenSSLContext::handleDataFromApplication(const ByteArray& data) {
+ if (SSL_write(handle_, data.getData(), data.getSize()) >= 0) {
+ sendPendingDataToNetwork();
+ }
+ else {
+ state_ = Error;
+ onError();
+ }
+}
+
+void OpenSSLContext::sendPendingDataToApplication() {
+ ByteArray data;
+ data.resize(SSL_READ_BUFFERSIZE);
+ int ret = SSL_read(handle_, data.getData(), data.getSize());
+ while (ret > 0) {
+ data.resize(ret);
+ onDataForApplication(data);
+ data.resize(SSL_READ_BUFFERSIZE);
+ ret = SSL_read(handle_, data.getData(), data.getSize());
+ }
+ if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) {
+ state_ = Error;
+ onError();
+ }
+}
+
+bool OpenSSLContext::setClientCertificate(const PKCS12Certificate& certificate) {
+ if (certificate.isNull()) {
+ return false;
+ }
+
+ // Create a PKCS12 structure
+ BIO* bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, certificate.getData().getData(), certificate.getData().getSize());
+ boost::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free);
+ BIO_free(bio);
+ if (!pkcs12) {
+ return false;
+ }
+
+ // Parse PKCS12
+ X509 *certPtr = 0;
+ EVP_PKEY* privateKeyPtr = 0;
+ STACK_OF(X509)* caCertsPtr = 0;
+ int result = PKCS12_parse(pkcs12.get(), certificate.getPassword().getUTF8Data(), &privateKeyPtr, &certPtr, &caCertsPtr);
+ if (result != 1) {
+ return false;
+ }
+ boost::shared_ptr<X509> cert(certPtr, X509_free);
+ boost::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free);
+ boost::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);
+
+ // Use the key & certificates
+ if (SSL_CTX_use_certificate(context_, cert.get()) != 1) {
+ return false;
+ }
+ if (SSL_CTX_use_PrivateKey(context_, privateKey.get()) != 1) {
+ return false;
+ }
+ for (int i = 0; i < sk_X509_num(caCerts.get()); ++i) {
+ SSL_CTX_add_extra_chain_cert(context_, sk_X509_value(caCerts.get(), i));
+ }
+ return true;
+}
+
+}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
new file mode 100644
index 0000000..3e735df
--- /dev/null
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -0,0 +1,43 @@
+#include <openssl/ssl.h>
+#include <boost/signal.hpp>
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class PKCS12Certificate;
+
+ class OpenSSLContext : boost::noncopyable {
+ public:
+ OpenSSLContext();
+ ~OpenSSLContext();
+
+ void connect();
+ bool setClientCertificate(const PKCS12Certificate& cert);
+
+ void handleDataFromNetwork(const ByteArray&);
+ void handleDataFromApplication(const ByteArray&);
+
+ public:
+ boost::signal<void (const ByteArray&)> onDataForNetwork;
+ boost::signal<void (const ByteArray&)> onDataForApplication;
+ boost::signal<void ()> onError;
+ boost::signal<void ()> onConnected;
+
+ private:
+ static void ensureLibraryInitialized();
+
+ void doConnect();
+ void sendPendingDataToNetwork();
+ void sendPendingDataToApplication();
+
+ private:
+ enum State { Start, Connecting, Connected, Error };
+
+ State state_;
+ SSL_CTX* context_;
+ SSL* handle_;
+ BIO* readBIO_;
+ BIO* writeBIO_;
+ };
+}
diff --git a/Swiften/TLS/PKCS12Certificate.h b/Swiften/TLS/PKCS12Certificate.h
new file mode 100644
index 0000000..4b0e708
--- /dev/null
+++ b/Swiften/TLS/PKCS12Certificate.h
@@ -0,0 +1,37 @@
+#ifndef SWIFTEN_PKCS12Certificate_H
+#define SWIFTEN_PKCS12Certificate_H
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class PKCS12Certificate {
+ public:
+ PKCS12Certificate() {}
+
+ PKCS12Certificate(const String& filename, const String& password) : password_(password) {
+ data_.readFromFile(filename);
+ }
+
+ bool isNull() const {
+ return data_.isEmpty();
+ }
+
+ const ByteArray& getData() const {
+ return data_;
+ }
+
+ void setData(const ByteArray& data) {
+ data_ = data;
+ }
+
+ const String& getPassword() const {
+ return password_;
+ }
+
+ private:
+ ByteArray data_;
+ String password_;
+ };
+}
+
+#endif