summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
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"