summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2011-05-26 18:46:49 (GMT)
committerRemko Tronçon <git@el-tramo.be>2011-09-25 17:42:32 (GMT)
commit4f62e5ec4b42929fe3c1a68667e63cb1b7a35509 (patch)
tree0d19fac3f578dec00ccf3e58930312951e38de89
parentde660b763459cdd707876ec244b6866abca07fa2 (diff)
downloadswift-4f62e5ec4b42929fe3c1a68667e63cb1b7a35509.zip
swift-4f62e5ec4b42929fe3c1a68667e63cb1b7a35509.tar.bz2
Google Summer of Code 2011 Project: Adding support for Jingle File Transfers (XEP-0234), Jingle SOCKS5 Bytestreams Transport Method (XEP-0260), Jingle In-Band Bytestreams Transport Method (XEP-0261) and SOCKS5 Bytestreams (XEP-0065).
License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php
-rw-r--r--.gitignore3
-rw-r--r--3rdParty/LibMiniUPnPc/SConscript66
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE27
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h531
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h24
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c241
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h17
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h15
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c125
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h48
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c121
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h15
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c138
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h15
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c906
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h121
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h19
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c522
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h30
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c216
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h37
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c156
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c157
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h71
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c81
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h17
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c683
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c1097
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h271
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c103
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h26
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c152
-rw-r--r--3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h64
-rw-r--r--3rdParty/LibNATPMP/SConscript50
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/LICENSE26
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/Makefile97
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/declspec.h15
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/getgateway.c554
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/getgateway.h36
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/natpmp.c350
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/natpmp.h203
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c229
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c50
-rw-r--r--3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h27
-rw-r--r--BuildTools/SCons/SConscript.boot4
-rw-r--r--BuildTools/SCons/SConstruct21
-rw-r--r--QA/Checker/IO.cpp8
-rw-r--r--QA/Checker/IO.h1
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp50
-rw-r--r--Swift/Controllers/Chat/ChatController.h13
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp15
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h8
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp17
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferController.cpp147
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferController.h72
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferOverview.cpp41
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferOverview.h38
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp31
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferProgressInfo.h29
-rw-r--r--Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp53
-rw-r--r--Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h38
-rw-r--r--Swift/Controllers/FileTransferListController.cpp45
-rw-r--r--Swift/Controllers/FileTransferListController.h35
-rw-r--r--Swift/Controllers/MainController.cpp32
-rw-r--r--Swift/Controllers/MainController.h7
-rw-r--r--Swift/Controllers/Roster/ContactRosterItem.cpp8
-rw-r--r--Swift/Controllers/Roster/ContactRosterItem.h10
-rw-r--r--Swift/Controllers/Roster/Roster.cpp10
-rw-r--r--Swift/Controllers/Roster/Roster.h3
-rw-r--r--Swift/Controllers/Roster/RosterController.cpp32
-rw-r--r--Swift/Controllers/Roster/RosterController.h12
-rw-r--r--Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp4
-rw-r--r--Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp26
-rw-r--r--Swift/Controllers/SConscript5
-rw-r--r--Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h16
-rw-r--r--Swift/Controllers/UIEvents/SendFileUIEvent.h34
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h13
-rw-r--r--Swift/Controllers/UIInterfaces/FileTransferListWidget.h23
-rw-r--r--Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h20
-rw-r--r--Swift/Controllers/UIInterfaces/UIFactory.h6
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h7
-rw-r--r--Swift/QtUI/QtChatView.cpp73
-rw-r--r--Swift/QtUI/QtChatView.h5
-rw-r--r--Swift/QtUI/QtChatWindow.cpp133
-rw-r--r--Swift/QtUI/QtChatWindow.h22
-rw-r--r--Swift/QtUI/QtFileTransferJSBridge.cpp19
-rw-r--r--Swift/QtUI/QtFileTransferJSBridge.h30
-rw-r--r--Swift/QtUI/QtFileTransferListItemModel.cpp110
-rw-r--r--Swift/QtUI/QtFileTransferListItemModel.h54
-rw-r--r--Swift/QtUI/QtFileTransferListWidget.cpp77
-rw-r--r--Swift/QtUI/QtFileTransferListWidget.h45
-rw-r--r--Swift/QtUI/QtLoginWindow.cpp10
-rw-r--r--Swift/QtUI/QtLoginWindow.h2
-rw-r--r--Swift/QtUI/QtUIFactory.cpp10
-rw-r--r--Swift/QtUI/QtUIFactory.h1
-rw-r--r--Swift/QtUI/QtWebView.cpp4
-rw-r--r--Swift/QtUI/QtXMLConsoleWidget.cpp11
-rw-r--r--Swift/QtUI/QtXMLConsoleWidget.h4
-rw-r--r--Swift/QtUI/Roster/QtRosterWidget.cpp12
-rw-r--r--Swift/QtUI/Roster/QtTreeWidget.cpp39
-rw-r--r--Swift/QtUI/Roster/QtTreeWidget.h7
-rw-r--r--Swift/QtUI/SConscript3
-rw-r--r--Swiften/Client/Client.cpp17
-rw-r--r--Swiften/Client/Client.h13
-rw-r--r--Swiften/Client/ClientXMLTracer.cpp11
-rw-r--r--Swiften/Client/ClientXMLTracer.h8
-rw-r--r--Swiften/Client/CoreClient.cpp1
-rw-r--r--Swiften/Client/CoreClient.h9
-rw-r--r--Swiften/Client/XMLBeautifier.cpp126
-rw-r--r--Swiften/Client/XMLBeautifier.h53
-rw-r--r--Swiften/Elements/DiscoInfo.cpp6
-rw-r--r--Swiften/Elements/DiscoInfo.h5
-rw-r--r--Swiften/Elements/JingleContentPayload.h1
-rw-r--r--Swiften/Elements/JingleFileTransferDescription.h22
-rw-r--r--Swiften/Elements/JingleFileTransferHash.h35
-rw-r--r--Swiften/Elements/JingleFileTransferReceived.h33
-rw-r--r--Swiften/Elements/JingleIBBTransportPayload.h9
-rw-r--r--Swiften/Elements/JinglePayload.h57
-rw-r--r--Swiften/Elements/JingleS5BTransportPayload.h96
-rw-r--r--Swiften/Elements/JingleTransportPayload.h12
-rw-r--r--Swiften/Elements/Payload.h4
-rw-r--r--Swiften/Elements/S5BProxyRequest.h61
-rw-r--r--Swiften/Elements/StreamInitiationFileInfo.h95
-rw-r--r--Swiften/Examples/SendFile/ReceiveFile.cpp37
-rw-r--r--Swiften/Examples/SendFile/SendFile.cpp78
-rw-r--r--Swiften/FileTransfer/ByteArrayReadBytestream.h2
-rw-r--r--Swiften/FileTransfer/ByteArrayWriteBytestream.h1
-rw-r--r--Swiften/FileTransfer/ConnectivityManager.cpp101
-rw-r--r--Swiften/FileTransfer/ConnectivityManager.h43
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp97
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h39
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp25
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h33
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp121
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h58
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp25
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h28
-rw-r--r--Swiften/FileTransfer/FileReadBytestream.cpp1
-rw-r--r--Swiften/FileTransfer/FileTransfer.h58
-rw-r--r--Swiften/FileTransfer/FileTransferManager.cpp14
-rw-r--r--Swiften/FileTransfer/FileTransferManager.h34
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.cpp123
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.h80
-rw-r--r--Swiften/FileTransfer/FileWriteBytestream.cpp8
-rw-r--r--Swiften/FileTransfer/FileWriteBytestream.h1
-rw-r--r--Swiften/FileTransfer/IBBReceiveSession.cpp9
-rw-r--r--Swiften/FileTransfer/IBBSendSession.cpp3
-rw-r--r--Swiften/FileTransfer/IBBSendSession.h2
-rw-r--r--Swiften/FileTransfer/IncomingFileTransfer.h5
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.cpp20
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.h11
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.cpp408
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.h60
-rw-r--r--Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp61
-rw-r--r--Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h33
-rw-r--r--Swiften/FileTransfer/JingleIncomingIBBTransport.cpp3
-rw-r--r--Swiften/FileTransfer/JingleTransport.h2
-rw-r--r--Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h6
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransfer.h8
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransferManager.cpp46
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransferManager.h47
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp401
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.h115
-rw-r--r--Swiften/FileTransfer/OutgoingSIFileTransfer.cpp4
-rw-r--r--Swiften/FileTransfer/ReadBytestream.h5
-rw-r--r--Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h4
-rw-r--r--Swiften/FileTransfer/SConscript16
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp235
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamClientSession.h94
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp72
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamProxy.h46
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp54
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.h49
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.cpp14
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.h8
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp86
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.h20
-rw-r--r--Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h36
-rw-r--r--Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp282
-rw-r--r--Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp283
-rw-r--r--Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp306
-rw-r--r--Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp16
-rw-r--r--Swiften/FileTransfer/WriteBytestream.h4
-rw-r--r--Swiften/Jingle/FakeJingleSession.cpp52
-rw-r--r--Swiften/Jingle/FakeJingleSession.h93
-rw-r--r--Swiften/Jingle/JingleContentID.h8
-rw-r--r--Swiften/Jingle/JingleResponder.cpp25
-rw-r--r--Swiften/Jingle/JingleResponder.h3
-rw-r--r--Swiften/Jingle/JingleSession.cpp4
-rw-r--r--Swiften/Jingle/JingleSession.h20
-rw-r--r--Swiften/Jingle/JingleSessionImpl.cpp159
-rw-r--r--Swiften/Jingle/JingleSessionImpl.h22
-rw-r--r--Swiften/Jingle/JingleSessionManager.cpp15
-rw-r--r--Swiften/Jingle/JingleSessionManager.h11
-rw-r--r--Swiften/Jingle/SConscript1
-rw-r--r--Swiften/Network/BoostNetworkFactories.cpp3
-rw-r--r--Swiften/Network/BoostNetworkFactories.h5
-rw-r--r--Swiften/Network/DummyConnectionFactory.h29
-rw-r--r--Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp62
-rw-r--r--Swiften/Network/NATPMPNATTraversalForwardPortRequest.h24
-rw-r--r--Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp64
-rw-r--r--Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h22
-rw-r--r--Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp83
-rw-r--r--Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h27
-rw-r--r--Swiften/Network/NetworkEnvironment.h24
-rw-r--r--Swiften/Network/NetworkFactories.h2
-rw-r--r--Swiften/Network/NetworkInterface.h35
-rw-r--r--Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp18
-rw-r--r--Swiften/Network/PlatformNATTraversalForwardPortRequest.h38
-rw-r--r--Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp18
-rw-r--r--Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h25
-rw-r--r--Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp18
-rw-r--r--Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h38
-rw-r--r--Swiften/Network/PlatformNATTraversalRequest.cpp25
-rw-r--r--Swiften/Network/PlatformNATTraversalRequest.h31
-rw-r--r--Swiften/Network/PlatformNATTraversalWorker.cpp140
-rw-r--r--Swiften/Network/PlatformNATTraversalWorker.h60
-rw-r--r--Swiften/Network/PlatformNetworkEnvironment.h26
-rw-r--r--Swiften/Network/SConscript16
-rw-r--r--Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp86
-rw-r--r--Swiften/Network/UPnPNATTraversalForwardPortRequest.h27
-rw-r--r--Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp57
-rw-r--r--Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h21
-rw-r--r--Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp92
-rw-r--r--Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h27
-rw-r--r--Swiften/Network/UnixNetworkEnvironment.cpp74
-rw-r--r--Swiften/Network/UnixNetworkEnvironment.h59
-rw-r--r--Swiften/Network/WindowsNetworkEnvironment.cpp97
-rw-r--r--Swiften/Network/WindowsNetworkEnvironment.h56
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp23
-rw-r--r--Swiften/Parser/PayloadParsers/IBBParser.cpp2
-rw-r--r--Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp74
-rw-r--r--Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h30
-rw-r--r--Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h35
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp75
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h38
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h35
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp43
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h27
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp49
-rw-r--r--Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h27
-rw-r--r--Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp38
-rw-r--r--Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h26
-rw-r--r--Swiften/Parser/PayloadParsers/JingleParser.cpp119
-rw-r--r--Swiften/Parser/PayloadParsers/JingleParser.h32
-rw-r--r--Swiften/Parser/PayloadParsers/JingleParserFactory.h35
-rw-r--r--Swiften/Parser/PayloadParsers/JingleReasonParser.cpp81
-rw-r--r--Swiften/Parser/PayloadParsers/JingleReasonParser.h30
-rw-r--r--Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp88
-rw-r--r--Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h29
-rw-r--r--Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp64
-rw-r--r--Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h30
-rw-r--r--Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp69
-rw-r--r--Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h28
-rw-r--r--Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp6
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp709
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp6
-rw-r--r--Swiften/Parser/SConscript10
-rw-r--r--Swiften/SConscript17
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp21
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp79
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h25
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp46
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h25
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp45
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h23
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp35
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h23
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp31
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h25
-rw-r--r--Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp144
-rw-r--r--Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h30
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp85
-rw-r--r--Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h27
-rw-r--r--Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h38
-rw-r--r--Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp56
-rw-r--r--Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h24
-rw-r--r--Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp10
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp512
-rw-r--r--Swiften/StringCodecs/Hexify.cpp18
-rw-r--r--Swiften/StringCodecs/Hexify.h1
-rw-r--r--Swiften/StringCodecs/MD5.cpp21
-rw-r--r--Swiften/StringCodecs/MD5.h11
-rw-r--r--Swiften/StringCodecs/UnitTest/HexifyTest.cpp6
-rw-r--r--Swiften/StringCodecs/UnitTest/MD5Test.cpp11
285 files changed, 18750 insertions, 251 deletions
diff --git a/.gitignore b/.gitignore
index 42586a9..6500850 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,6 @@ checker-report.xml
VERSION.*
cppcheck.log
/build
+/.settings/
+/nbproject/private/
+3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpcstrings.h
diff --git a/3rdParty/LibMiniUPnPc/SConscript b/3rdParty/LibMiniUPnPc/SConscript
new file mode 100644
index 0000000..be9910c
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/SConscript
@@ -0,0 +1,66 @@
+Import(["env", "conf_env"])
+
+if env.get("LIBMINIUPNPC_BUNDLED", False) :
+
+################################################################################
+# Module flags
+################################################################################
+
+ if env["SCONS_STAGE"] == "flags" :
+ env["LIBMINIUPNPC_FLAGS"] = {
+ "CPPPATH": [Dir("src/miniupnpc")],
+ "LIBPATH": [Dir(".")],
+ "LIBS": ["Swiften_MiniUPnPc"],
+ }
+ #if env["PLATFORM"] == "win32" :
+ # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")]
+ # if env["MSVC_VERSION"][:3] == "9.0" :
+ # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")]
+
+################################################################################
+# Build
+################################################################################
+
+ if env["SCONS_STAGE"] == "build" :
+ myenv = env.Clone()
+ myenv.Append(CPPPATH = ["src"])
+ # Remove warn flags
+ myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]])
+
+ myenv.Append(CCFLAGS = ["-DNDEBUG", "-DSTATICLIB"])
+
+ if myenv["PLATFORM"] != "win32":
+ myenv.Append(CCFLAGS = ["-DMINIUPNPC_SET_SOCKET_TIMEOUT"])
+
+ if myenv["PLATFORM"] == "darwin":
+ myenv.Append(CCFLAGS = ["-DMACOSX", "-D_DARWIN_C_SOURCE"])
+
+ if myenv["PLATFORM"] == "win32":
+ myenv.Append(CCFLAGS = ["-DWIN32", "-D_WIN32_WINNT=0x0501"])
+
+ myenv.WriteVal("src/miniupnpc/miniupnpcstrings.h", myenv.Value(
+"""
+#ifndef __MINIUPNPCSTRINGS_H__
+#define __MINIUPNPCSTRINGS_H__
+
+#define OS_STRING "$OS_STRING"
+#define MINIUPNPC_VERSION_STRING "1.5"
+
+#endif
+""".replace("$OS_STRING", myenv["PLATFORM"])))
+
+ myenv.StaticLibrary("Swiften_MiniUPnPc", [
+ "src/miniupnpc/igd_desc_parse.c",
+ "src/miniupnpc/miniupnpc.c",
+ "src/miniupnpc/minixml.c",
+ "src/miniupnpc/minisoap.c",
+ "src/miniupnpc/minissdpc.c",
+ "src/miniupnpc/miniwget.c",
+ #"src/miniupnpc/upnpc.c",
+ "src/miniupnpc/upnpcommands.c",
+ "src/miniupnpc/upnpreplyparse.c",
+ "src/miniupnpc/upnperrors.c",
+ "src/miniupnpc/connecthostport.c",
+ "src/miniupnpc/portlistingparse.c",
+ "src/miniupnpc/receivedata.c"
+ ])
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE b/3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE
new file mode 100644
index 0000000..2434c86
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE
@@ -0,0 +1,27 @@
+MiniUPnPc
+Copyright (c) 2005-2011, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h
new file mode 100644
index 0000000..1fe0599
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h
@@ -0,0 +1,531 @@
+/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#ifdef QUEUE_MACRO_DEBUG
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#ifdef SLIST_ENTRY
+#undef SLIST_ENTRY
+#endif
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h
new file mode 100644
index 0000000..f11e5e9
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h
@@ -0,0 +1,24 @@
+/* $Id: codelength.h,v 1.1 2008/10/06 22:04:06 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2008 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef __CODELENGTH_H__
+#define __CODELENGTH_H__
+
+/* Encode length by using 7bit per Byte :
+ * Most significant bit of each byte specifies that the
+ * following byte is part of the code */
+#define DECODELENGTH(n, p) n = 0; \
+ do { n = (n << 7) | (*p & 0x7f); } \
+ while(*(p++)&0x80);
+
+#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
+ if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
+ if(n>=16384) *(p++) = (n >> 14) | 0x80; \
+ if(n>=128) *(p++) = (n >> 7) | 0x80; \
+ *(p++) = n & 0x7f;
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c
new file mode 100644
index 0000000..76e8e37
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c
@@ -0,0 +1,241 @@
+/* $Id: connecthostport.c,v 1.5 2011/04/09 08:49:50 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2010-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+/* use getaddrinfo() or gethostbyname()
+ * uncomment the following line in order to use gethostbyname() */
+#ifdef NO_GETADDRINFO
+#define USE_GETHOSTBYNAME
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define herror
+#define socklen_t int
+#else /* #ifdef WIN32 */
+#include <unistd.h>
+#include <sys/param.h>
+#include <errno.h>
+#define closesocket close
+#include <netdb.h>
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#ifndef USE_GETHOSTBYNAME
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif /* #ifndef USE_GETHOSTBYNAME */
+#endif /* #else WIN32 */
+
+/* definition of PRINT_SOCKET_ERROR */
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+#define herror(A) printf("%s\n", A)
+#endif
+
+#include "connecthostport.h"
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+int connecthostport(const char * host, unsigned short port)
+{
+ int s, n;
+#ifdef USE_GETHOSTBYNAME
+ struct sockaddr_in dest;
+ struct hostent *hp;
+#else /* #ifdef USE_GETHOSTBYNAME */
+ char tmp_host[MAXHOSTNAMELEN+1];
+ char port_str[8];
+ struct addrinfo *ai, *p;
+ struct addrinfo hints;
+#endif /* #ifdef USE_GETHOSTBYNAME */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+#ifdef USE_GETHOSTBYNAME
+ hp = gethostbyname(host);
+ if(hp == NULL)
+ {
+ herror(host);
+ return -1;
+ }
+ memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
+ memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if(s < 0)
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return -1;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons(port);
+ n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
+#ifdef MINIUPNPC_IGNORE_EINTR
+ while(n < 0 && errno == EINTR)
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ return -1;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n<0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ closesocket(s);
+ return -1;
+ }
+#else /* #ifdef USE_GETHOSTBYNAME */
+ /* use getaddrinfo() instead of gethostbyname() */
+ memset(&hints, 0, sizeof(hints));
+ /* hints.ai_flags = AI_ADDRCONFIG; */
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
+ /* hints.ai_protocol = IPPROTO_TCP; */
+ snprintf(port_str, sizeof(port_str), "%hu", port);
+ if(host[0] == '[')
+ {
+ /* literal ip v6 address */
+ int i;
+ for(i = 0; host[i+1] && (host[i+1] != ']') && i < MAXHOSTNAMELEN; i++)
+ {
+ tmp_host[i] = host[i+1];
+ }
+ tmp_host[i] = '\0';
+ }
+ else
+ {
+ strncpy(tmp_host, host, MAXHOSTNAMELEN);
+ }
+ tmp_host[MAXHOSTNAMELEN] = '\0';
+ n = getaddrinfo(tmp_host, port_str, &hints, &ai);
+ if(n != 0)
+ {
+#ifdef WIN32
+ fprintf(stderr, "getaddrinfo() error : %d\n", n);
+#else
+ fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
+#endif
+ return -1;
+ }
+ s = -1;
+ for(p = ai; p; p = p->ai_next)
+ {
+ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if(s < 0)
+ continue;
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ n = connect(s, p->ai_addr, p->ai_addrlen);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ while(n < 0 && errno == EINTR)
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ freeaddrinfo(ai);
+ return -1;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n < 0)
+ {
+ closesocket(s);
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ freeaddrinfo(ai);
+ if(s < 0)
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return -1;
+ }
+ if(n < 0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ return -1;
+ }
+#endif /* #ifdef USE_GETHOSTBYNAME */
+ return s;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h
new file mode 100644
index 0000000..57e24eb
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h
@@ -0,0 +1,17 @@
+/* $Id: connecthostport.h,v 1.1 2010/04/04 23:21:03 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2010 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __CONNECTHOSTPORT_H__
+#define __CONNECTHOSTPORT_H__
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+int connecthostport(const char * host, unsigned short port);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h
new file mode 100644
index 0000000..b804247
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h
@@ -0,0 +1,15 @@
+#ifndef __DECLSPEC_H__
+#define __DECLSPEC_H__
+
+#if defined(WIN32) && !defined(STATICLIB)
+ #ifdef MINIUPNP_EXPORTS
+ #define LIBSPEC __declspec(dllexport)
+ #else
+ #define LIBSPEC __declspec(dllimport)
+ #endif
+#else
+ #define LIBSPEC
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c
new file mode 100644
index 0000000..6c3e656
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c
@@ -0,0 +1,125 @@
+/* $Id: igd_desc_parse.c,v 1.14 2011/04/11 09:19:24 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2010 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include "igd_desc_parse.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Start element handler :
+ * update nesting level counter and copy element name */
+void IGDstartelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ memcpy( datas->cureltname, name, l);
+ datas->cureltname[l] = '\0';
+ datas->level++;
+ if( (l==7) && !memcmp(name, "service", l) ) {
+ datas->tmp.controlurl[0] = '\0';
+ datas->tmp.eventsuburl[0] = '\0';
+ datas->tmp.scpdurl[0] = '\0';
+ datas->tmp.servicetype[0] = '\0';
+ }
+}
+
+/* End element handler :
+ * update nesting level counter and update parser state if
+ * service element is parsed */
+void IGDendelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ datas->level--;
+ /*printf("endelt %2d %.*s\n", datas->level, l, name);*/
+ if( (l==7) && !memcmp(name, "service", l) )
+ {
+ /*
+ if( datas->state < 1
+ && !strcmp(datas->servicetype,
+ // "urn:schemas-upnp-org:service:WANIPConnection:1") )
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
+ datas->state ++;
+ */
+ if(0==strcmp(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
+ memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(0==strcmp(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1")) {
+ memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(0==strcmp(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPConnection:1")
+ || 0==strcmp(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
+ if(datas->first.servicetype[0] == '\0') {
+ memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else {
+ memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
+ }
+ }
+ }
+}
+
+/* Data handler :
+ * copy data depending on the current element name and state */
+void IGDdata(void * d, const char * data, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ char * dstmember = 0;
+ /*printf("%2d %s : %.*s\n",
+ datas->level, datas->cureltname, l, data); */
+ if( !strcmp(datas->cureltname, "URLBase") )
+ dstmember = datas->urlbase;
+ else if( !strcmp(datas->cureltname, "presentationURL") )
+ dstmember = datas->presentationurl;
+ else if( !strcmp(datas->cureltname, "serviceType") )
+ dstmember = datas->tmp.servicetype;
+ else if( !strcmp(datas->cureltname, "controlURL") )
+ dstmember = datas->tmp.controlurl;
+ else if( !strcmp(datas->cureltname, "eventSubURL") )
+ dstmember = datas->tmp.eventsuburl;
+ else if( !strcmp(datas->cureltname, "SCPDURL") )
+ dstmember = datas->tmp.scpdurl;
+/* else if( !strcmp(datas->cureltname, "deviceType") )
+ dstmember = datas->devicetype_tmp;*/
+ if(dstmember)
+ {
+ if(l>=MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(dstmember, data, l);
+ dstmember[l] = '\0';
+ }
+}
+
+void printIGD(struct IGDdatas * d)
+{
+ printf("urlbase = '%s'\n", d->urlbase);
+ printf("WAN Device (Common interface config) :\n");
+ /*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
+ printf(" serviceType = '%s'\n", d->CIF.servicetype);
+ printf(" controlURL = '%s'\n", d->CIF.controlurl);
+ printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
+ printf("primary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->first.devicetype);*/
+ printf(" servicetype = '%s'\n", d->first.servicetype);
+ printf(" controlURL = '%s'\n", d->first.controlurl);
+ printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->first.scpdurl);
+ printf("secondary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->second.devicetype);*/
+ printf(" servicetype = '%s'\n", d->second.servicetype);
+ printf(" controlURL = '%s'\n", d->second.controlurl);
+ printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->second.scpdurl);
+ printf("WAN IPv6 Firewall Control :\n");
+ /*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
+ printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
+ printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
+ printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h
new file mode 100644
index 0000000..bab1fd5
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h
@@ -0,0 +1,48 @@
+/* $Id: igd_desc_parse.h,v 1.10 2011/04/11 09:19:24 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2010 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __IGD_DESC_PARSE_H__
+#define __IGD_DESC_PARSE_H__
+
+/* Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices */
+#define MINIUPNPC_URL_MAXSIZE (128)
+struct IGDdatas_service {
+ char controlurl[MINIUPNPC_URL_MAXSIZE];
+ char eventsuburl[MINIUPNPC_URL_MAXSIZE];
+ char scpdurl[MINIUPNPC_URL_MAXSIZE];
+ char servicetype[MINIUPNPC_URL_MAXSIZE];
+ /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
+};
+
+struct IGDdatas {
+ char cureltname[MINIUPNPC_URL_MAXSIZE];
+ char urlbase[MINIUPNPC_URL_MAXSIZE];
+ char presentationurl[MINIUPNPC_URL_MAXSIZE];
+ int level;
+ /*int state;*/
+ /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+ struct IGDdatas_service CIF;
+ /* "urn:schemas-upnp-org:service:WANIPConnection:1"
+ * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+ struct IGDdatas_service first;
+ /* if both WANIPConnection and WANPPPConnection are present */
+ struct IGDdatas_service second;
+ /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
+ struct IGDdatas_service IPv6FC;
+ /* tmp */
+ struct IGDdatas_service tmp;
+};
+
+void IGDstartelt(void *, const char *, int);
+void IGDendelt(void *, const char *, int);
+void IGDdata(void *, const char *, int);
+void printIGD(struct IGDdatas *);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c
new file mode 100644
index 0000000..8889bf0
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c
@@ -0,0 +1,121 @@
+/* $Id: minisoap.c,v 1.21 2011/03/22 19:15:35 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2009 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ *
+ * Minimal SOAP implementation for UPnP protocol.
+ */
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#include <io.h>
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include "minisoap.h"
+#include "miniupnpcstrings.h"
+
+/* only for malloc */
+#include <stdlib.h>
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+/* httpWrite sends the headers and the body to the socket
+ * and returns the number of bytes sent */
+static int
+httpWrite(int fd, const char * body, int bodysize,
+ const char * headers, int headerssize)
+{
+ int n = 0;
+ /*n = write(fd, headers, headerssize);*/
+ /*if(bodysize>0)
+ n += write(fd, body, bodysize);*/
+ /* Note : my old linksys router only took into account
+ * soap request that are sent into only one packet */
+ char * p;
+ /* TODO: AVOID MALLOC */
+ p = malloc(headerssize+bodysize);
+ if(!p)
+ return 0;
+ memcpy(p, headers, headerssize);
+ memcpy(p+headerssize, body, bodysize);
+ /*n = write(fd, p, headerssize+bodysize);*/
+ n = send(fd, p, headerssize+bodysize, 0);
+ if(n<0) {
+ PRINT_SOCKET_ERROR("send");
+ }
+ /* disable send on the socket */
+ /* draytek routers dont seems to like that... */
+#if 0
+#ifdef WIN32
+ if(shutdown(fd, SD_SEND)<0) {
+#else
+ if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
+#endif
+ PRINT_SOCKET_ERROR("shutdown");
+ }
+#endif
+ free(p);
+ return n;
+}
+
+/* self explanatory */
+int soapPostSubmit(int fd,
+ const char * url,
+ const char * host,
+ unsigned short port,
+ const char * action,
+ const char * body,
+ const char * httpversion)
+{
+ int bodysize;
+ char headerbuf[512];
+ int headerssize;
+ char portstr[8];
+ bodysize = (int)strlen(body);
+ /* We are not using keep-alive HTTP connections.
+ * HTTP/1.1 needs the header Connection: close to do that.
+ * This is the default with HTTP/1.0
+ * Using HTTP/1.1 means we need to support chunked transfer-encoding :
+ * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
+ * transfer encoding. */
+ /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+ portstr[0] = '\0';
+ if(port != 80)
+ snprintf(portstr, sizeof(portstr), ":%hu", port);
+ headerssize = snprintf(headerbuf, sizeof(headerbuf),
+ "POST %s HTTP/%s\r\n"
+ "Host: %s%s\r\n"
+ "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+ "Content-Length: %d\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%s\"\r\n"
+ "Connection: Close\r\n"
+ "Cache-Control: no-cache\r\n" /* ??? */
+ "Pragma: no-cache\r\n"
+ "\r\n",
+ url, httpversion, host, portstr, bodysize, action);
+#ifdef DEBUG
+ /*printf("SOAP request : headersize=%d bodysize=%d\n",
+ headerssize, bodysize);
+ */
+ printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
+ url, httpversion, host, portstr);
+ printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
+ printf("Headers :\n%s", headerbuf);
+ printf("Body :\n%s\n", body);
+#endif
+ return httpWrite(fd, body, bodysize, headerbuf, headerssize);
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h
new file mode 100644
index 0000000..696725f
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h
@@ -0,0 +1,15 @@
+/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+#ifndef __MINISOAP_H__
+#define __MINISOAP_H__
+
+/*int httpWrite(int, const char *, int, const char *);*/
+int soapPostSubmit(int, const char *, const char *, unsigned short,
+ const char *, const char *, const char *);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c
new file mode 100644
index 0000000..e61c1cd
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c
@@ -0,0 +1,138 @@
+/* $Id: minissdpc.c,v 1.14 2010/11/25 09:57:25 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2009 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+/*#include <syslog.h>*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#include <sys/types.h>
+#else
+#define ssize_t int
+#endif
+
+#if defined(WIN32) || defined(__amigaos__) || defined(__amigaos4__)
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <winsock.h>
+#include <stdint.h>
+#endif
+#if defined(__amigaos__) || defined(__amigaos4__)
+#include <sys/socket.h>
+#endif
+#if defined(__amigaos__)
+#define uint16_t unsigned short
+#endif
+/* Hack */
+#define UNIX_PATH_LEN 108
+struct sockaddr_un {
+ uint16_t sun_family;
+ char sun_path[UNIX_PATH_LEN];
+};
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+#include "minissdpc.h"
+#include "miniupnpc.h"
+
+#include "codelength.h"
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = NULL;
+ unsigned char buffer[2048];
+ ssize_t n;
+ unsigned char * p;
+ unsigned char * url;
+ unsigned int i;
+ unsigned int urlsize, stsize, usnsize, l;
+ int s;
+ struct sockaddr_un addr;
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(s < 0)
+ {
+ /*syslog(LOG_ERR, "socket(unix): %m");*/
+ perror("socket(unix)");
+ return NULL;
+ }
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
+ /* TODO : check if we need to handle the EINTR */
+ if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
+ {
+ /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
+ close(s);
+ return NULL;
+ }
+ stsize = strlen(devtype);
+ buffer[0] = 1; /* request type 1 : request devices/services by type */
+ p = buffer + 1;
+ l = stsize; CODELENGTH(l, p);
+ if(p + stsize > buffer + sizeof(buffer))
+ {
+ /* devtype is too long ! */
+ close(s);
+ return NULL;
+ }
+ memcpy(p, devtype, stsize);
+ p += stsize;
+ if(write(s, buffer, p - buffer) < 0)
+ {
+ /*syslog(LOG_ERR, "write(): %m");*/
+ perror("minissdpc.c: write()");
+ close(s);
+ return NULL;
+ }
+ n = read(s, buffer, sizeof(buffer));
+ if(n<=0)
+ {
+ perror("minissdpc.c: read()");
+ close(s);
+ return NULL;
+ }
+ p = buffer + 1;
+ for(i = 0; i < buffer[0]; i++)
+ {
+ if(p+2>=buffer+sizeof(buffer))
+ break;
+ DECODELENGTH(urlsize, p);
+ if(p+urlsize+2>=buffer+sizeof(buffer))
+ break;
+ url = p;
+ p += urlsize;
+ DECODELENGTH(stsize, p);
+ if(p+stsize+2>=buffer+sizeof(buffer))
+ break;
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ memcpy(tmp->buffer, url, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->buffer + urlsize + 1, p, stsize);
+ p += stsize;
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ devlist = tmp;
+ /* added for compatibility with recent versions of MiniSSDPd
+ * >= 2007/12/19 */
+ DECODELENGTH(usnsize, p);
+ p += usnsize;
+ if(p>buffer + sizeof(buffer))
+ break;
+ }
+ close(s);
+ return devlist;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h
new file mode 100644
index 0000000..25e91ce
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h
@@ -0,0 +1,15 @@
+/* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2007 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __MINISSDPC_H__
+#define __MINISSDPC_H__
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c
new file mode 100644
index 0000000..bd46a24
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c
@@ -0,0 +1,906 @@
+/* $Id: miniupnpc.c,v 1.95 2011/05/15 21:42:26 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2011 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#define __EXTENSIONS__ 1
+#if !defined(MACOSX) && !defined(__sun)
+#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+#ifndef __cplusplus
+#define _XOPEN_SOURCE 600
+#endif
+#endif
+#ifndef __BSD_VISIBLE
+#define __BSD_VISIBLE 1
+#endif
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+/* Win32 Specific includes and defines */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#define snprintf _snprintf
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#define MAXHOSTNAMELEN 64
+#else /* #ifdef WIN32 */
+/* Standard POSIX includes */
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+/* Amiga OS 3 specific stuff */
+#define socklen_t int
+#else
+#include <sys/select.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif
+#include <strings.h>
+#include <errno.h>
+#define closesocket close
+#endif /* #else WIN32 */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#include <sys/time.h>
+#endif
+#if defined(__amigaos__) || defined(__amigaos4__)
+/* Amiga OS specific stuff */
+#define TIMEVAL struct timeval
+#endif
+
+#include "miniupnpc.h"
+#include "minissdpc.h"
+#include "miniwget.h"
+#include "minisoap.h"
+#include "minixml.h"
+#include "upnpcommands.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#define SOAPPREFIX "s"
+#define SERVICEPREFIX "u"
+#define SERVICEPREFIX2 'u'
+
+/* root description parsing */
+LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
+{
+ struct xmlparser parser;
+ /* xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = IGDstartelt;
+ parser.endeltfunc = IGDendelt;
+ parser.datafunc = IGDdata;
+ parser.attfunc = 0;
+ parsexml(&parser);
+#ifdef DEBUG
+ printIGD(data);
+#endif
+}
+
+/* simpleUPnPcommand2 :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+char * simpleUPnPcommand2(int s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize, const char * httpversion)
+{
+ char hostname[MAXHOSTNAMELEN+1];
+ unsigned short port = 0;
+ char * path;
+ char soapact[128];
+ char soapbody[2048];
+ char * buf;
+ int n;
+
+ *bufsize = 0;
+ snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
+ if(args==NULL)
+ {
+ /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
+ "</" SERVICEPREFIX ":%s>"
+ "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
+ "\r\n", action, service, action);
+ }
+ else
+ {
+ char * p;
+ const char * pe, * pv;
+ int soapbodylen;
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
+ action, service);
+ p = soapbody + soapbodylen;
+ while(args->elt)
+ {
+ /* check that we are never overflowing the string... */
+ if(soapbody + sizeof(soapbody) <= p + 100)
+ {
+ /* we keep a margin of at least 100 bytes */
+ return NULL;
+ }
+ *(p++) = '<';
+ pe = args->elt;
+ while(*pe)
+ *(p++) = *(pe++);
+ *(p++) = '>';
+ if((pv = args->val))
+ {
+ while(*pv)
+ *(p++) = *(pv++);
+ }
+ *(p++) = '<';
+ *(p++) = '/';
+ pe = args->elt;
+ while(*pe)
+ *(p++) = *(pe++);
+ *(p++) = '>';
+ args++;
+ }
+ *(p++) = '<';
+ *(p++) = '/';
+ *(p++) = SERVICEPREFIX2;
+ *(p++) = ':';
+ pe = action;
+ while(*pe)
+ *(p++) = *(pe++);
+ strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
+ soapbody + sizeof(soapbody) - p);
+ }
+ if(!parseURL(url, hostname, &port, &path)) return NULL;
+ if(s<0)
+ {
+ s = connecthostport(hostname, port);
+ if(s < 0)
+ {
+ return NULL;
+ }
+ }
+
+ n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
+ if(n<=0) {
+#ifdef DEBUG
+ printf("Error sending SOAP request\n");
+#endif
+ closesocket(s);
+ return NULL;
+ }
+
+ buf = getHTTPResponse(s, bufsize);
+#ifdef DEBUG
+ if(*bufsize > 0 && buf)
+ {
+ printf("SOAP Response :\n%.*s\n", *bufsize, buf);
+ }
+#endif
+ closesocket(s);
+ return buf;
+}
+
+/* simpleUPnPcommand :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+char * simpleUPnPcommand(int s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize)
+{
+ char * buf;
+
+ buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
+/*
+ buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
+ if (!buf || *bufsize == 0)
+ {
+#if DEBUG
+ printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
+#endif
+ buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
+ }
+*/
+ return buf;
+}
+
+/* parseMSEARCHReply()
+ * the last 4 arguments are filled during the parsing :
+ * - location/locationsize : "location:" field of the SSDP reply packet
+ * - st/stsize : "st:" field of the SSDP reply packet.
+ * The strings are NOT null terminated */
+static void
+parseMSEARCHReply(const char * reply, int size,
+ const char * * location, int * locationsize,
+ const char * * st, int * stsize)
+{
+ int a, b, i;
+ i = 0;
+ a = i; /* start of the line */
+ b = 0; /* end of the "header" (position of the colon) */
+ while(i<size)
+ {
+ switch(reply[i])
+ {
+ case ':':
+ if(b==0)
+ {
+ b = i; /* end of the "header" */
+ /*for(j=a; j<b; j++)
+ {
+ putchar(reply[j]);
+ }
+ */
+ }
+ break;
+ case '\x0a':
+ case '\x0d':
+ if(b!=0)
+ {
+ /*for(j=b+1; j<i; j++)
+ {
+ putchar(reply[j]);
+ }
+ putchar('\n');*/
+ /* skip the colon and white spaces */
+ do { b++; } while(reply[b]==' ');
+ if(0==strncasecmp(reply+a, "location", 8))
+ {
+ *location = reply+b;
+ *locationsize = i-b;
+ }
+ else if(0==strncasecmp(reply+a, "st", 2))
+ {
+ *st = reply+b;
+ *stsize = i-b;
+ }
+ b = 0;
+ }
+ a = i+1;
+ break;
+ default:
+ break;
+ }
+ i++;
+ }
+}
+
+/* port upnp discover : SSDP protocol */
+#define PORT 1900
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define UPNP_MCAST_ADDR "239.255.255.250"
+/* for IPv6 */
+#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
+#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
+
+/* upnpDiscover() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll) */
+LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int sameport,
+ int ipv6,
+ int * error)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = 0;
+ int opt = 1;
+ static const char MSearchMsgFmt[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: %s:" XSTR(PORT) "\r\n"
+ "ST: %s\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: %u\r\n"
+ "\r\n";
+ static const char * const deviceList[] = {
+#if 0
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
+ "urn:schemas-upnp-org:service:WANIPConnection:2",
+#endif
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+ "urn:schemas-upnp-org:service:WANIPConnection:1",
+ "urn:schemas-upnp-org:service:WANPPPConnection:1",
+ "upnp:rootdevice",
+ 0
+ };
+ int deviceIndex = 0;
+ char bufr[1536]; /* reception and emission buffer */
+ int sudp;
+ int n;
+ struct sockaddr_storage sockudp_r;
+ unsigned int mx;
+#ifdef NO_GETADDRINFO
+ struct sockaddr_storage sockudp_w;
+#else
+ int rv;
+ struct addrinfo hints, *servinfo, *p;
+#endif
+#ifdef WIN32
+ MIB_IPFORWARDROW ip_forward;
+#endif
+ int linklocal = 1;
+
+ if(error)
+ *error = UPNPDISCOVER_UNKNOWN_ERROR;
+#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* first try to get infos from minissdpd ! */
+ if(!minissdpdsock)
+ minissdpdsock = "/var/run/minissdpd.sock";
+ while(!devlist && deviceList[deviceIndex]) {
+ devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
+ minissdpdsock);
+ /* We return what we have found if it was not only a rootdevice */
+ if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
+ if(error)
+ *error = UPNPDISCOVER_SUCCESS;
+ return devlist;
+ }
+ deviceIndex++;
+ }
+ deviceIndex = 0;
+#endif
+ /* fallback to direct discovery */
+#ifdef WIN32
+ sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else
+ sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
+#endif
+ if(sudp < 0)
+ {
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("socket");
+ return NULL;
+ }
+ /* reception */
+ memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
+ p->sin6_family = AF_INET6;
+ if(sameport)
+ p->sin6_port = htons(PORT);
+ p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
+ p->sin_family = AF_INET;
+ if(sameport)
+ p->sin_port = htons(PORT);
+ p->sin_addr.s_addr = INADDR_ANY;
+ }
+#ifdef WIN32
+/* This code could help us to use the right Network interface for
+ * SSDP multicast traffic */
+/* Get IP associated with the index given in the ip_forward struct
+ * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
+ if(!ipv6
+ && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
+ DWORD dwRetVal = 0;
+ PMIB_IPADDRTABLE pIPAddrTable;
+ DWORD dwSize = 0;
+#ifdef DEBUG
+ IN_ADDR IPAddr;
+#endif
+ int i;
+#ifdef DEBUG
+ printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
+#endif
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
+ if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
+ free(pIPAddrTable);
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
+ }
+ if(pIPAddrTable) {
+ dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+#ifdef DEBUG
+ printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
+#endif
+ for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
+#ifdef DEBUG
+ printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
+ printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
+ printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
+ printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
+ printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
+ printf("\tType and State[%d]:", i);
+ printf("\n");
+#endif
+ if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
+ /* Set the address of this interface to be used */
+ struct in_addr mc_if;
+ memset(&mc_if, 0, sizeof(mc_if));
+ mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
+#ifndef DEBUG
+ break;
+#endif
+ }
+ }
+ free(pIPAddrTable);
+ pIPAddrTable = NULL;
+ }
+ }
+#endif
+
+#ifdef WIN32
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
+#else
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
+#endif
+ {
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("setsockopt");
+ return NULL;
+ }
+
+ if(multicastif)
+ {
+ if(ipv6) {
+#if !defined(WIN32)
+ /* according to MSDN, if_nametoindex() is supported since
+ * MS Windows Vista and MS Windows Server 2008.
+ * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
+ unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#else
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
+#endif
+#endif
+ } else {
+ struct in_addr mc_if;
+ mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ }
+ }
+
+ /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
+ if (bind(sudp, (const struct sockaddr *)&sockudp_r,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
+ {
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("bind");
+ closesocket(sudp);
+ return NULL;
+ }
+
+ if(error)
+ *error = UPNPDISCOVER_SUCCESS;
+ /* Calculating maximum response time in seconds */
+ mx = ((unsigned int)delay) / 1000u;
+ /* receiving SSDP response packet */
+ for(n = 0; deviceList[deviceIndex]; deviceIndex++)
+ {
+ if(n == 0)
+ {
+ /* sending the SSDP M-SEARCH packet */
+ n = snprintf(bufr, sizeof(bufr),
+ MSearchMsgFmt,
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceList[deviceIndex], mx);
+#ifdef DEBUG
+ printf("Sending %s", bufr);
+#endif
+#ifdef NO_GETADDRINFO
+ /* the following code is not using getaddrinfo */
+ /* emission */
+ memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
+ p->sin6_family = AF_INET6;
+ p->sin6_port = htons(PORT);
+ inet_pton(AF_INET6,
+ linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
+ &(p->sin6_addr));
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
+ p->sin_family = AF_INET;
+ p->sin_port = htons(PORT);
+ p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
+ }
+ n = sendto(sudp, bufr, n, 0,
+ &sockudp_w,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+ if (n < 0) {
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("sendto");
+ break;
+ }
+#else /* #ifdef NO_GETADDRINFO */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
+ hints.ai_socktype = SOCK_DGRAM;
+ /*hints.ai_flags = */
+ if ((rv = getaddrinfo(ipv6
+ ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
+ : UPNP_MCAST_ADDR,
+ XSTR(PORT), &hints, &servinfo)) != 0) {
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+#ifdef WIN32
+ fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
+#else
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+#endif
+ break;
+ }
+ for(p = servinfo; p; p = p->ai_next) {
+ n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
+ if (n < 0) {
+ PRINT_SOCKET_ERROR("sendto");
+ continue;
+ }
+ }
+ freeaddrinfo(servinfo);
+ if(n < 0) {
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+ break;
+ }
+#endif /* #ifdef NO_GETADDRINFO */
+ }
+ /* Waiting for SSDP REPLY packet to M-SEARCH */
+ n = receivedata(sudp, bufr, sizeof(bufr), delay);
+ if (n < 0) {
+ /* error */
+ if(error)
+ *error = UPNPDISCOVER_SOCKET_ERROR;
+ break;
+ } else if (n == 0) {
+ /* no data or Time Out */
+ if (devlist) {
+ /* no more device type to look for... */
+ if(error)
+ *error = UPNPDISCOVER_SUCCESS;
+ break;
+ }
+ if(ipv6) {
+ if(linklocal) {
+ linklocal = 0;
+ --deviceIndex;
+ } else {
+ linklocal = 1;
+ }
+ }
+ } else {
+ const char * descURL=NULL;
+ int urlsize=0;
+ const char * st=NULL;
+ int stsize=0;
+ /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
+ parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
+ if(st&&descURL)
+ {
+#ifdef DEBUG
+ printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
+ stsize, st, urlsize, descURL);
+#endif
+ for(tmp=devlist; tmp; tmp = tmp->pNext) {
+ if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
+ tmp->descURL[urlsize] == '\0' &&
+ memcmp(tmp->st, st, stsize) == 0 &&
+ tmp->st[stsize] == '\0')
+ break;
+ }
+ /* at the exit of the loop above, tmp is null if
+ * no duplicate device was found */
+ if(tmp)
+ continue;
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
+ if(!tmp) {
+ /* memory allocation error */
+ if(error)
+ *error = UPNPDISCOVER_MEMORY_ERROR;
+ break;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ memcpy(tmp->buffer, descURL, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->buffer + urlsize + 1, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ devlist = tmp;
+ }
+ }
+ }
+ closesocket(sudp);
+ return devlist;
+}
+
+/* freeUPNPDevlist() should be used to
+ * free the chained list returned by upnpDiscover() */
+LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
+{
+ struct UPNPDev * next;
+ while(devlist)
+ {
+ next = devlist->pNext;
+ free(devlist);
+ devlist = next;
+ }
+}
+
+static void
+url_cpy_or_cat(char * dst, const char * src, int n)
+{
+ if( (src[0] == 'h')
+ &&(src[1] == 't')
+ &&(src[2] == 't')
+ &&(src[3] == 'p')
+ &&(src[4] == ':')
+ &&(src[5] == '/')
+ &&(src[6] == '/'))
+ {
+ strncpy(dst, src, n);
+ }
+ else
+ {
+ int l = strlen(dst);
+ if(src[0] != '/')
+ dst[l++] = '/';
+ if(l<=n)
+ strncpy(dst + l, src, n - l);
+ }
+}
+
+/* Prepare the Urls for usage...
+ */
+LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * descURL)
+{
+ char * p;
+ int n1, n2, n3, n4;
+ n1 = strlen(data->urlbase);
+ if(n1==0)
+ n1 = strlen(descURL);
+ n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
+ n2 = n1; n3 = n1; n4 = n1;
+ n1 += strlen(data->first.scpdurl);
+ n2 += strlen(data->first.controlurl);
+ n3 += strlen(data->CIF.controlurl);
+ n4 += strlen(data->IPv6FC.controlurl);
+
+ urls->ipcondescURL = (char *)malloc(n1);
+ urls->controlURL = (char *)malloc(n2);
+ urls->controlURL_CIF = (char *)malloc(n3);
+ urls->controlURL_6FC = (char *)malloc(n4);
+ /* maintenant on chope la desc du WANIPConnection */
+ if(data->urlbase[0] != '\0')
+ strncpy(urls->ipcondescURL, data->urlbase, n1);
+ else
+ strncpy(urls->ipcondescURL, descURL, n1);
+ p = strchr(urls->ipcondescURL+7, '/');
+ if(p) p[0] = '\0';
+ strncpy(urls->controlURL, urls->ipcondescURL, n2);
+ strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
+ strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
+
+ url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
+
+ url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
+
+ url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
+
+ url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
+
+#ifdef DEBUG
+ printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
+ (unsigned)strlen(urls->ipcondescURL), n1);
+ printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
+ (unsigned)strlen(urls->controlURL), n2);
+ printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
+ (unsigned)strlen(urls->controlURL_CIF), n3);
+ printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
+ (unsigned)strlen(urls->controlURL_6FC), n4);
+#endif
+}
+
+LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls * urls)
+{
+ if(!urls)
+ return;
+ free(urls->controlURL);
+ urls->controlURL = 0;
+ free(urls->ipcondescURL);
+ urls->ipcondescURL = 0;
+ free(urls->controlURL_CIF);
+ urls->controlURL_CIF = 0;
+ free(urls->controlURL_6FC);
+ urls->controlURL_6FC = 0;
+}
+
+int
+UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ char status[64];
+ unsigned int uptime;
+ status[0] = '\0';
+ UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, NULL);
+ if(0 == strcmp("Connected", status))
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ char * descXML;
+ int descXMLsize = 0;
+ struct UPNPDev * dev;
+ int ndev = 0;
+ int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
+ if(!devlist)
+ {
+#ifdef DEBUG
+ printf("Empty devlist\n");
+#endif
+ return 0;
+ }
+ for(state = 1; state <= 3; state++)
+ {
+ for(dev = devlist; dev; dev = dev->pNext)
+ {
+ /* we should choose an internet gateway device.
+ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
+ descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
+ lanaddr, lanaddrlen);
+ if(descXML)
+ {
+ ndev++;
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(descXML, descXMLsize, data);
+ free(descXML);
+ descXML = NULL;
+ if(0==strcmp(data->CIF.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
+ || state >= 3 )
+ {
+ GetUPNPUrls(urls, data, dev->descURL);
+
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL,
+ UPNPIGD_IsConnected(urls, data));
+#endif
+ if((state >= 2) || UPNPIGD_IsConnected(urls, data))
+ return state;
+ FreeUPNPUrls(urls);
+ if(data->second.servicetype[0] != '\0') {
+#ifdef DEBUG
+ printf("We tried %s, now we try %s !\n",
+ data->first.servicetype, data->second.servicetype);
+#endif
+ /* swaping WANPPPConnection and WANIPConnection ! */
+ memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
+ memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
+ memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
+ GetUPNPUrls(urls, data, dev->descURL);
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL,
+ UPNPIGD_IsConnected(urls, data));
+#endif
+ if((state >= 2) || UPNPIGD_IsConnected(urls, data))
+ return state;
+ FreeUPNPUrls(urls);
+ }
+ }
+ memset(data, 0, sizeof(struct IGDdatas));
+ }
+#ifdef DEBUG
+ else
+ {
+ printf("error getting XML description %s\n", dev->descURL);
+ }
+#endif
+ }
+ }
+ return 0;
+}
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ char * descXML;
+ int descXMLsize = 0;
+ descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
+ lanaddr, lanaddrlen);
+ if(descXML) {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(descXML, descXMLsize, data);
+ free(descXML);
+ descXML = NULL;
+ GetUPNPUrls(urls, data, rootdescurl);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h
new file mode 100644
index 0000000..50df017
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h
@@ -0,0 +1,121 @@
+/* $Id: miniupnpc.h,v 1.23 2011/04/11 08:21:46 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __MINIUPNPC_H__
+#define __MINIUPNPC_H__
+
+#include "declspec.h"
+#include "igd_desc_parse.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+ const char *, struct UPNParg *,
+ int *);
+
+struct UPNPDev {
+ struct UPNPDev * pNext;
+ char * descURL;
+ char * st;
+ char buffer[2];
+};
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If sameport is not null, SSDP packets will be sent from the source port
+ * 1900 (same as destination port) otherwise system assign a source port. */
+LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int sameport,
+ int ipv6,
+ int * error);
+/* freeUPNPDevlist()
+ * free list returned by upnpDiscover() */
+LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ * controlURL_6FC: controlURL of the WANIPv6FirewallControl
+ */
+struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+LIBSPEC void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);
+
+LIBSPEC void FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h
new file mode 100644
index 0000000..86081c3
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h
@@ -0,0 +1,19 @@
+/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef __MINIUPNPCTYPES_H__
+#define __MINIUPNPCTYPES_H__
+
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+#define UNSIGNED_INTEGER unsigned long long
+#define STRTOUI strtoull
+#else
+#define UNSIGNED_INTEGER unsigned int
+#define STRTOUI strtoul
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c
new file mode 100644
index 0000000..87f6155
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c
@@ -0,0 +1,522 @@
+/* $Id: miniwget.c,v 1.52 2011/06/17 22:59:42 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#define snprintf _snprintf
+#define socklen_t int
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#else /* #ifdef WIN32 */
+#include <unistd.h>
+#include <sys/param.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#define closesocket close
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#endif /* #else WIN32 */
+#if defined(__sun) || defined(sun)
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+#include "miniupnpcstrings.h"
+#include "miniwget.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+/*
+ * Read a HTTP response from a socket.
+ * Process Content-Length and Transfer-encoding headers.
+ * return a pointer to the content buffer, which length is saved
+ * to the length parameter.
+ */
+void *
+getHTTPResponse(int s, int * size)
+{
+ char buf[2048];
+ int n;
+ int endofheaders = 0;
+ int chunked = 0;
+ int content_length = -1;
+ unsigned int chunksize = 0;
+ unsigned int bytestocopy = 0;
+ /* buffers : */
+ char * header_buf;
+ int header_buf_len = 2048;
+ int header_buf_used = 0;
+ char * content_buf;
+ int content_buf_len = 2048;
+ int content_buf_used = 0;
+ char chunksize_buf[32];
+ int chunksize_buf_index;
+
+ header_buf = malloc(header_buf_len);
+ content_buf = malloc(content_buf_len);
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+
+ while((n = receivedata(s, buf, 2048, 5000)) > 0)
+ {
+ if(endofheaders == 0)
+ {
+ int i;
+ int linestart=0;
+ int colon=0;
+ int valuestart=0;
+ if(header_buf_used + n > header_buf_len) {
+ header_buf = realloc(header_buf, header_buf_used + n);
+ header_buf_len = header_buf_used + n;
+ }
+ memcpy(header_buf + header_buf_used, buf, n);
+ header_buf_used += n;
+ /* search for CR LF CR LF (end of headers)
+ * recognize also LF LF */
+ i = 0;
+ while(i < (header_buf_used-1) && (endofheaders == 0)) {
+ if(header_buf[i] == '\r') {
+ i++;
+ if(header_buf[i] == '\n') {
+ i++;
+ if(i < header_buf_used && header_buf[i] == '\r') {
+ i++;
+ if(i < header_buf_used && header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ }
+ } else if(header_buf[i] == '\n') {
+ i++;
+ if(header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ i++;
+ }
+ if(endofheaders == 0)
+ continue;
+ /* parse header lines */
+ for(i = 0; i < endofheaders - 1; i++) {
+ if(colon <= linestart && header_buf[i]==':')
+ {
+ colon = i;
+ while(i < (endofheaders-1)
+ && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
+ i++;
+ valuestart = i + 1;
+ }
+ /* detecting end of line */
+ else if(header_buf[i]=='\r' || header_buf[i]=='\n')
+ {
+ if(colon > linestart && valuestart > colon)
+ {
+#ifdef DEBUG
+ printf("header='%.*s', value='%.*s'\n",
+ colon-linestart, header_buf+linestart,
+ i-valuestart, header_buf+valuestart);
+#endif
+ if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
+ {
+ content_length = atoi(header_buf+valuestart);
+#ifdef DEBUG
+ printf("Content-Length: %d\n", content_length);
+#endif
+ }
+ else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
+ && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
+ {
+#ifdef DEBUG
+ printf("chunked transfer-encoding!\n");
+#endif
+ chunked = 1;
+ }
+ }
+ while(header_buf[i]=='\r' || header_buf[i] == '\n')
+ i++;
+ linestart = i;
+ colon = linestart;
+ valuestart = 0;
+ }
+ }
+ /* copy the remaining of the received data back to buf */
+ n = header_buf_used - endofheaders;
+ memcpy(buf, header_buf + endofheaders, n);
+ /* if(headers) */
+ }
+ if(endofheaders)
+ {
+ /* content */
+ if(chunked)
+ {
+ int i = 0;
+ while(i < n)
+ {
+ if(chunksize == 0)
+ {
+ /* reading chunk size */
+ if(chunksize_buf_index == 0) {
+ /* skipping any leading CR LF */
+ if(i<n && buf[i] == '\r') i++;
+ if(i<n && buf[i] == '\n') i++;
+ }
+ while(i<n && isxdigit(buf[i])
+ && chunksize_buf_index < (sizeof(chunksize_buf)-1))
+ {
+ chunksize_buf[chunksize_buf_index++] = buf[i];
+ chunksize_buf[chunksize_buf_index] = '\0';
+ i++;
+ }
+ while(i<n && buf[i] != '\r' && buf[i] != '\n')
+ i++; /* discarding chunk-extension */
+ if(i<n && buf[i] == '\r') i++;
+ if(i<n && buf[i] == '\n') {
+ int j;
+ for(j = 0; j < chunksize_buf_index; j++) {
+ if(chunksize_buf[j] >= '0'
+ && chunksize_buf[j] <= '9')
+ chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
+ else
+ chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+ i++;
+ } else {
+ /* not finished to get chunksize */
+ continue;
+ }
+#ifdef DEBUG
+ printf("chunksize = %u (%x)\n", chunksize, chunksize);
+#endif
+ if(chunksize == 0)
+ {
+#ifdef DEBUG
+ printf("end of HTTP content - %d %d\n", i, n);
+ /*printf("'%.*s'\n", n-i, buf+i);*/
+#endif
+ goto end_of_stream;
+ }
+ }
+ bytestocopy = ((int)chunksize < n - i)?chunksize:(n - i);
+ if((int)(content_buf_used + bytestocopy) > content_buf_len)
+ {
+ if(content_length >= content_buf_used + (int)bytestocopy) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + (int)bytestocopy;
+ }
+ content_buf = (char *)realloc((void *)content_buf,
+ content_buf_len);
+ }
+ memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
+ content_buf_used += bytestocopy;
+ i += bytestocopy;
+ chunksize -= bytestocopy;
+ }
+ }
+ else
+ {
+ /* not chunked */
+ if(content_length > 0
+ && (content_buf_used + n) > content_length) {
+ /* skipping additional bytes */
+ n = content_length - content_buf_used;
+ }
+ if(content_buf_used + n > content_buf_len)
+ {
+ if(content_length >= content_buf_used + n) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + n;
+ }
+ content_buf = (char *)realloc((void *)content_buf,
+ content_buf_len);
+ }
+ memcpy(content_buf + content_buf_used, buf, n);
+ content_buf_used += n;
+ }
+ }
+ /* use the Content-Length header value if available */
+ if(content_length > 0 && content_buf_used >= content_length)
+ {
+#ifdef DEBUG
+ printf("End of HTTP content\n");
+#endif
+ break;
+ }
+ }
+end_of_stream:
+ free(header_buf); header_buf = NULL;
+ *size = content_buf_used;
+ if(content_buf_used == 0)
+ {
+ free(content_buf);
+ content_buf = NULL;
+ }
+ return content_buf;
+}
+
+/* miniwget3() :
+ * do all the work.
+ * Return NULL if something failed. */
+static void *
+miniwget3(const char * url, const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ const char * httpversion)
+{
+ char buf[2048];
+ int s;
+ int n;
+ int len;
+ int sent;
+ void * content;
+
+ *size = 0;
+ s = connecthostport(host, port);
+ if(s < 0)
+ return NULL;
+
+ /* get address for caller ! */
+ if(addr_str)
+ {
+ struct sockaddr_storage saddr;
+ socklen_t saddrlen;
+
+ saddrlen = sizeof(saddr);
+ if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
+ {
+ perror("getsockname");
+ }
+ else
+ {
+#if defined(__amigaos__) && !defined(__amigaos4__)
+ /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
+ * But his function make a string with the port : nn.nn.nn.nn:port */
+/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
+ NULL, addr_str, (DWORD *)&addr_str_len))
+ {
+ printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
+ }*/
+ /* the following code is only compatible with ip v4 addresses */
+ strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
+#else
+#if 0
+ if(saddr.sa_family == AF_INET6) {
+ inet_ntop(AF_INET6,
+ &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
+ addr_str, addr_str_len);
+ } else {
+ inet_ntop(AF_INET,
+ &(((struct sockaddr_in *)&saddr)->sin_addr),
+ addr_str, addr_str_len);
+ }
+#endif
+ /* getnameinfo return ip v6 address with the scope identifier
+ * such as : 2a01:e35:8b2b:7330::%4281128194 */
+ n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
+ addr_str, addr_str_len,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if(n != 0) {
+#ifdef WIN32
+ fprintf(stderr, "getnameinfo() failed : %d\n", n);
+#else
+ fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
+#endif
+ }
+#endif
+ }
+#ifdef DEBUG
+ printf("address miniwget : %s\n", addr_str);
+#endif
+ }
+
+ len = snprintf(buf, sizeof(buf),
+ "GET %s HTTP/%s\r\n"
+ "Host: %s:%d\r\n"
+ "Connection: Close\r\n"
+ "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+
+ "\r\n",
+ path, httpversion, host, port);
+ sent = 0;
+ /* sending the HTTP request */
+ while(sent < len)
+ {
+ n = send(s, buf+sent, len-sent, 0);
+ if(n < 0)
+ {
+ perror("send");
+ closesocket(s);
+ return NULL;
+ }
+ else
+ {
+ sent += n;
+ }
+ }
+ content = getHTTPResponse(s, size);
+ closesocket(s);
+ return content;
+}
+
+/* miniwget2() :
+ * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
+static void *
+miniwget2(const char * url, const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len)
+{
+ char * respbuffer;
+
+ respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
+/*
+ respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.0");
+ if (*size == 0)
+ {
+#ifdef DEBUG
+ printf("Retrying with HTTP/1.1\n");
+#endif
+ free(respbuffer);
+ respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
+ }
+*/
+ return respbuffer;
+}
+
+
+
+
+/* parseURL()
+ * arguments :
+ * url : source string not modified
+ * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
+ * port : port (destination)
+ * path : pointer to the path part of the URL
+ *
+ * Return values :
+ * 0 - Failure
+ * 1 - Success */
+int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
+{
+ char * p1, *p2, *p3;
+ if(!url)
+ return 0;
+ p1 = strstr(url, "://");
+ if(!p1)
+ return 0;
+ p1 += 3;
+ if( (url[0]!='h') || (url[1]!='t')
+ ||(url[2]!='t') || (url[3]!='p'))
+ return 0;
+ memset(hostname, 0, MAXHOSTNAMELEN + 1);
+ if(*p1 == '[')
+ {
+ /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
+ p2 = strchr(p1, ']');
+ p3 = strchr(p1, '/');
+ if(p2 && p3)
+ {
+ p2++;
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ if(*p2 == ':')
+ {
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ else
+ {
+ *port = 80;
+ }
+ *path = p3;
+ return 1;
+ }
+ }
+ p2 = strchr(p1, ':');
+ p3 = strchr(p1, '/');
+ if(!p3)
+ return 0;
+ if(!p2 || (p2>p3))
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
+ *port = 80;
+ }
+ else
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ *path = p3;
+ return 1;
+}
+
+void * miniwget(const char * url, int * size)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/chemin */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(!parseURL(url, hostname, &port, &path))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path);
+#endif
+ return miniwget2(url, hostname, port, path, size, 0, 0);
+}
+
+void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/chemin */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(addr)
+ addr[0] = '\0';
+ if(!parseURL(url, hostname, &port, &path))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path);
+#endif
+ return miniwget2(url, hostname, port, path, size, addr, addrlen);
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h
new file mode 100644
index 0000000..8314b40
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h
@@ -0,0 +1,30 @@
+/* $Id: miniwget.h,v 1.6 2010/12/09 16:11:33 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __MINIWGET_H__
+#define __MINIWGET_H__
+
+#include "declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBSPEC void * getHTTPResponse(int s, int * size);
+
+LIBSPEC void * miniwget(const char *, int *);
+
+LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int);
+
+int parseURL(const char *, char *, unsigned short *, char * *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c
new file mode 100644
index 0000000..8b5594c
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c
@@ -0,0 +1,216 @@
+/* $Id: minixml.c,v 1.9 2011/02/07 13:44:57 nanard Exp $ */
+/* minixml.c : the minimum size a xml parser can be ! */
+/* Project : miniupnp
+ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+
+Copyright (c) 2005-2011, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <string.h>
+#include "minixml.h"
+
+/* parseatt : used to parse the argument list
+ * return 0 (false) in case of success and -1 (true) if the end
+ * of the xmlbuffer is reached. */
+static int parseatt(struct xmlparser * p)
+{
+ const char * attname;
+ int attnamelen;
+ const char * attvalue;
+ int attvaluelen;
+ while(p->xml < p->xmlend)
+ {
+ if(*p->xml=='/' || *p->xml=='>')
+ return 0;
+ if( !IS_WHITE_SPACE(*p->xml) )
+ {
+ char sep;
+ attname = p->xml;
+ attnamelen = 0;
+ while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
+ {
+ attnamelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(*(p->xml++) != '=')
+ {
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(IS_WHITE_SPACE(*p->xml))
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ sep = *p->xml;
+ if(sep=='\'' || sep=='\"')
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while(*p->xml != sep)
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ else
+ {
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && *p->xml != '>' && *p->xml != '/')
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ /*printf("%.*s='%.*s'\n",
+ attnamelen, attname, attvaluelen, attvalue);*/
+ if(p->attfunc)
+ p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
+ }
+ p->xml++;
+ }
+ return -1;
+}
+
+/* parseelt parse the xml stream and
+ * call the callback functions when needed... */
+static void parseelt(struct xmlparser * p)
+{
+ int i;
+ const char * elementname;
+ while(p->xml < (p->xmlend - 1))
+ {
+ if((p->xml)[0]=='<' && (p->xml)[1]!='?')
+ {
+ i = 0; elementname = ++p->xml;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && (*p->xml!='>') && (*p->xml!='/')
+ )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ /* to ignore namespace : */
+ if(*p->xml==':')
+ {
+ i = 0;
+ elementname = ++p->xml;
+ }
+ }
+ if(i>0)
+ {
+ if(p->starteltfunc)
+ p->starteltfunc(p->data, elementname, i);
+ if(parseatt(p))
+ return;
+ if(*p->xml!='/')
+ {
+ const char * data;
+ i = 0; data = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while( IS_WHITE_SPACE(*p->xml) )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ if(memcmp(p->xml, "<![CDATA[", 9) == 0)
+ {
+ /* CDATA handling */
+ p->xml += 9;
+ data = p->xml;
+ i = 0;
+ while(memcmp(p->xml, "]]>", 3) != 0)
+ {
+ i++; p->xml++;
+ if ((p->xml + 3) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc)
+ p->datafunc(p->data, data, i);
+ while(*p->xml!='<')
+ {
+ p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ }
+ else
+ {
+ while(*p->xml!='<')
+ {
+ i++; p->xml++;
+ if ((p->xml + 1) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc && *(p->xml + 1) == '/')
+ p->datafunc(p->data, data, i);
+ }
+ }
+ }
+ else if(*p->xml == '/')
+ {
+ i = 0; elementname = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while((*p->xml != '>'))
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ if(p->endeltfunc)
+ p->endeltfunc(p->data, elementname, i);
+ p->xml++;
+ }
+ }
+ else
+ {
+ p->xml++;
+ }
+ }
+}
+
+/* the parser must be initialized before calling this function */
+void parsexml(struct xmlparser * parser)
+{
+ parser->xml = parser->xmlstart;
+ parser->xmlend = parser->xmlstart + parser->xmlsize;
+ parseelt(parser);
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h
new file mode 100644
index 0000000..857c70e
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h
@@ -0,0 +1,37 @@
+/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
+/* minimal xml parser
+ *
+ * Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __MINIXML_H__
+#define __MINIXML_H__
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/* if a callback function pointer is set to NULL,
+ * the function is not called */
+struct xmlparser {
+ const char *xmlstart;
+ const char *xmlend;
+ const char *xml; /* pointer to current character */
+ int xmlsize;
+ void * data;
+ void (*starteltfunc) (void *, const char *, int);
+ void (*endeltfunc) (void *, const char *, int);
+ void (*datafunc) (void *, const char *, int);
+ void (*attfunc) (void *, const char *, int, const char *, int);
+};
+
+/* parsexml()
+ * the xmlparser structure must be initialized before the call
+ * the following structure members have to be initialized :
+ * xmlstart, xmlsize, data, *func
+ * xml is for internal usage, xmlend is computed automatically */
+void parsexml(struct xmlparser *);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c
new file mode 100644
index 0000000..766211b
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c
@@ -0,0 +1,156 @@
+/* $Id: minixmlvalid.c,v 1.4 2011/02/07 13:44:57 nanard Exp $ */
+/* MiniUPnP Project
+ * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+ * minixmlvalid.c :
+ * validation program for the minixml parser
+ *
+ * (c) 2006-2011 Thomas Bernard */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "minixml.h"
+
+/* xml event structure */
+struct event {
+ enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
+ const char * data;
+ int len;
+};
+
+struct eventlist {
+ int n;
+ struct event * events;
+};
+
+/* compare 2 xml event lists
+ * return 0 if the two lists are equals */
+int evtlistcmp(struct eventlist * a, struct eventlist * b)
+{
+ int i;
+ struct event * ae, * be;
+ if(a->n != b->n)
+ {
+ printf("event number not matching : %d != %d\n", a->n, b->n);
+ //return 1;
+ }
+ for(i=0; i<a->n; i++)
+ {
+ ae = a->events + i;
+ be = b->events + i;
+ if( (ae->type != be->type)
+ ||(ae->len != be->len)
+ ||memcmp(ae->data, be->data, ae->len))
+ {
+ printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
+ ae->type, ae->len, ae->data,
+ be->type, be->len, be->data);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Test data */
+static const char xmldata[] =
+"<xmlroot>\n"
+" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
+"character data"
+"</elt1> \n \t"
+"<elt1b/>"
+"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
+"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
+"</xmlroot>";
+
+static const struct event evtref[] =
+{
+ {ELTSTART, "xmlroot", 7},
+ {ELTSTART, "elt1", 4},
+ /* attributes */
+ {CHARDATA, "character data", 14},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt1b", 5},
+ {ELTSTART, "elt1", 4},
+ {CHARDATA, " <html>stuff !\n ", 16},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt2a", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, "chardata1", 9},
+ {ELTEND, "elt2b", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, " chardata2 ", 11},
+ {ELTEND, "elt2b", 5},
+ {ELTEND, "elt2a", 5},
+ {ELTEND, "xmlroot", 7}
+};
+
+void startelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("startelt : %.*s\n", l, p);*/
+ evt->type = ELTSTART;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void endelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("endelt : %.*s\n", l, p);*/
+ evt->type = ELTEND;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void chardata(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("chardata : '%.*s'\n", l, p);*/
+ evt->type = CHARDATA;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+int testxmlparser(const char * xml, int size)
+{
+ int r;
+ struct eventlist evtlist;
+ struct eventlist evtlistref;
+ struct xmlparser parser;
+ evtlist.n = 0;
+ evtlist.events = malloc(sizeof(struct event)*100);
+ memset(&parser, 0, sizeof(parser));
+ parser.xmlstart = xml;
+ parser.xmlsize = size;
+ parser.data = &evtlist;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = chardata;
+ parsexml(&parser);
+ printf("%d events\n", evtlist.n);
+ /* compare */
+ evtlistref.n = sizeof(evtref)/sizeof(struct event);
+ evtlistref.events = (struct event *)evtref;
+ r = evtlistcmp(&evtlistref, &evtlist);
+ free(evtlist.events);
+ return r;
+}
+
+int main(int argc, char * * argv)
+{
+ int r;
+ r = testxmlparser(xmldata, sizeof(xmldata)-1);
+ if(r)
+ printf("minixml validation test failed\n");
+ return r;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c
new file mode 100644
index 0000000..e09e80f
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c
@@ -0,0 +1,157 @@
+/* $Id: portlistingparse.c,v 1.4 2011/03/18 11:02:17 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#include <string.h>
+#include <stdlib.h>
+#include "portlistingparse.h"
+#include "minixml.h"
+
+/* list of the elements */
+static const struct {
+ const portMappingElt code;
+ const char * const str;
+} elements[] = {
+ { PortMappingEntry, "PortMappingEntry"},
+ { NewRemoteHost, "NewRemoteHost"},
+ { NewExternalPort, "NewExternalPort"},
+ { NewProtocol, "NewProtocol"},
+ { NewInternalPort, "NewInternalPort"},
+ { NewInternalClient, "NewInternalClient"},
+ { NewEnabled, "NewEnabled"},
+ { NewDescription, "NewDescription"},
+ { NewLeaseTime, "NewLeaseTime"},
+ { PortMappingEltNone, NULL}
+};
+
+/* Helper function */
+static UNSIGNED_INTEGER
+atoui(const char * p, int l)
+{
+ UNSIGNED_INTEGER r = 0;
+ while(l > 0 && *p)
+ {
+ if(*p >= '0' && *p <= '9')
+ r = r*10 + (*p - '0');
+ else
+ break;
+ p++;
+ l--;
+ }
+ return r;
+}
+
+/* Start element handler */
+static void
+startelt(void * d, const char * name, int l)
+{
+ int i;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pdata->curelt = PortMappingEltNone;
+ for(i = 0; elements[i].str; i++)
+ {
+ if(memcmp(name, elements[i].str, l) == 0)
+ {
+ pdata->curelt = elements[i].code;
+ break;
+ }
+ }
+ if(pdata->curelt == PortMappingEntry)
+ {
+ struct PortMapping * pm;
+ pm = calloc(1, sizeof(struct PortMapping));
+ LIST_INSERT_HEAD( &(pdata->head), pm, entries);
+ }
+}
+
+/* End element handler */
+static void
+endelt(void * d, const char * name, int l)
+{
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pdata->curelt = PortMappingEltNone;
+}
+
+/* Data handler */
+static void
+data(void * d, const char * data, int l)
+{
+ struct PortMapping * pm;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pm = pdata->head.lh_first;
+ if(!pm)
+ return;
+ if(l > 63)
+ l = 63;
+ switch(pdata->curelt)
+ {
+ case NewRemoteHost:
+ memcpy(pm->remoteHost, data, l);
+ pm->remoteHost[l] = '\0';
+ break;
+ case NewExternalPort:
+ pm->externalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewProtocol:
+ if(l > 3)
+ l = 3;
+ memcpy(pm->protocol, data, l);
+ pm->protocol[l] = '\0';
+ break;
+ case NewInternalPort:
+ pm->internalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewInternalClient:
+ memcpy(pm->internalClient, data, l);
+ pm->internalClient[l] = '\0';
+ break;
+ case NewEnabled:
+ pm->enabled = (unsigned char)atoui(data, l);
+ break;
+ case NewDescription:
+ memcpy(pm->description, data, l);
+ pm->description[l] = '\0';
+ break;
+ case NewLeaseTime:
+ pm->leaseTime = atoui(data, l);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* Parse the PortMappingList XML document for IGD version 2
+ */
+void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata)
+{
+ struct xmlparser parser;
+
+ memset(pdata, 0, sizeof(struct PortMappingParserData));
+ LIST_INIT(&(pdata->head));
+ /* init xmlparser */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = pdata;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = data;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+FreePortListing(struct PortMappingParserData * pdata)
+{
+ struct PortMapping * pm;
+ while((pm = pdata->head.lh_first) != NULL)
+ {
+ LIST_REMOVE(pm, entries);
+ free(pm);
+ }
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h
new file mode 100644
index 0000000..1852478
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h
@@ -0,0 +1,71 @@
+/* $Id: portlistingparse.h,v 1.4 2011/02/15 23:03:56 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef __PORTLISTINGPARSE_H__
+#define __PORTLISTINGPARSE_H__
+
+#include "declspec.h"
+/* for the definition of UNSIGNED_INTEGER */
+#include "miniupnpctypes.h"
+
+#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__)
+#include "bsdqueue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sample of PortMappingEntry :
+ <p:PortMappingEntry>
+ <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
+ <p:NewExternalPort>2345</p:NewExternalPort>
+ <p:NewProtocol>TCP</p:NewProtocol>
+ <p:NewInternalPort>2345</p:NewInternalPort>
+ <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
+ <p:NewEnabled>1</p:NewEnabled>
+ <p:NewDescription>dooom</p:NewDescription>
+ <p:NewLeaseTime>345</p:NewLeaseTime>
+ </p:PortMappingEntry>
+ */
+typedef enum { PortMappingEltNone,
+ PortMappingEntry, NewRemoteHost,
+ NewExternalPort, NewProtocol,
+ NewInternalPort, NewInternalClient,
+ NewEnabled, NewDescription,
+ NewLeaseTime } portMappingElt;
+
+struct PortMapping {
+ LIST_ENTRY(PortMapping) entries;
+ UNSIGNED_INTEGER leaseTime;
+ unsigned short externalPort;
+ unsigned short internalPort;
+ char remoteHost[64];
+ char internalClient[64];
+ char description[64];
+ char protocol[4];
+ unsigned char enabled;
+};
+
+struct PortMappingParserData {
+ LIST_HEAD(portmappinglisthead, PortMapping) head;
+ portMappingElt curelt;
+};
+
+LIBSPEC void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata);
+
+LIBSPEC void
+FreePortListing(struct PortMappingParserData * pdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c
new file mode 100644
index 0000000..a1eadfc
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c
@@ -0,0 +1,81 @@
+/* $Id: receivedata.c,v 1.1 2011/04/11 08:21:47 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif
+#include <errno.h>
+#define MINIUPNPC_IGNORE_EINTR
+#endif
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#include "receivedata.h"
+
+int
+receivedata(int socket, char * data, int length, int timeout)
+{
+ int n;
+#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* using poll */
+ struct pollfd fds[1]; /* for the poll */
+#ifdef MINIUPNPC_IGNORE_EINTR
+ do {
+#endif
+ fds[0].fd = socket;
+ fds[0].events = POLLIN;
+ n = poll(fds, 1, timeout);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ } while(n < 0 && errno == EINTR);
+#endif
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("poll");
+ return -1;
+ } else if(n == 0) {
+ /* timeout */
+ return 0;
+ }
+#else /* !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+ /* using select under WIN32 and amigaos */
+ fd_set socketSet;
+ TIMEVAL timeval;
+ FD_ZERO(&socketSet);
+ FD_SET(socket, &socketSet);
+ timeval.tv_sec = timeout / 1000;
+ timeval.tv_usec = (timeout % 1000) * 1000;
+ n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("select");
+ return -1;
+ } else if(n == 0) {
+ return 0;
+ }
+#endif
+ n = recv(socket, data, length, 0);
+ if(n<0) {
+ PRINT_SOCKET_ERROR("recv");
+ }
+ return n;
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h
new file mode 100644
index 0000000..7a551b9
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h
@@ -0,0 +1,17 @@
+/* $Id: receivedata.h,v 1.1 2011/04/11 08:21:47 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __RECEIVEDATA_H__
+#define __RECEIVEDATA_H__
+
+/* Reads data from the specified socket.
+ * Returns the number of bytes read if successful, zero if no bytes were
+ * read or if we timed out. Returns negative if there was an error. */
+int receivedata(int socket, char * data, int length, int timeout);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c
new file mode 100644
index 0000000..b136d9d
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c
@@ -0,0 +1,683 @@
+/* $Id: upnpc.c,v 1.88 2011/06/17 23:31:01 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef WIN32
+#include <winsock2.h>
+#define snprintf _snprintf
+#endif
+#include "miniwget.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "upnperrors.h"
+
+/* protofix() checks if protocol is "UDP" or "TCP"
+ * returns NULL if not */
+const char * protofix(const char * proto)
+{
+ static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
+ static const char proto_udp[4] = { 'U', 'D', 'P', 0};
+ int i, b;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_tcp[i])
+ || (proto[i] == (proto_tcp[i] | 32)) );
+ if(b)
+ return proto_tcp;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_udp[i])
+ || (proto[i] == (proto_udp[i] | 32)) );
+ if(b)
+ return proto_udp;
+ return 0;
+}
+
+static void DisplayInfos(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ char externalIPAddress[40];
+ char connectionType[64];
+ char status[64];
+ char lastconnerr[64];
+ unsigned int uptime;
+ unsigned int brUp, brDown;
+ time_t timenow, timestarted;
+ int r;
+ UPNP_GetConnectionTypeInfo(urls->controlURL,
+ data->first.servicetype,
+ connectionType);
+ if(connectionType[0])
+ printf("Connection Type : %s\n", connectionType);
+ else
+ printf("GetConnectionTypeInfo failed.\n");
+ UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, lastconnerr);
+ printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
+ status, uptime, lastconnerr);
+ timenow = time(NULL);
+ timestarted = timenow - uptime;
+ printf(" Time started : %s", ctime(&timestarted));
+ UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
+ &brDown, &brUp);
+ printf("MaxBitRateDown : %u bps", brDown);
+ if(brDown >= 1000000) {
+ printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
+ } else if(brDown >= 1000) {
+ printf(" (%u Kbps)", brDown / 1000);
+ }
+ printf(" MaxBitRateUp %u bps", brUp);
+ if(brUp >= 1000000) {
+ printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
+ } else if(brUp >= 1000) {
+ printf(" (%u Kbps)", brUp / 1000);
+ }
+ printf("\n");
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r != UPNPCOMMAND_SUCCESS)
+ printf("GetExternalIPAddress() returned %d\n", r);
+ if(externalIPAddress[0])
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+ else
+ printf("GetExternalIPAddress failed.\n");
+}
+
+static void GetConnectionStatus(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ DisplayInfos(urls, data);
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+static void ListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ char index[6];
+ char intClient[40];
+ char intPort[6];
+ char extPort[6];
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16];
+ /*unsigned int num=0;
+ UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
+ printf("PortMappingNumberOfEntries : %u\n", num);*/
+ do {
+ snprintf(index, 6, "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled,
+ rHost, duration);
+ if(r==0)
+ /*
+ printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
+ " desc='%s' rHost='%s'\n",
+ i, protocol, extPort, intClient, intPort,
+ enabled, duration,
+ desc, rHost);
+ */
+ printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
+ i, protocol, extPort, intClient, intPort,
+ desc, rHost, duration);
+ else
+ printf("GetGenericPortMappingEntry() returned %d (%s)\n",
+ r, strupnperror(r));
+ i++;
+ } while(r==0);
+}
+
+static void NewListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ struct PortMappingParserData pdata;
+ struct PortMapping * pm;
+
+ memset(&pdata, 0, sizeof(struct PortMappingParserData));
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "TCP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "UDP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * 1 - get connection type
+ * 2 - get extenal ip address
+ * 3 - Add port mapping
+ * 4 - get this port mapping from the IGD */
+static void SetRedirectAndTest(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * iaddr,
+ const char * iport,
+ const char * eport,
+ const char * proto,
+ const char * leaseDuration)
+{
+ char externalIPAddress[40];
+ char intClient[40];
+ char intPort[6];
+ char duration[16];
+ int r;
+
+ if(!iaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return;
+ }
+
+ UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(externalIPAddress[0])
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+ else
+ printf("GetExternalIPAddress failed.\n");
+
+ r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, 0, proto, 0, leaseDuration);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+
+ r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ eport, proto,
+ intClient, intPort, NULL/*desc*/,
+ NULL/*enabled*/, duration);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
+ r, strupnperror(r));
+
+ if(intClient[0]) {
+ printf("InternalIP:Port = %s:%s\n", intClient, intPort);
+ printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
+ externalIPAddress, eport, proto, intClient, intPort, duration);
+ }
+}
+
+static void
+RemoveRedirect(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * eport,
+ const char * proto)
+{
+ int r;
+ if(!proto || !eport)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return;
+ }
+ r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, 0);
+ printf("UPNP_DeletePortMapping() returned : %d\n", r);
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ int firewallEnabled = 0, inboundPinholeAllowed = 0;
+
+ UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
+ printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
+ printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
+
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+/* Test function
+ * 1 - Add pinhole
+ * 2 - Check if pinhole is working from the IGD side */
+static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto, const char * lease_time)
+{
+ char uniqueID[8];
+ //int isWorking = 0;
+ int r;
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ /*proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return;
+ }*/
+ r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+ else
+ {
+ printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n", intaddr, iport, remoteaddr, eport, uniqueID);
+ /*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
+ }
+}
+
+/* Test function
+ * 1 - Check if pinhole is working from the IGD side
+ * 2 - Update pinhole */
+static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * uniqueID, const char * lease_time)
+{
+ int isWorking = 0;
+ int r;
+
+ if(!uniqueID || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ if(isWorking || r==709)
+ {
+ r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
+ printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * Get pinhole timeout
+ */
+static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto)
+{
+ int timeout = 0;
+ int r;
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+
+ r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+ else
+ printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
+}
+
+static void
+GetPinholePackets(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, pinholePackets = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
+}
+
+static void
+CheckPinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, isWorking = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+}
+
+static void
+RemovePinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
+ printf("UPNP_DeletePinhole() returned : %d\n", r);
+}
+
+
+/* sample upnp client program */
+int main(int argc, char ** argv)
+{
+ char command = 0;
+ char ** commandargv = 0;
+ int commandargc = 0;
+ struct UPNPDev * devlist = 0;
+ char lanaddr[64]; /* my ip address on the LAN */
+ int i;
+ const char * rootdescurl = 0;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int retcode = 0;
+ int error = 0;
+ int ipv6 = 0;
+
+#ifdef WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+ printf("upnpc : miniupnpc library test client. (c) 2006-2011 Thomas Bernard\n");
+ printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
+ "for more information.\n");
+ /* command line processing */
+ for(i=1; i<argc; i++)
+ {
+ if(argv[i][0] == '-')
+ {
+ if(argv[i][1] == 'u')
+ rootdescurl = argv[++i];
+ else if(argv[i][1] == 'm')
+ multicastif = argv[++i];
+ else if(argv[i][1] == 'p')
+ minissdpdpath = argv[++i];
+ else if(argv[i][1] == '6')
+ ipv6 = 1;
+ else
+ {
+ command = argv[i][1];
+ i++;
+ commandargv = argv + i;
+ commandargc = argc - i;
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "option '%s' invalid\n", argv[i]);
+ }
+ }
+
+ if(!command || (command == 'a' && commandargc<4)
+ || (command == 'd' && argc<2)
+ || (command == 'r' && argc<2)
+ || (command == 'A' && commandargc<6)
+ || (command == 'U' && commandargc<2)
+ || (command == 'D' && commandargc<1))
+ {
+ fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -d external_port protocol [port2 protocol2] [...]\n\t\tDelete port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings, IGD v2)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -r port1 protocol1 [port2 protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
+ fprintf(stderr, "\nprotocol is UDP or TCP\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -6 : use ip v6 instead of ip v4.\n");
+ fprintf(stderr, " -u url : bypass discovery process by providing the XML root description url.\n");
+ fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v6) to use for sending SSDP multicast packets.\n");
+ fprintf(stderr, " -p path : use this path for MiniSSDPd socket.\n");
+ return 1;
+ }
+
+ if( rootdescurl
+ || (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
+ 0/*sameport*/, ipv6, &error)))
+ {
+ struct UPNPDev * device;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ if(devlist)
+ {
+ printf("List of UPNP devices found on the network :\n");
+ for(device = devlist; device; device = device->pNext)
+ {
+ printf(" desc: %s\n st: %s\n\n",
+ device->descURL, device->st);
+ }
+ }
+ else
+ {
+ printf("upnpDiscover() error code=%d\n", error);
+ }
+ i = 1;
+ if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
+ || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
+ {
+ switch(i) {
+ case 1:
+ printf("Found valid IGD : %s\n", urls.controlURL);
+ break;
+ case 2:
+ printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ case 3:
+ printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ default:
+ printf("Found device (igd ?) : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ }
+ printf("Local LAN ip address : %s\n", lanaddr);
+ #if 0
+ printf("getting \"%s\"\n", urls.ipcondescURL);
+ descXML = miniwget(urls.ipcondescURL, &descXMLsize);
+ if(descXML)
+ {
+ /*fwrite(descXML, 1, descXMLsize, stdout);*/
+ free(descXML); descXML = NULL;
+ }
+ #endif
+
+ switch(command)
+ {
+ case 'l':
+ DisplayInfos(&urls, &data);
+ ListRedirections(&urls, &data);
+ break;
+ case 'L':
+ NewListRedirections(&urls, &data);
+ break;
+ case 'a':
+ SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0");
+ break;
+ case 'd':
+ for(i=0; i<commandargc; i+=2)
+ {
+ RemoveRedirect(&urls, &data, commandargv[i], commandargv[i+1]);
+ }
+ break;
+ case 's':
+ GetConnectionStatus(&urls, &data);
+ break;
+ case 'r':
+ for(i=0; i<commandargc; i+=2)
+ {
+ /*printf("port %s protocol %s\n", argv[i], argv[i+1]);*/
+ SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i], commandargv[i+1], "0");
+ }
+ break;
+ case 'A':
+ SetPinholeAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4], commandargv[5]);
+ break;
+ case 'U':
+ GetPinholeAndUpdate(&urls, &data,
+ commandargv[0], commandargv[1]);
+ break;
+ case 'C':
+ for(i=0; i<commandargc; i++)
+ {
+ CheckPinhole(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'K':
+ for(i=0; i<commandargc; i++)
+ {
+ GetPinholePackets(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'D':
+ for(i=0; i<commandargc; i++)
+ {
+ RemovePinhole(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'S':
+ GetFirewallStatus(&urls, &data);
+ break;
+ case 'G':
+ GetPinholeOutboundTimeout(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4]);
+ break;
+ case 'P':
+ printf("Presentation URL found:\n");
+ printf(" %s\n", data.presentationurl);
+ break;
+ default:
+ fprintf(stderr, "Unknown switch -%c\n", command);
+ retcode = 1;
+ }
+
+ FreeUPNPUrls(&urls);
+ }
+ else
+ {
+ fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
+ retcode = 1;
+ }
+ freeUPNPDevlist(devlist); devlist = 0;
+ }
+ else
+ {
+ fprintf(stderr, "No IGD UPnP Device found on the network !\n");
+ retcode = 1;
+ }
+ return retcode;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c
new file mode 100644
index 0000000..1114759
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c
@@ -0,0 +1,1097 @@
+/* $Id: upnpcommands.c,v 1.37 2011/06/04 15:56:23 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+#include "portlistingparse.h"
+
+static UNSIGNED_INTEGER
+my_atoui(const char * s)
+{
+ return s ? ((UNSIGNED_INTEGER)STRTOUI(s, NULL, 0)) : 0;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/* UPNP_GetStatusInfo() call the corresponding UPNP method
+ * returns the current status and uptime */
+LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ char * up;
+ char * err;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!status && !uptime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetStatusInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ up = GetValueFromNameValueList(&pdata, "NewUptime");
+ p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
+ err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
+ if(p && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(status) {
+ if(p){
+ strncpy(status, p, 64 );
+ status[63] = '\0';
+ }else
+ status[0]= '\0';
+ }
+
+ if(uptime) {
+ if(up)
+ sscanf(up,"%u",uptime);
+ else
+ uptime = 0;
+ }
+
+ if(lastconnerror) {
+ if(err) {
+ strncpy(lastconnerror, err, 64 );
+ lastconnerror[63] = '\0';
+ } else
+ lastconnerror[0] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
+ * returns the connection type */
+LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!connectionType)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetConnectionTypeInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewConnectionType");
+ /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
+ /* PossibleConnectionTypes will have several values.... */
+ if(p) {
+ strncpy(connectionType, p, 64 );
+ connectionType[63] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ connectionType[0] = '\0';
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
+ * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
+ * One of the values can be null
+ * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
+ * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
+LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
+ const char * servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ char * down;
+ char * up;
+ char * p;
+
+ if(!bitrateDown && !bitrateUp)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ /* shouldn't we use GetCommonLinkProperties ? */
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetCommonLinkProperties", 0, &bufsize))) {
+ /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
+ /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
+ down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
+ up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
+ /*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
+ /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkStatus");*/
+ if(down && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(bitrateDown) {
+ if(down)
+ sscanf(down,"%u",bitrateDown);
+ else
+ *bitrateDown = 0;
+ }
+
+ if(bitrateUp) {
+ if(up)
+ sscanf(up,"%u",bitrateUp);
+ else
+ *bitrateUp = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!extIpAdd || !controlURL || !servicetype)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetExternalIPAddress", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
+ p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
+ if(p) {
+ strncpy(extIpAdd, p, 16 );
+ extIpAdd[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ extIpAdd[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize))) {
+ free(AddPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ /*buffer[bufsize] = '\0';*/
+ /*puts(buffer);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(AddPortMappingArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ DeletePortMappingArgs[0].elt = "NewRemoteHost";
+ DeletePortMappingArgs[0].val = remoteHost;
+ DeletePortMappingArgs[1].elt = "NewExternalPort";
+ DeletePortMappingArgs[1].val = extPort;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize))) {
+ free(DeletePortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(DeletePortMappingArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int r = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!index)
+ return UPNPCOMMAND_INVALID_ARGS;
+ intClient[0] = '\0';
+ intPort[0] = '\0';
+ GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
+ GetPortMappingArgs[0].elt = "NewPortMappingIndex";
+ GetPortMappingArgs[0].val = index;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize))) {
+ free(GetPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
+ if(p && rHost)
+ {
+ strncpy(rHost, p, 64);
+ rHost[63] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewExternalPort");
+ if(p && extPort)
+ {
+ strncpy(extPort, p, 6);
+ extPort[5] = '\0';
+ r = UPNPCOMMAND_SUCCESS;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewProtocol");
+ if(p && protocol)
+ {
+ strncpy(protocol, p, 4);
+ protocol[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p && intClient)
+ {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ r = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p && intPort)
+ {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled)
+ {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc)
+ {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && duration)
+ {
+ strncpy(duration, p, 16);
+ duration[15] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ r = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &r);
+ }
+ ClearNameValueList(&pdata);
+ free(GetPortMappingArgs);
+ return r;
+}
+
+LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
+ const char * servicetype,
+ unsigned int * numEntries)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char* p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPortMappingNumberOfEntries", 0,
+ &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+#ifdef DEBUG
+ DisplayNameValueList(buffer, bufsize);
+#endif
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
+ if(numEntries && p) {
+ *numEntries = 0;
+ sscanf(p, "%u", numEntries);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data */
+LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!intPort || !intClient || !extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ GetPortMappingArgs[0].elt = "NewRemoteHost";
+ /* TODO : add remote host ? */
+ GetPortMappingArgs[1].elt = "NewExternalPort";
+ GetPortMappingArgs[1].val = extPort;
+ GetPortMappingArgs[2].elt = "NewProtocol";
+ GetPortMappingArgs[2].val = proto;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize))) {
+ free(GetPortMappingArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p) {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ intClient[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p) {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ } else
+ intPort[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled) {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc) {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && leaseDuration)
+ {
+ strncpy(leaseDuration, p, 16);
+ leaseDuration[15] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ free(GetPortMappingArgs);
+ return ret;
+}
+
+/* UPNP_GetListOfPortMappings()
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetListOfPortMappingsArgs;
+ const char * p;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!startPort || !endPort || !protocol)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg));
+ GetListOfPortMappingsArgs[0].elt = "NewStartPort";
+ GetListOfPortMappingsArgs[0].val = startPort;
+ GetListOfPortMappingsArgs[1].elt = "NewEndPort";
+ GetListOfPortMappingsArgs[1].val = endPort;
+ GetListOfPortMappingsArgs[2].elt = "NewProtocol";
+ GetListOfPortMappingsArgs[2].val = protocol;
+ GetListOfPortMappingsArgs[3].elt = "NewManage";
+ GetListOfPortMappingsArgs[3].val = "1";
+ GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
+ GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize))) {
+ free(GetListOfPortMappingsArgs);
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ free(GetListOfPortMappingsArgs);
+
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/
+ /*if(p) {
+ printf("NewPortListing : %s\n", p);
+ }*/
+ /*printf("NewPortListing(%d chars) : %s\n",
+ pdata.portListingLength, pdata.portListing);*/
+ if(pdata.portListing)
+ {
+ /*struct PortMapping * pm;
+ int i = 0;*/
+ ParsePortListing(pdata.portListing, pdata.portListingLength,
+ data);
+ ret = UPNPCOMMAND_SUCCESS;
+ /*
+ for(pm = data->head.lh_first; pm != NULL; pm = pm->entries.le_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s'\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost);
+ i++;
+ }
+ */
+ /*FreePortListing(&data);*/
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+
+ //printf("%.*s", bufsize, buffer);
+
+ return ret;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * fe, *ipa, *p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!firewallEnabled && !inboundPinholeAllowed)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetFirewallStatus", 0, &bufsize);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ fe = GetValueFromNameValueList(&pdata, "FirewallEnabled");
+ ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed");
+ if(ipa && fe)
+ ret = UPNPCOMMAND_SUCCESS;
+ if(fe)
+ *firewallEnabled = my_atoui(fe);
+ /*else
+ *firewallEnabled = 0;*/
+ if(ipa)
+ *inboundPinholeAllowed = my_atoui(ipa);
+ /*else
+ *inboundPinholeAllowed = 0;*/
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout)
+{
+ struct UPNParg * GetOutboundPinholeTimeoutArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remotePort || !remoteHost)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg));
+ GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost";
+ GetOutboundPinholeTimeoutArgs[0].val = remoteHost;
+ GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort";
+ GetOutboundPinholeTimeoutArgs[1].val = remotePort;
+ GetOutboundPinholeTimeoutArgs[2].elt = "Protocol";
+ GetOutboundPinholeTimeoutArgs[2].val = proto;
+ GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort";
+ GetOutboundPinholeTimeoutArgs[3].val = intPort;
+ GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient";
+ GetOutboundPinholeTimeoutArgs[4].val = intClient;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout");
+ if(p)
+ *opTimeout = my_atoui(p);
+ }
+ ClearNameValueList(&pdata);
+ free(GetOutboundPinholeTimeoutArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID)
+{
+ struct UPNParg * AddPinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPinholeArgs = calloc(7, sizeof(struct UPNParg));
+ // RemoteHost can be wilcarded
+ if(strncmp(remoteHost, "empty", 5)==0)
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = remoteHost;
+ }
+ AddPinholeArgs[1].elt = "RemotePort";
+ AddPinholeArgs[1].val = remotePort;
+ AddPinholeArgs[2].elt = "Protocol";
+ AddPinholeArgs[2].val = proto;
+ AddPinholeArgs[3].elt = "InternalPort";
+ AddPinholeArgs[3].val = intPort;
+ if(strncmp(intClient, "empty", 5)==0)
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = intClient;
+ }
+ AddPinholeArgs[5].elt = "LeaseTime";
+ AddPinholeArgs[5].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPinhole", AddPinholeArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "UniqueID");
+ if(p)
+ {
+ strncpy(uniqueID, p, 8);
+ uniqueID[7] = '\0';
+ }
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ //printf("AddPortMapping errorCode = '%s'\n", resVal);
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(AddPinholeArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime)
+{
+ struct UPNParg * UpdatePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg));
+ UpdatePinholeArgs[0].elt = "UniqueID";
+ UpdatePinholeArgs[0].val = uniqueID;
+ UpdatePinholeArgs[1].elt = "NewLeaseTime";
+ UpdatePinholeArgs[1].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(UpdatePinholeArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePinholeArgs = calloc(2, sizeof(struct UPNParg));
+ DeletePinholeArgs[0].elt = "UniqueID";
+ DeletePinholeArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePinhole", DeletePinholeArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ free(DeletePinholeArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * CheckPinholeWorkingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg));
+ CheckPinholeWorkingArgs[0].elt = "UniqueID";
+ CheckPinholeWorkingArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "IsWorking");
+ if(p)
+ {
+ *isWorking=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ else
+ *isWorking = 0;
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ free(CheckPinholeWorkingArgs);
+ return ret;
+}
+
+LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPinholePacketsArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg));
+ GetPinholePacketsArgs[0].elt = "UniqueID";
+ GetPinholePacketsArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "PinholePackets");
+ if(p)
+ {
+ *packets=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ free(GetPinholePacketsArgs);
+ return ret;
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h
new file mode 100644
index 0000000..66d95e0
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h
@@ -0,0 +1,271 @@
+/* $Id: upnpcommands.h,v 1.23 2011/04/11 09:14:00 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef __UPNPCOMMANDS_H__
+#define __UPNPCOMMANDS_H__
+
+#include "upnpreplyparse.h"
+#include "portlistingparse.h"
+#include "declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype);
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype);
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype);
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+ const char* servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ * with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ * must be the same
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ * permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ * and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ * cannot be a specific port value */
+LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration);
+
+/* UPNP_DeletePortMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+ const char* servicetype,
+ unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry()
+ * retrieves an existing port mapping
+ * params :
+ * in extPort
+ * in proto
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out leaseDuration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration);
+
+/* UPNP_GetGenericPortMappingEntry()
+ * params :
+ * in index
+ * out extPort (6 bytes)
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out protocol (4 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out rHost (64 bytes)
+ * out duration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration);
+
+/* UPNP_GetListOfPortMappings() Available in IGD v2
+ *
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed);
+
+LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout);
+
+LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID);
+
+LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime);
+
+LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking);
+
+LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c
new file mode 100644
index 0000000..a48ae10
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c
@@ -0,0 +1,103 @@
+/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2007 Thomas Bernard
+ * All Right reserved.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <string.h>
+#include "upnperrors.h"
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+
+const char * strupnperror(int err)
+{
+ const char * s = NULL;
+ switch(err) {
+ case UPNPCOMMAND_SUCCESS:
+ s = "Success";
+ break;
+ case UPNPCOMMAND_UNKNOWN_ERROR:
+ s = "Miniupnpc Unknown Error";
+ break;
+ case UPNPCOMMAND_INVALID_ARGS:
+ s = "Miniupnpc Invalid Arguments";
+ break;
+ case UPNPDISCOVER_SOCKET_ERROR:
+ s = "Miniupnpc Socket error";
+ break;
+ case UPNPDISCOVER_MEMORY_ERROR:
+ s = "Miniupnpc Memory allocation error";
+ break;
+ case 401:
+ s = "Invalid Action";
+ break;
+ case 402:
+ s = "Invalid Args";
+ break;
+ case 501:
+ s = "Action Failed";
+ break;
+ case 606:
+ s = "Action not authorized";
+ break;
+ case 701:
+ s = "PinholeSpaceExhausted";
+ break;
+ case 702:
+ s = "FirewallDisabled";
+ break;
+ case 703:
+ s = "InboundPinholeNotAllowed";
+ break;
+ case 704:
+ s = "NoSuchEntry";
+ break;
+ case 705:
+ s = "ProtocolNotSupported";
+ break;
+ case 706:
+ s = "InternalPortWildcardingNotAllowed";
+ break;
+ case 707:
+ s = "ProtocolWildcardingNotAllowed";
+ break;
+ case 708:
+ s = "WildcardNotPermittedInSrcIP";
+ break;
+ case 709:
+ s = "NoPacketSent";
+ break;
+ case 713:
+ s = "SpecifiedArrayIndexInvalid";
+ break;
+ case 714:
+ s = "NoSuchEntryInArray";
+ break;
+ case 715:
+ s = "WildCardNotPermittedInSrcIP";
+ break;
+ case 716:
+ s = "WildCardNotPermittedInExtPort";
+ break;
+ case 718:
+ s = "ConflictInMappingEntry";
+ break;
+ case 724:
+ s = "SamePortValuesRequired";
+ break;
+ case 725:
+ s = "OnlyPermanentLeasesSupported";
+ break;
+ case 726:
+ s = "RemoteHostOnlySupportsWildcard";
+ break;
+ case 727:
+ s = "ExternalPortOnlySupportsWildcard";
+ break;
+ default:
+ s = NULL;
+ }
+ return s;
+}
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h
new file mode 100644
index 0000000..2c544c9
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h
@@ -0,0 +1,26 @@
+/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
+/* (c) 2007 Thomas Bernard
+ * All rights reserved.
+ * MiniUPnP Project.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef __UPNPERRORS_H__
+#define __UPNPERRORS_H__
+
+#include "declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strupnperror()
+ * Return a string description of the UPnP error code
+ * or NULL for undefinded errors */
+LIBSPEC const char * strupnperror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c
new file mode 100644
index 0000000..482030b
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c
@@ -0,0 +1,152 @@
+/* $Id: upnpreplyparse.c,v 1.11 2011/02/07 16:17:06 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2011 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "upnpreplyparse.h"
+#include "minixml.h"
+
+static void
+NameValueParserStartElt(void * d, const char * name, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ if(l>63)
+ l = 63;
+ memcpy(data->curelt, name, l);
+ data->curelt[l] = '\0';
+}
+
+static void
+NameValueParserGetData(void * d, const char * datas, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ struct NameValue * nv;
+ if(strcmp(data->curelt, "NewPortListing") == 0)
+ {
+ /* specific case for NewPortListing which is a XML Document */
+ data->portListing = malloc(l + 1);
+ if(!data->portListing)
+ {
+ /* malloc error */
+ return;
+ }
+ memcpy(data->portListing, datas, l);
+ data->portListing[l] = '\0';
+ data->portListingLength = l;
+ }
+ else
+ {
+ /* standard case. Limited to 63 chars strings */
+ nv = malloc(sizeof(struct NameValue));
+ if(l>63)
+ l = 63;
+ strncpy(nv->name, data->curelt, 64);
+ nv->name[63] = '\0';
+ memcpy(nv->value, datas, l);
+ nv->value[l] = '\0';
+ LIST_INSERT_HEAD( &(data->head), nv, entries);
+ }
+}
+
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data)
+{
+ struct xmlparser parser;
+ LIST_INIT(&(data->head));
+ data->portListing = NULL;
+ data->portListingLength = 0;
+ /* init xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = NameValueParserStartElt;
+ parser.endeltfunc = 0;
+ parser.datafunc = NameValueParserGetData;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+ClearNameValueList(struct NameValueParserData * pdata)
+{
+ struct NameValue * nv;
+ if(pdata->portListing)
+ {
+ free(pdata->portListing);
+ pdata->portListing = NULL;
+ pdata->portListingLength = 0;
+ }
+ while((nv = pdata->head.lh_first) != NULL)
+ {
+ LIST_REMOVE(nv, entries);
+ free(nv);
+ }
+}
+
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ for(nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL);
+ nv = nv->entries.le_next)
+ {
+ if(strcmp(nv->name, Name) == 0)
+ p = nv->value;
+ }
+ return p;
+}
+
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ char * pname;
+ for(nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL);
+ nv = nv->entries.le_next)
+ {
+ pname = strrchr(nv->name, ':');
+ if(pname)
+ pname++;
+ else
+ pname = nv->name;
+ if(strcmp(pname, Name)==0)
+ p = nv->value;
+ }
+ return p;
+}
+#endif
+
+/* debug all-in-one function
+ * do parsing then display to stdout */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize)
+{
+ struct NameValueParserData pdata;
+ struct NameValue * nv;
+ ParseNameValue(buffer, bufsize, &pdata);
+ for(nv = pdata.head.lh_first;
+ nv != NULL;
+ nv = nv->entries.le_next)
+ {
+ printf("%s = %s\n", nv->name, nv->value);
+ }
+ ClearNameValueList(&pdata);
+}
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h
new file mode 100644
index 0000000..267ea87
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h
@@ -0,0 +1,64 @@
+/* $Id: upnpreplyparse.h,v 1.11 2011/02/07 16:17:06 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2011 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef __UPNPREPLYPARSE_H__
+#define __UPNPREPLYPARSE_H__
+
+#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__)
+#include "bsdqueue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NameValue {
+ LIST_ENTRY(NameValue) entries;
+ char name[64];
+ char value[64];
+};
+
+struct NameValueParserData {
+ LIST_HEAD(listhead, NameValue) head;
+ char curelt[64];
+ char * portListing;
+ int portListingLength;
+};
+
+/* ParseNameValue() */
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data);
+
+/* ClearNameValueList() */
+void
+ClearNameValueList(struct NameValueParserData * pdata);
+
+/* GetValueFromNameValueList() */
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name);
+
+/* GetValueFromNameValueListIgnoreNS() */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name);
+
+/* DisplayNameValueList() */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibNATPMP/SConscript b/3rdParty/LibNATPMP/SConscript
new file mode 100644
index 0000000..60e59a0
--- /dev/null
+++ b/3rdParty/LibNATPMP/SConscript
@@ -0,0 +1,50 @@
+Import(["env", "conf_env"])
+
+if env.get("LIBNATPMP_BUNDLED", False) :
+
+################################################################################
+# Module flags
+################################################################################
+
+ if env["SCONS_STAGE"] == "flags" :
+ env["LIBNATPMP_FLAGS"] = {
+ "CPPPATH": [Dir("src/libnatpmp")],
+ "LIBPATH": [Dir(".")],
+ "LIBS": ["Swiften_NATPMP"],
+ }
+ #if env["PLATFORM"] == "win32" :
+ # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")]
+ # if env["MSVC_VERSION"][:3] == "9.0" :
+ # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")]
+
+################################################################################
+# Build
+################################################################################
+
+ if env["SCONS_STAGE"] == "build" :
+ myenv = env.Clone()
+ myenv.Append(CPPPATH = ["src"])
+ # Remove warn flags
+ myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]])
+
+ myenv.Append(CCFLAGS = ["-DNDEBUG", "-DSTATICLIB"])
+
+ #if myenv["PLATFORM"] != "win32":
+ # myenv.Append(CCFLAGS = ["-DMINIUPNPC_SET_SOCKET_TIMEOUT"])
+
+ if myenv["PLATFORM"] == "darwin":
+ myenv.Append(CCFLAGS = ["-DMACOSX", "-D_DARWIN_C_SOURCE"])
+
+ if myenv["PLATFORM"] == "win32":
+ myenv.Append(CCFLAGS = ["-DWIN32"])
+
+ src_files = [
+ "src/libnatpmp/getgateway.c",
+ "src/libnatpmp/natpmp.c",
+ "src/libnatpmp/natpmpc.c",
+ ]
+
+ if myenv["PLATFORM"] == "win32":
+ src_files += ["src/libnatpmp/wingettimeofday.c"]
+
+ myenv.StaticLibrary("Swiften_NATPMP", src_files)
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/LICENSE b/3rdParty/LibNATPMP/src/libnatpmp/LICENSE
new file mode 100644
index 0000000..14db2fe
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2007-2009, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/Makefile b/3rdParty/LibNATPMP/src/libnatpmp/Makefile
new file mode 100644
index 0000000..b523e53
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/Makefile
@@ -0,0 +1,97 @@
+# $Id: Makefile,v 1.16 2011/01/03 17:31:03 nanard Exp $
+# This Makefile is designed for use with GNU make
+# libnatpmp
+# (c) 2007-2011 Thomas Bernard
+# http://miniupnp.free.fr/libnatpmp.html
+
+OS = $(shell uname -s)
+CC = gcc
+INSTALL = install
+
+# APIVERSION is used in soname
+APIVERSION = 1
+LDFLAGS = --no-undefined
+CFLAGS = -O -fPIC -Wall -DENABLE_STRNATPMPERR
+
+LIBOBJS = natpmp.o getgateway.o
+
+OBJS = $(LIBOBJS) testgetgateway.o natpmpc.o
+
+STATICLIB = libnatpmp.a
+ifeq ($(OS), Darwin)
+ SHAREDLIB = libnatpmp.dynlib
+ SONAME = $(basename $(SHAREDLIB)).$(APIVERSION).dylib
+ CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS)
+else
+ SHAREDLIB = libnatpmp.so
+ SONAME = $(SHAREDLIB).$(APIVERSION)
+endif
+
+HEADERS = natpmp.h
+
+EXECUTABLES = testgetgateway natpmpc-shared natpmpc-static
+
+INSTALLPREFIX ?= $(PREFIX)/usr
+INSTALLDIRINC = $(INSTALLPREFIX)/include
+INSTALLDIRLIB = $(INSTALLPREFIX)/lib
+INSTALLDIRBIN = $(INSTALLPREFIX)/bin
+
+.PHONY: all clean depend install cleaninstall installpythonmodule
+
+all: $(STATICLIB) $(SHAREDLIB) $(EXECUTABLES)
+
+pythonmodule: $(STATICLIB) libnatpmpmodule.c setup.py
+ python setup.py build
+ touch $@
+
+installpythonmodule: pythonmodule
+ python setup.py install
+
+clean:
+ $(RM) $(OBJS) $(EXECUTABLES) $(STATICLIB) $(SHAREDLIB)
+ $(RM) pythonmodule
+ $(RM) -r build/ dist/
+
+depend:
+ makedepend -f$(MAKEFILE_LIST) -Y $(OBJS:.o=.c) 2>/dev/null
+
+install: $(HEADERS) $(STATICLIB) $(SHAREDLIB) natpmpc-shared
+ $(INSTALL) -d $(INSTALLDIRINC)
+ $(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC)
+ $(INSTALL) -d $(INSTALLDIRLIB)
+ $(INSTALL) -m 644 $(STATICLIB) $(INSTALLDIRLIB)
+ $(INSTALL) -m 644 $(SHAREDLIB) $(INSTALLDIRLIB)/$(SONAME)
+ $(INSTALL) -d $(INSTALLDIRBIN)
+ $(INSTALL) -m 755 natpmpc-shared $(INSTALLDIRBIN)/natpmpc
+ ln -s -f $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIB)
+
+cleaninstall:
+ $(RM) $(addprefix $(INSTALLDIRINC), $(HEADERS))
+ $(RM) $(INSTALLDIRLIB)/$(SONAME)
+ $(RM) $(INSTALLDIRLIB)/$(SHAREDLIB)
+ $(RM) $(INSTALLDIRLIB)/$(STATICLIB)
+
+testgetgateway: testgetgateway.o getgateway.o
+
+natpmpc-static: natpmpc.o $(STATICLIB)
+ $(CC) -o $@ $^
+
+natpmpc-shared: natpmpc.o $(SHAREDLIB)
+ $(CC) -o $@ $^
+
+$(STATICLIB): $(LIBOBJS)
+ $(AR) crs $@ $?
+
+$(SHAREDLIB): $(LIBOBJS)
+ifeq ($(OS), Darwin)
+ $(CC) -dynamiclib -Wl,-install_name,$(SONAME) -o $@ $^
+else
+ $(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^
+endif
+
+# DO NOT DELETE
+
+natpmp.o: natpmp.h getgateway.h declspec.h
+getgateway.o: getgateway.h declspec.h
+testgetgateway.o: getgateway.h declspec.h
+natpmpc.o: natpmp.h
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/declspec.h b/3rdParty/LibNATPMP/src/libnatpmp/declspec.h
new file mode 100644
index 0000000..ea479d1
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/declspec.h
@@ -0,0 +1,15 @@
+#ifndef __DECLSPEC_H__
+#define __DECLSPEC_H__
+
+#if defined(WIN32) && !defined(STATICLIB)
+ #ifdef NATPMP_EXPORTS
+ #define LIBSPEC __declspec(dllexport)
+ #else
+ #define LIBSPEC __declspec(dllimport)
+ #endif
+#else
+ #define LIBSPEC
+#endif
+
+#endif
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/getgateway.c b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.c
new file mode 100644
index 0000000..bcde3ad
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.c
@@ -0,0 +1,554 @@
+/* $Id: getgateway.c,v 1.19 2009/12/19 15:20:45 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2009, Thomas BERNARD <miniupnp@free.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <stdio.h>
+#include <ctype.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#endif
+#if !defined(_MSC_VER)
+#include <sys/param.h>
+#endif
+/* There is no portable method to get the default route gateway.
+ * So below are four (or five ?) differents functions implementing this.
+ * Parsing /proc/net/route is for linux.
+ * sysctl is the way to access such informations on BSD systems.
+ * Many systems should provide route information through raw PF_ROUTE
+ * sockets.
+ * In MS Windows, default gateway is found by looking into the registry
+ * or by using GetBestRoute(). */
+#ifdef __linux__
+#define USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef BSD
+#undef USE_PROC_NET_ROUTE
+#define USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef __APPLE__
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#define USE_SYSCTL_NET_ROUTE
+#endif
+
+#if (defined(sun) && defined(__SVR4))
+#undef USE_PROC_NET_ROUTE
+#define USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef WIN32
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+//#define USE_WIN32_CODE
+#define USE_WIN32_CODE_2
+#endif
+
+#ifdef __CYGWIN__
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#define USE_WIN32_CODE
+#include <stdarg.h>
+#include <w32api/windef.h>
+#include <w32api/winbase.h>
+#include <w32api/winreg.h>
+#endif
+
+#ifdef __HAIKU__
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <sys/sockio.h>
+#define USE_HAIKU_CODE
+#endif
+
+#ifdef USE_SYSCTL_NET_ROUTE
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#endif
+#ifdef USE_SOCKET_ROUTE
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#endif
+
+#ifdef USE_WIN32_CODE
+#include <unknwn.h>
+#include <winreg.h>
+#define MAX_KEY_LENGTH 255
+#define MAX_VALUE_LENGTH 16383
+#endif
+
+#ifdef USE_WIN32_CODE_2
+#include <windows.h>
+#include <iphlpapi.h>
+#endif
+
+#include "getgateway.h"
+
+#ifndef WIN32
+#define SUCCESS (0)
+#define FAILED (-1)
+#endif
+
+#ifdef USE_PROC_NET_ROUTE
+/*
+ parse /proc/net/route which is as follow :
+
+Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
+wlan0 0001A8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
+eth0 0000FEA9 00000000 0001 0 0 0 0000FFFF 0 0 0
+wlan0 00000000 0101A8C0 0003 0 0 0 00000000 0 0 0
+eth0 00000000 00000000 0001 0 0 1000 00000000 0 0 0
+
+ One header line, and then one line by route by route table entry.
+*/
+int getdefaultgateway(in_addr_t * addr)
+{
+ unsigned long d, g;
+ char buf[256];
+ int line = 0;
+ FILE * f;
+ char * p;
+ f = fopen("/proc/net/route", "r");
+ if(!f)
+ return FAILED;
+ while(fgets(buf, sizeof(buf), f)) {
+ if(line > 0) { /* skip the first line */
+ p = buf;
+ /* skip the interface name */
+ while(*p && !isspace(*p))
+ p++;
+ while(*p && isspace(*p))
+ p++;
+ if(sscanf(p, "%lx%lx", &d, &g)==2) {
+ if(d == 0 && g != 0) { /* default */
+ *addr = g;
+ fclose(f);
+ return SUCCESS;
+ }
+ }
+ }
+ line++;
+ }
+ /* default route not found ! */
+ if(f)
+ fclose(f);
+ return FAILED;
+}
+#endif /* #ifdef USE_PROC_NET_ROUTE */
+
+
+#ifdef USE_SYSCTL_NET_ROUTE
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+int getdefaultgateway(in_addr_t * addr)
+{
+#if 0
+ /* net.route.0.inet.dump.0.0 ? */
+ int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
+ NET_RT_DUMP, 0, 0/*tableid*/};
+#endif
+ /* net.route.0.inet.flags.gateway */
+ int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
+ NET_RT_FLAGS, RTF_GATEWAY};
+ size_t l;
+ char * buf, * p;
+ struct rt_msghdr * rt;
+ struct sockaddr * sa;
+ struct sockaddr * sa_tab[RTAX_MAX];
+ int i;
+ int r = FAILED;
+ if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
+ return FAILED;
+ }
+ if(l>0) {
+ buf = malloc(l);
+ if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
+ free(buf);
+ return FAILED;
+ }
+ for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
+ rt = (struct rt_msghdr *)p;
+ sa = (struct sockaddr *)(rt + 1);
+ for(i=0; i<RTAX_MAX; i++) {
+ if(rt->rtm_addrs & (1 << i)) {
+ sa_tab[i] = sa;
+ sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
+ } else {
+ sa_tab[i] = NULL;
+ }
+ }
+ if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
+ && sa_tab[RTAX_DST]->sa_family == AF_INET
+ && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
+ if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
+ *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
+ r = SUCCESS;
+ }
+ }
+ }
+ free(buf);
+ }
+ return r;
+}
+#endif /* #ifdef USE_SYSCTL_NET_ROUTE */
+
+
+#ifdef USE_SOCKET_ROUTE
+/* Thanks to Darren Kenny for this code */
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\
+ }
+
+#define rtm m_rtmsg.m_rtm
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+int getdefaultgateway(in_addr_t *addr)
+{
+ int s, seq, l, rtm_addrs, i;
+ pid_t pid;
+ struct sockaddr so_dst, so_mask;
+ char *cp = m_rtmsg.m_space;
+ struct sockaddr *gate = NULL, *sa;
+ struct rt_msghdr *msg_hdr;
+
+ pid = getpid();
+ seq = 0;
+ rtm_addrs = RTA_DST | RTA_NETMASK;
+
+ memset(&so_dst, 0, sizeof(so_dst));
+ memset(&so_mask, 0, sizeof(so_mask));
+ memset(&rtm, 0, sizeof(struct rt_msghdr));
+
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+
+ so_dst.sa_family = AF_INET;
+ so_mask.sa_family = AF_INET;
+
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+
+ if (write(s, (char *)&m_rtmsg, l) < 0) {
+ close(s);
+ return FAILED;
+ }
+
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+ close(s);
+
+ msg_hdr = &rtm;
+
+ cp = ((char *)(msg_hdr + 1));
+ if (msg_hdr->rtm_addrs) {
+ for (i = 1; i; i <<= 1)
+ if (i & msg_hdr->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+
+ cp += sizeof(struct sockaddr);
+ }
+ } else {
+ return FAILED;
+ }
+
+
+ if (gate != NULL ) {
+ *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr;
+ return SUCCESS;
+ } else {
+ return FAILED;
+ }
+}
+#endif /* #ifdef USE_SOCKET_ROUTE */
+
+#ifdef USE_WIN32_CODE
+LIBSPEC int getdefaultgateway(in_addr_t * addr)
+{
+ HKEY networkCardsKey;
+ HKEY networkCardKey;
+ HKEY interfacesKey;
+ HKEY interfaceKey;
+ DWORD i = 0;
+ DWORD numSubKeys = 0;
+ TCHAR keyName[MAX_KEY_LENGTH];
+ DWORD keyNameLength = MAX_KEY_LENGTH;
+ TCHAR keyValue[MAX_VALUE_LENGTH];
+ DWORD keyValueLength = MAX_VALUE_LENGTH;
+ DWORD keyValueType = REG_SZ;
+ TCHAR gatewayValue[MAX_VALUE_LENGTH];
+ DWORD gatewayValueLength = MAX_VALUE_LENGTH;
+ DWORD gatewayValueType = REG_MULTI_SZ;
+ int done = 0;
+
+ //const char * networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+ //const char * interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#ifdef UNICODE
+ LPCTSTR networkCardsPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+ LPCTSTR interfacesPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#define STR_SERVICENAME L"ServiceName"
+#define STR_DHCPDEFAULTGATEWAY L"DhcpDefaultGateway"
+#define STR_DEFAULTGATEWAY L"DefaultGateway"
+#else
+ LPCTSTR networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+ LPCTSTR interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#define STR_SERVICENAME "ServiceName"
+#define STR_DHCPDEFAULTGATEWAY "DhcpDefaultGateway"
+#define STR_DEFAULTGATEWAY "DefaultGateway"
+#endif
+ // The windows registry lists its primary network devices in the following location:
+ // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
+ //
+ // Each network device has its own subfolder, named with an index, with various properties:
+ // -NetworkCards
+ // -5
+ // -Description = Broadcom 802.11n Network Adapter
+ // -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D}
+ // -8
+ // -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller
+ // -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD}
+ //
+ // The above service name is the name of a subfolder within:
+ // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
+ //
+ // There may be more subfolders in this interfaces path than listed in the network cards path above:
+ // -Interfaces
+ // -{3a539854-6a70-11db-887c-806e6f6e6963}
+ // -DhcpIPAddress = 0.0.0.0
+ // -[more]
+ // -{E35A72F8-5065-4097-8DFE-C7790774EE4D}
+ // -DhcpIPAddress = 10.0.1.4
+ // -DhcpDefaultGateway = 10.0.1.1
+ // -[more]
+ // -{86226414-5545-4335-A9D1-5BD7120119AD}
+ // -DhcpIpAddress = 10.0.1.5
+ // -DhcpDefaultGateay = 10.0.1.1
+ // -[more]
+ //
+ // In order to extract this information, we enumerate each network card, and extract the ServiceName value.
+ // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value.
+ // Once one is found, we're done.
+ //
+ // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value.
+ // However, the technique used is the technique most cited on the web, and we assume it to be more correct.
+
+ if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key
+ networkCardsPath, // Name of registry subkey to open
+ 0, // Reserved - must be zero
+ KEY_READ, // Mask - desired access rights
+ &networkCardsKey)) // Pointer to output key
+ {
+ // Unable to open network cards keys
+ return -1;
+ }
+
+ if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key
+ interfacesPath, // Name of registry subkey to open
+ 0, // Reserved - must be zero
+ KEY_READ, // Mask - desired access rights
+ &interfacesKey)) // Pointer to output key
+ {
+ // Unable to open interfaces key
+ RegCloseKey(networkCardsKey);
+ return -1;
+ }
+
+ // Figure out how many subfolders are within the NetworkCards folder
+ RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys);
+
+ // Enumrate through each subfolder within the NetworkCards folder
+ for(i = 0; i < numSubKeys && !done; i++)
+ {
+ keyNameLength = MAX_KEY_LENGTH;
+ if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key
+ i, // Index of subkey to retrieve
+ keyName, // Buffer that receives the name of the subkey
+ &keyNameLength, // Variable that receives the size of the above buffer
+ NULL, // Reserved - must be NULL
+ NULL, // Buffer that receives the class string
+ NULL, // Variable that receives the size of the above buffer
+ NULL)) // Variable that receives the last write time of subkey
+ {
+ if(RegOpenKeyEx(networkCardsKey, keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS)
+ {
+ keyValueLength = MAX_VALUE_LENGTH;
+ if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey, // Open registry key
+ STR_SERVICENAME, // Name of key to query
+ NULL, // Reserved - must be NULL
+ &keyValueType, // Receives value type
+ (LPBYTE)keyValue, // Receives value
+ &keyValueLength)) // Receives value length in bytes
+ {
+// printf("keyValue: %s\n", keyValue);
+ if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS)
+ {
+ gatewayValueLength = MAX_VALUE_LENGTH;
+ if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key
+ STR_DHCPDEFAULTGATEWAY, // Name of key to query
+ NULL, // Reserved - must be NULL
+ &gatewayValueType, // Receives value type
+ (LPBYTE)gatewayValue, // Receives value
+ &gatewayValueLength)) // Receives value length in bytes
+ {
+ // Check to make sure it's a string
+ if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
+ {
+ //printf("gatewayValue: %s\n", gatewayValue);
+ done = 1;
+ }
+ }
+ else if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key
+ STR_DEFAULTGATEWAY, // Name of key to query
+ NULL, // Reserved - must be NULL
+ &gatewayValueType, // Receives value type
+ (LPBYTE)gatewayValue,// Receives value
+ &gatewayValueLength)) // Receives value length in bytes
+ {
+ // Check to make sure it's a string
+ if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
+ {
+ //printf("gatewayValue: %s\n", gatewayValue);
+ done = 1;
+ }
+ }
+ RegCloseKey(interfaceKey);
+ }
+ }
+ RegCloseKey(networkCardKey);
+ }
+ }
+ }
+
+ RegCloseKey(interfacesKey);
+ RegCloseKey(networkCardsKey);
+
+ if(done)
+ {
+#if UNICODE
+ char tmp[32];
+ for(i = 0; i < 32; i++) {
+ tmp[i] = (char)gatewayValue[i];
+ if(!tmp[i])
+ break;
+ }
+ tmp[31] = '\0';
+ *addr = inet_addr(tmp);
+#else
+ *addr = inet_addr(gatewayValue);
+#endif
+ return 0;
+ }
+
+ return -1;
+}
+#endif /* #ifdef USE_WIN32_CODE */
+
+#ifdef USE_WIN32_CODE_2
+int getdefaultgateway(in_addr_t *addr)
+{
+ MIB_IPFORWARDROW ip_forward;
+ memset(&ip_forward, 0, sizeof(ip_forward));
+ if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR)
+ return -1;
+ *addr = ip_forward.dwForwardNextHop;
+ return 0;
+}
+#endif /* #ifdef USE_WIN32_CODE_2 */
+
+#ifdef USE_HAIKU_CODE
+int getdefaultgateway(in_addr_t *addr)
+{
+ int fd, ret = -1;
+ struct ifconf config;
+ void *buffer = NULL;
+ struct ifreq *interface;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ return -1;
+ }
+ if (ioctl(fd, SIOCGRTSIZE, &config, sizeof(config)) != 0) {
+ goto fail;
+ }
+ if (config.ifc_value < 1) {
+ goto fail; /* No routes */
+ }
+ if ((buffer = malloc(config.ifc_value)) == NULL) {
+ goto fail;
+ }
+ config.ifc_len = config.ifc_value;
+ config.ifc_buf = buffer;
+ if (ioctl(fd, SIOCGRTTABLE, &config, sizeof(config)) != 0) {
+ goto fail;
+ }
+ for (interface = buffer;
+ (uint8_t *)interface < (uint8_t *)buffer + config.ifc_len; ) {
+ struct route_entry route = interface->ifr_route;
+ int intfSize;
+ if (route.flags & (RTF_GATEWAY | RTF_DEFAULT)) {
+ *addr = ((struct sockaddr_in *)route.gateway)->sin_addr.s_addr;
+ ret = 0;
+ break;
+ }
+ intfSize = sizeof(route) + IF_NAMESIZE;
+ if (route.destination != NULL) {
+ intfSize += route.destination->sa_len;
+ }
+ if (route.mask != NULL) {
+ intfSize += route.mask->sa_len;
+ }
+ if (route.gateway != NULL) {
+ intfSize += route.gateway->sa_len;
+ }
+ interface = (struct ifreq *)((uint8_t *)interface + intfSize);
+ }
+fail:
+ free(buffer);
+ close(fd);
+ return ret;
+}
+#endif /* #ifdef USE_HAIKU_CODE */
+
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/getgateway.h b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.h
new file mode 100644
index 0000000..9432528
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.h
@@ -0,0 +1,36 @@
+/* $Id: getgateway.h,v 1.4 2009/12/19 12:00:00 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007, Thomas BERNARD <miniupnp@free.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifndef __GETGATEWAY_H__
+#define __GETGATEWAY_H__
+
+#ifdef WIN32
+#if !defined(_MSC_VER)
+#include <stdint.h>
+#else
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif
+#define in_addr_t uint32_t
+#endif
+#include "declspec.h"
+
+/* getdefaultgateway() :
+ * return value :
+ * 0 : success
+ * -1 : failure */
+LIBSPEC int getdefaultgateway(in_addr_t * addr);
+
+#endif
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/natpmp.c b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.c
new file mode 100644
index 0000000..53869c3
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.c
@@ -0,0 +1,350 @@
+/* $Id: natpmp.c,v 1.13 2011/01/03 17:31:03 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2011, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifdef __linux__
+#define _BSD_SOURCE 1
+#endif
+#include <string.h>
+#include <time.h>
+#if !defined(_MSC_VER)
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <errno.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define ECONNREFUSED WSAECONNREFUSED
+#include "wingettimeofday.h"
+#else
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#define closesocket close
+#endif
+#include "natpmp.h"
+#include "getgateway.h"
+
+LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw)
+{
+#ifdef WIN32
+ u_long ioctlArg = 1;
+#else
+ int flags;
+#endif
+ struct sockaddr_in addr;
+ if(!p)
+ return NATPMP_ERR_INVALIDARGS;
+ memset(p, 0, sizeof(natpmp_t));
+ p->s = socket(PF_INET, SOCK_DGRAM, 0);
+ if(p->s < 0)
+ return NATPMP_ERR_SOCKETERROR;
+#ifdef WIN32
+ if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
+ return NATPMP_ERR_FCNTLERROR;
+#else
+ if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
+ return NATPMP_ERR_FCNTLERROR;
+ if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
+ return NATPMP_ERR_FCNTLERROR;
+#endif
+
+ if(forcegw) {
+ p->gateway = forcedgw;
+ } else {
+ if(getdefaultgateway(&(p->gateway)) < 0)
+ return NATPMP_ERR_CANNOTGETGATEWAY;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NATPMP_PORT);
+ addr.sin_addr.s_addr = p->gateway;
+ if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ return NATPMP_ERR_CONNECTERR;
+ return 0;
+}
+
+LIBSPEC int closenatpmp(natpmp_t * p)
+{
+ if(!p)
+ return NATPMP_ERR_INVALIDARGS;
+ if(closesocket(p->s) < 0)
+ return NATPMP_ERR_CLOSEERR;
+ return 0;
+}
+
+int sendpendingrequest(natpmp_t * p)
+{
+ int r;
+/* struct sockaddr_in addr;*/
+ if(!p)
+ return NATPMP_ERR_INVALIDARGS;
+/* memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NATPMP_PORT);
+ addr.sin_addr.s_addr = p->gateway;
+ r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
+ (struct sockaddr *)&addr, sizeof(addr));*/
+ r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
+ return (r<0) ? NATPMP_ERR_SENDERR : r;
+}
+
+int sendnatpmprequest(natpmp_t * p)
+{
+ int n;
+ if(!p)
+ return NATPMP_ERR_INVALIDARGS;
+ /* TODO : check if no request is allready pending */
+ p->has_pending_request = 1;
+ p->try_number = 1;
+ n = sendpendingrequest(p);
+ gettimeofday(&p->retry_time, NULL); // check errors !
+ p->retry_time.tv_usec += 250000; /* add 250ms */
+ if(p->retry_time.tv_usec >= 1000000) {
+ p->retry_time.tv_usec -= 1000000;
+ p->retry_time.tv_sec++;
+ }
+ return n;
+}
+
+LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
+{
+ struct timeval now;
+ if(!p || !timeout)
+ return NATPMP_ERR_INVALIDARGS;
+ if(!p->has_pending_request)
+ return NATPMP_ERR_NOPENDINGREQ;
+ if(gettimeofday(&now, NULL) < 0)
+ return NATPMP_ERR_GETTIMEOFDAYERR;
+ timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
+ timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
+ if(timeout->tv_usec < 0) {
+ timeout->tv_usec += 1000000;
+ timeout->tv_sec--;
+ }
+ return 0;
+}
+
+LIBSPEC int sendpublicaddressrequest(natpmp_t * p)
+{
+ if(!p)
+ return NATPMP_ERR_INVALIDARGS;
+ //static const unsigned char request[] = { 0, 0 };
+ p->pending_request[0] = 0;
+ p->pending_request[1] = 0;
+ p->pending_request_len = 2;
+ // TODO: return 0 instead of sizeof(request) ??
+ return sendnatpmprequest(p);
+}
+
+LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
+ uint16_t privateport, uint16_t publicport,
+ uint32_t lifetime)
+{
+ if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
+ return NATPMP_ERR_INVALIDARGS;
+ p->pending_request[0] = 0;
+ p->pending_request[1] = protocol;
+ p->pending_request[2] = 0;
+ p->pending_request[3] = 0;
+ *((uint16_t *)(p->pending_request + 4)) = htons(privateport);
+ *((uint16_t *)(p->pending_request + 6)) = htons(publicport);
+ *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
+ p->pending_request_len = 12;
+ return sendnatpmprequest(p);
+}
+
+LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
+{
+ unsigned char buf[16];
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int n;
+ if(!p)
+ return NATPMP_ERR_INVALIDARGS;
+ n = recvfrom(p->s, buf, sizeof(buf), 0,
+ (struct sockaddr *)&addr, &addrlen);
+ if(n<0)
+ switch(errno) {
+ /*case EAGAIN:*/
+ case EWOULDBLOCK:
+ n = NATPMP_TRYAGAIN;
+ break;
+ case ECONNREFUSED:
+ n = NATPMP_ERR_NOGATEWAYSUPPORT;
+ break;
+ default:
+ n = NATPMP_ERR_RECVFROM;
+ }
+ /* check that addr is correct (= gateway) */
+ else if(addr.sin_addr.s_addr != p->gateway)
+ n = NATPMP_ERR_WRONGPACKETSOURCE;
+ else {
+ response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
+ response->epoch = ntohl(*((uint32_t *)(buf + 4)));
+ if(buf[0] != 0)
+ n = NATPMP_ERR_UNSUPPORTEDVERSION;
+ else if(buf[1] < 128 || buf[1] > 130)
+ n = NATPMP_ERR_UNSUPPORTEDOPCODE;
+ else if(response->resultcode != 0) {
+ switch(response->resultcode) {
+ case 1:
+ n = NATPMP_ERR_UNSUPPORTEDVERSION;
+ break;
+ case 2:
+ n = NATPMP_ERR_NOTAUTHORIZED;
+ break;
+ case 3:
+ n = NATPMP_ERR_NETWORKFAILURE;
+ break;
+ case 4:
+ n = NATPMP_ERR_OUTOFRESOURCES;
+ break;
+ case 5:
+ n = NATPMP_ERR_UNSUPPORTEDOPCODE;
+ break;
+ default:
+ n = NATPMP_ERR_UNDEFINEDERROR;
+ }
+ } else {
+ response->type = buf[1] & 0x7f;
+ if(buf[1] == 128)
+ //response->publicaddress.addr = *((uint32_t *)(buf + 8));
+ response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
+ else {
+ response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
+ response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
+ response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
+ }
+ n = 0;
+ }
+ }
+ return n;
+}
+
+int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
+{
+ int n;
+ if(!p || !response)
+ return NATPMP_ERR_INVALIDARGS;
+ if(!p->has_pending_request)
+ return NATPMP_ERR_NOPENDINGREQ;
+ n = readnatpmpresponse(p, response);
+ if(n<0) {
+ if(n==NATPMP_TRYAGAIN) {
+ struct timeval now;
+ gettimeofday(&now, NULL); // check errors !
+ if(timercmp(&now, &p->retry_time, >=)) {
+ int delay, r;
+ if(p->try_number >= 9) {
+ return NATPMP_ERR_NOGATEWAYSUPPORT;
+ }
+ /*printf("retry! %d\n", p->try_number);*/
+ delay = 250 * (1<<p->try_number); // ms
+ /*for(i=0; i<p->try_number; i++)
+ delay += delay;*/
+ p->retry_time.tv_sec += (delay / 1000);
+ p->retry_time.tv_usec += (delay % 1000) * 1000;
+ if(p->retry_time.tv_usec >= 1000000) {
+ p->retry_time.tv_usec -= 1000000;
+ p->retry_time.tv_sec++;
+ }
+ p->try_number++;
+ r = sendpendingrequest(p);
+ if(r<0)
+ return r;
+ }
+ }
+ } else {
+ p->has_pending_request = 0;
+ }
+ return n;
+}
+
+#ifdef ENABLE_STRNATPMPERR
+LIBSPEC const char * strnatpmperr(int r)
+{
+ const char * s;
+ switch(r) {
+ case NATPMP_ERR_INVALIDARGS:
+ s = "invalid arguments";
+ break;
+ case NATPMP_ERR_SOCKETERROR:
+ s = "socket() failed";
+ break;
+ case NATPMP_ERR_CANNOTGETGATEWAY:
+ s = "cannot get default gateway ip address";
+ break;
+ case NATPMP_ERR_CLOSEERR:
+#ifdef WIN32
+ s = "closesocket() failed";
+#else
+ s = "close() failed";
+#endif
+ break;
+ case NATPMP_ERR_RECVFROM:
+ s = "recvfrom() failed";
+ break;
+ case NATPMP_ERR_NOPENDINGREQ:
+ s = "no pending request";
+ break;
+ case NATPMP_ERR_NOGATEWAYSUPPORT:
+ s = "the gateway does not support nat-pmp";
+ break;
+ case NATPMP_ERR_CONNECTERR:
+ s = "connect() failed";
+ break;
+ case NATPMP_ERR_WRONGPACKETSOURCE:
+ s = "packet not received from the default gateway";
+ break;
+ case NATPMP_ERR_SENDERR:
+ s = "send() failed";
+ break;
+ case NATPMP_ERR_FCNTLERROR:
+ s = "fcntl() failed";
+ break;
+ case NATPMP_ERR_GETTIMEOFDAYERR:
+ s = "gettimeofday() failed";
+ break;
+ case NATPMP_ERR_UNSUPPORTEDVERSION:
+ s = "unsupported nat-pmp version error from server";
+ break;
+ case NATPMP_ERR_UNSUPPORTEDOPCODE:
+ s = "unsupported nat-pmp opcode error from server";
+ break;
+ case NATPMP_ERR_UNDEFINEDERROR:
+ s = "undefined nat-pmp server error";
+ break;
+ case NATPMP_ERR_NOTAUTHORIZED:
+ s = "not authorized";
+ break;
+ case NATPMP_ERR_NETWORKFAILURE:
+ s = "network failure";
+ break;
+ case NATPMP_ERR_OUTOFRESOURCES:
+ s = "nat-pmp server out of resources";
+ break;
+ default:
+ s = "Unknown libnatpmp error";
+ }
+ return s;
+}
+#endif
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/natpmp.h b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.h
new file mode 100644
index 0000000..1175b58
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.h
@@ -0,0 +1,203 @@
+/* $Id: natpmp.h,v 1.14 2011/01/03 17:31:03 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2011, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifndef __NATPMP_H__
+#define __NATPMP_H__
+
+/* NAT-PMP Port as defined by the NAT-PMP draft */
+#define NATPMP_PORT (5351)
+
+#include <time.h>
+#if !defined(_MSC_VER)
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <winsock2.h>
+#if !defined(_MSC_VER) || _MSC_VER >= 1600
+#include <stdint.h>
+#else
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif
+#define in_addr_t uint32_t
+#include "declspec.h"
+#else
+#define LIBSPEC
+#include <netinet/in.h>
+#endif
+
+typedef struct {
+ int s; /* socket */
+ in_addr_t gateway; /* default gateway (IPv4) */
+ int has_pending_request;
+ unsigned char pending_request[12];
+ int pending_request_len;
+ int try_number;
+ struct timeval retry_time;
+} natpmp_t;
+
+typedef struct {
+ uint16_t type; /* NATPMP_RESPTYPE_* */
+ uint16_t resultcode; /* NAT-PMP response code */
+ uint32_t epoch; /* Seconds since start of epoch */
+ union {
+ struct {
+ //in_addr_t addr;
+ struct in_addr addr;
+ } publicaddress;
+ struct {
+ uint16_t privateport;
+ uint16_t mappedpublicport;
+ uint32_t lifetime;
+ } newportmapping;
+ } pnu;
+} natpmpresp_t;
+
+/* possible values for type field of natpmpresp_t */
+#define NATPMP_RESPTYPE_PUBLICADDRESS (0)
+#define NATPMP_RESPTYPE_UDPPORTMAPPING (1)
+#define NATPMP_RESPTYPE_TCPPORTMAPPING (2)
+
+/* Values to pass to sendnewportmappingrequest() */
+#define NATPMP_PROTOCOL_UDP (1)
+#define NATPMP_PROTOCOL_TCP (2)
+
+/* return values */
+/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */
+#define NATPMP_ERR_INVALIDARGS (-1)
+/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */
+#define NATPMP_ERR_SOCKETERROR (-2)
+/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */
+#define NATPMP_ERR_CANNOTGETGATEWAY (-3)
+/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */
+#define NATPMP_ERR_CLOSEERR (-4)
+/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */
+#define NATPMP_ERR_RECVFROM (-5)
+/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while
+ * no NAT-PMP request was pending */
+#define NATPMP_ERR_NOPENDINGREQ (-6)
+/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */
+#define NATPMP_ERR_NOGATEWAYSUPPORT (-7)
+/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */
+#define NATPMP_ERR_CONNECTERR (-8)
+/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */
+#define NATPMP_ERR_WRONGPACKETSOURCE (-9)
+/* NATPMP_ERR_SENDERR : send() failed. check errno for details */
+#define NATPMP_ERR_SENDERR (-10)
+/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */
+#define NATPMP_ERR_FCNTLERROR (-11)
+/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */
+#define NATPMP_ERR_GETTIMEOFDAYERR (-12)
+
+/* */
+#define NATPMP_ERR_UNSUPPORTEDVERSION (-14)
+#define NATPMP_ERR_UNSUPPORTEDOPCODE (-15)
+
+/* Errors from the server : */
+#define NATPMP_ERR_UNDEFINEDERROR (-49)
+#define NATPMP_ERR_NOTAUTHORIZED (-51)
+#define NATPMP_ERR_NETWORKFAILURE (-52)
+#define NATPMP_ERR_OUTOFRESOURCES (-53)
+
+/* NATPMP_TRYAGAIN : no data available for the moment. try again later */
+#define NATPMP_TRYAGAIN (-100)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* initnatpmp()
+ * initialize a natpmp_t object
+ * With forcegw=1 the gateway is not detected automaticaly.
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SOCKETERROR
+ * NATPMP_ERR_FCNTLERROR
+ * NATPMP_ERR_CANNOTGETGATEWAY
+ * NATPMP_ERR_CONNECTERR */
+LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw);
+
+/* closenatpmp()
+ * close resources associated with a natpmp_t object
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_CLOSEERR */
+LIBSPEC int closenatpmp(natpmp_t * p);
+
+/* sendpublicaddressrequest()
+ * send a public address NAT-PMP request to the network gateway
+ * Return values :
+ * 2 = OK (size of the request)
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SENDERR */
+LIBSPEC int sendpublicaddressrequest(natpmp_t * p);
+
+/* sendnewportmappingrequest()
+ * send a new port mapping NAT-PMP request to the network gateway
+ * Arguments :
+ * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP,
+ * lifetime is in seconds.
+ * To remove a port mapping, set lifetime to zero.
+ * To remove all port mappings to the host, set lifetime and both ports
+ * to zero.
+ * Return values :
+ * 12 = OK (size of the request)
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SENDERR */
+LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
+ uint16_t privateport, uint16_t publicport,
+ uint32_t lifetime);
+
+/* getnatpmprequesttimeout()
+ * fills the timeval structure with the timeout duration of the
+ * currently pending NAT-PMP request.
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_GETTIMEOFDAYERR
+ * NATPMP_ERR_NOPENDINGREQ */
+LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout);
+
+/* readnatpmpresponseorretry()
+ * fills the natpmpresp_t structure if possible
+ * Return values :
+ * 0 = OK
+ * NATPMP_TRYAGAIN
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_NOPENDINGREQ
+ * NATPMP_ERR_NOGATEWAYSUPPORT
+ * NATPMP_ERR_RECVFROM
+ * NATPMP_ERR_WRONGPACKETSOURCE
+ * NATPMP_ERR_UNSUPPORTEDVERSION
+ * NATPMP_ERR_UNSUPPORTEDOPCODE
+ * NATPMP_ERR_NOTAUTHORIZED
+ * NATPMP_ERR_NETWORKFAILURE
+ * NATPMP_ERR_OUTOFRESOURCES
+ * NATPMP_ERR_UNSUPPORTEDOPCODE
+ * NATPMP_ERR_UNDEFINEDERROR */
+LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response);
+
+#ifdef ENABLE_STRNATPMPERR
+LIBSPEC const char * strnatpmperr(int t);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c b/3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c
new file mode 100644
index 0000000..d869572
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c
@@ -0,0 +1,229 @@
+/* $Id: natpmpc.c,v 1.9 2011/04/18 18:25:21 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2011, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#if defined(_MSC_VER)
+#if _MSC_VER >= 1400
+#define strcasecmp _stricmp
+#else
+#define strcasecmp stricmp
+#endif
+#else
+#include <unistd.h>
+#endif
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "natpmp.h"
+
+void usage(FILE * out, const char * argv0)
+{
+ fprintf(out, "Usage :\n");
+ fprintf(out, " %s [options]\n", argv0);
+ fprintf(out, "\tdisplay the public IP address.\n");
+ fprintf(out, " %s -h\n", argv0);
+ fprintf(out, "\tdisplay this help screen.\n");
+ fprintf(out, " %s [options] -a <public port> <private port> <protocol> [lifetime]\n", argv0);
+ fprintf(out, "\tadd a port mapping.\n");
+ fprintf(out, "\nOption available :\n");
+ fprintf(out, " -g ipv4address\n");
+ fprintf(out, "\tforce the gateway to be used as destination for NAT-PMP commands.\n");
+ fprintf(out, "\n In order to remove a mapping, set it with a lifetime of 0 seconds.\n");
+ fprintf(out, " To remove all mappings for your machine, use 0 as private port and lifetime.\n");
+}
+
+/* sample code for using libnatpmp */
+int main(int argc, char * * argv)
+{
+ natpmp_t natpmp;
+ natpmpresp_t response;
+ int r;
+ int sav_errno;
+ struct timeval timeout;
+ fd_set fds;
+ int i;
+ int protocol = 0;
+ uint16_t privateport = 0;
+ uint16_t publicport = 0;
+ uint32_t lifetime = 3600;
+ int command = 0;
+ int forcegw = 0;
+ in_addr_t gateway = 0;
+
+#ifdef WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+
+ /* argument parsing */
+ for(i=1; i<argc; i++) {
+ if(argv[i][0] == '-') {
+ switch(argv[i][1]) {
+ case 'h':
+ usage(stdout, argv[0]);
+ return 0;
+ case 'g':
+ forcegw = 1;
+ if(argc < i + 1) {
+ fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]);
+ return 1;
+ }
+ gateway = inet_addr(argv[++i]);
+ break;
+ case 'a':
+ command = 'a';
+ if(argc < i + 3) {
+ fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]);
+ return 1;
+ }
+ i++;
+ if(1 != sscanf(argv[i], "%hu", &publicport)) {
+ fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]);
+ return 1;
+ }
+ i++;
+ if(1 != sscanf(argv[i], "%hu", &privateport)) {
+ fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]);
+ return 1;
+ }
+ i++;
+ if(0 == strcasecmp(argv[i], "tcp"))
+ protocol = NATPMP_PROTOCOL_TCP;
+ else if(0 == strcasecmp(argv[i], "udp"))
+ protocol = NATPMP_PROTOCOL_UDP;
+ else {
+ fprintf(stderr, "%s is not a valid protocol\n", argv[i]);
+ return 1;
+ }
+ if(argc >= i) {
+ i++;
+ if(1 != sscanf(argv[i], "%u", &lifetime)) {
+ fprintf(stderr, "%s is not a correct 32bits unsigned integer\n", argv[i]);
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown option %s\n", argv[i]);
+ usage(stderr, argv[0]);
+ return 1;
+ }
+ } else {
+ fprintf(stderr, "Unknown option %s\n", argv[i]);
+ usage(stderr, argv[0]);
+ return 1;
+ }
+ }
+
+ /* initnatpmp() */
+ r = initnatpmp(&natpmp, forcegw, gateway);
+ printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS");
+ if(r<0)
+ return 1;
+
+ printf("using gateway : %s\n", inet_ntoa(*((struct in_addr *)&natpmp.gateway)));
+
+ /* sendpublicaddressrequest() */
+ r = sendpublicaddressrequest(&natpmp);
+ printf("sendpublicaddressrequest returned %d (%s)\n",
+ r, r==2?"SUCCESS":"FAILED");
+ if(r<0)
+ return 1;
+
+ do {
+ FD_ZERO(&fds);
+ FD_SET(natpmp.s, &fds);
+ getnatpmprequesttimeout(&natpmp, &timeout);
+ r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+ if(r<0) {
+ fprintf(stderr, "select()");
+ return 1;
+ }
+ r = readnatpmpresponseorretry(&natpmp, &response);
+ sav_errno = errno;
+ printf("readnatpmpresponseorretry returned %d (%s)\n",
+ r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
+ if(r<0 && r!=NATPMP_TRYAGAIN) {
+#ifdef ENABLE_STRNATPMPERR
+ fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n",
+ strnatpmperr(r));
+#endif
+ fprintf(stderr, " errno=%d '%s'\n",
+ sav_errno, strerror(sav_errno));
+ }
+ } while(r==NATPMP_TRYAGAIN);
+ if(r<0)
+ return 1;
+
+ /* TODO : check that response.type == 0 */
+ printf("Public IP address : %s\n", inet_ntoa(response.pnu.publicaddress.addr));
+ printf("epoch = %u\n", response.epoch);
+
+ if(command == 'a') {
+ /* sendnewportmappingrequest() */
+ r = sendnewportmappingrequest(&natpmp, protocol,
+ privateport, publicport,
+ lifetime);
+ printf("sendnewportmappingrequest returned %d (%s)\n",
+ r, r==12?"SUCCESS":"FAILED");
+ if(r < 0)
+ return 1;
+
+ do {
+ FD_ZERO(&fds);
+ FD_SET(natpmp.s, &fds);
+ getnatpmprequesttimeout(&natpmp, &timeout);
+ select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+ r = readnatpmpresponseorretry(&natpmp, &response);
+ printf("readnatpmpresponseorretry returned %d (%s)\n",
+ r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
+ } while(r==NATPMP_TRYAGAIN);
+ if(r<0) {
+#ifdef ENABLE_STRNATPMPERR
+ fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n",
+ strnatpmperr(r));
+#endif
+ return 1;
+ }
+
+ printf("Mapped public port %hu protocol %s to local port %hu "
+ "liftime %u\n",
+ response.pnu.newportmapping.mappedpublicport,
+ response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" :
+ (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" :
+ "UNKNOWN"),
+ response.pnu.newportmapping.privateport,
+ response.pnu.newportmapping.lifetime);
+ printf("epoch = %u\n", response.epoch);
+ }
+
+ r = closenatpmp(&natpmp);
+ printf("closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED");
+ if(r<0)
+ return 1;
+
+ return 0;
+}
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c
new file mode 100644
index 0000000..5b1b8a6
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c
@@ -0,0 +1,50 @@
+/* $Id: wingettimeofday.c,v 1.3 2009/12/19 12:00:00 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifdef WIN32
+#if defined(_MSC_VER)
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+#else
+#include <sys/time.h>
+#endif
+
+typedef struct _FILETIME {
+ unsigned long dwLowDateTime;
+ unsigned long dwHighDateTime;
+} FILETIME;
+
+void __stdcall GetSystemTimeAsFileTime(FILETIME*);
+
+//int gettimeofday(struct timeval* p, void* tz /* IGNORED */);
+
+int gettimeofday(struct timeval* p, void* tz /* IGNORED */) {
+ union {
+ long long ns100; /*time since 1 Jan 1601 in 100ns units */
+ FILETIME ft;
+ } _now;
+
+ if(!p)
+ return -1;
+ GetSystemTimeAsFileTime( &(_now.ft) );
+ p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL );
+ p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL);
+ return 0;
+}
+#endif
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h
new file mode 100644
index 0000000..ed6c599
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h
@@ -0,0 +1,27 @@
+/* $Id: wingettimeofday.h,v 1.1 2009/12/19 12:02:42 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifndef __WINGETTIMEOFDAY_H__
+#define __WINGETTIMEOFDAY_H__
+#ifdef WIN32
+#if defined(_MSC_VER)
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+int gettimeofday(struct timeval* p, void* tz /* IGNORED */);
+#endif
+#endif
diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot
index e969def..77b1cf1 100644
--- a/BuildTools/SCons/SConscript.boot
+++ b/BuildTools/SCons/SConscript.boot
@@ -180,7 +180,7 @@ else :
if gccVersion >= ["4", "5", "0"] :
env.Append(CXXFLAGS = ["-Wlogical-op"])
if "clang" in env["CC"] :
- env.Append(CXXFLAGS = ["-W#warnings", "-W-Wc++0x-compat", "-Wc++0x-compat", "-Waddress-of-temporary", "-Wambiguous-member-template", "-Warray-bounds", "-Watomic-properties", "-Wbind-to-temporary-copy", "-Wbuiltin-macro-redefined", "-Wc++-compat", "-Wc++0x-extensions", "-Wcomments", "-Wconditional-uninitialized", "-Wconstant-logical-operand", "-Wdeclaration-after-statement", "-Wdeprecated", "-Wdeprecated-implementations", "-Wdeprecated-writable-strings", "-Wduplicate-method-arg", "-Wempty-body", "-Wendif-labels", "-Wenum-compare", "-Wformat=2", "-Wfour-char-constants", "-Wgnu", "-Wincomplete-implementation", "-Winvalid-noreturn", "-Winvalid-offsetof", "-Winvalid-token-paste", "-Wlocal-type-template-args", "-Wmethod-signatures", "-Wmicrosoft", "-Wmissing-declarations", "-Wnon-pod-varargs", "-Wnonfragile-abi2", "-Wnull-dereference", "-Wout-of-line-declaration", "-Woverlength-strings", "-Wpacked", "-Wpointer-arith", "-Wpointer-sign", "-Wprotocol", "-Wreadonly-setter-attrs", "-Wselector", "-Wshift-overflow", "-Wshift-sign-overflow", "-Wstrict-selector-match", "-Wsuper-class-method-mismatch", "-Wtautological-compare", "-Wtypedef-redefinition", "-Wundeclared-selector", "-Wunknown-attributes", "-Wunknown-warning-option", "-Wunnamed-type-template-args", "-Wunused-exception-parameter", "-Wunused-member-function", "-Wused-but-marked-unused", "-Wvariadic-macros"])
+ env.Append(CXXFLAGS = ["-W#warnings", "-Wc++0x-compat", "-Waddress-of-temporary", "-Wambiguous-member-template", "-Warray-bounds", "-Watomic-properties", "-Wbind-to-temporary-copy", "-Wbuiltin-macro-redefined", "-Wc++-compat", "-Wc++0x-extensions", "-Wcomments", "-Wconditional-uninitialized", "-Wconstant-logical-operand", "-Wdeclaration-after-statement", "-Wdeprecated", "-Wdeprecated-implementations", "-Wdeprecated-writable-strings", "-Wduplicate-method-arg", "-Wempty-body", "-Wendif-labels", "-Wenum-compare", "-Wformat=2", "-Wfour-char-constants", "-Wgnu", "-Wincomplete-implementation", "-Winvalid-noreturn", "-Winvalid-offsetof", "-Winvalid-token-paste", "-Wlocal-type-template-args", "-Wmethod-signatures", "-Wmicrosoft", "-Wmissing-declarations", "-Wnon-pod-varargs", "-Wnonfragile-abi2", "-Wnull-dereference", "-Wout-of-line-declaration", "-Woverlength-strings", "-Wpacked", "-Wpointer-arith", "-Wpointer-sign", "-Wprotocol", "-Wreadonly-setter-attrs", "-Wselector", "-Wshift-overflow", "-Wshift-sign-overflow", "-Wstrict-selector-match", "-Wsuper-class-method-mismatch", "-Wtautological-compare", "-Wtypedef-redefinition", "-Wundeclared-selector", "-Wunknown-warning-option", "-Wunnamed-type-template-args", "-Wunused-exception-parameter", "-Wunused-member-function", "-Wused-but-marked-unused", "-Wvariadic-macros"])
# To enable:
# "-Wheader-hygiene"
# "-Wnon-gcc",
@@ -193,7 +193,7 @@ if env.get("coverage", 0) :
env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"])
if env["PLATFORM"] == "win32" :
- env.Append(LIBS = ["user32", "crypt32", "dnsapi", "ws2_32", "wsock32", "Advapi32"])
+ env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32"])
env.Append(CCFLAGS = ["/EHsc", "/nologo"])
# FIXME: We should find a decent solution for MSVS 10
if int(env["MSVS_VERSION"].split(".")[0]) < 10 :
diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct
index 88a195a..ba2f4b7 100644
--- a/BuildTools/SCons/SConstruct
+++ b/BuildTools/SCons/SConstruct
@@ -311,6 +311,27 @@ else :
env["LIBIDN_BUNDLED"] = 1
conf.Finish()
+# LibMiniUPnPc
+libminiupnpc_conf_env = conf_env.Clone()
+
+conf = Configure(libminiupnpc_conf_env)
+if conf.CheckCHeader("miniupnpc.h") and conf.CheckLib(env["libminiupnpc_libname"]) :
+ print "NOT IMPLEMENTED YET"
+else :
+ env["LIBMINIUPNPC_BUNDLED"] = 1
+conf.Finish()
+
+# LibNATPMP
+libnatpmp_conf_env = conf_env.Clone()
+
+conf = Configure(libnatpmp_conf_env)
+if conf.CheckCHeader("natpmp.h") and conf.CheckLib(env["libnatpmp_libname"]) :
+ print "NOT IMPLEMENTED YET"
+else :
+ env["LIBNATPMP_BUNDLED"] = 1
+conf.Finish()
+
+
# SQLite
#sqlite_conf_env = conf_env.Clone()
#sqlite_flags = {}
diff --git a/QA/Checker/IO.cpp b/QA/Checker/IO.cpp
index d9eadd6..41fe5d3 100644
--- a/QA/Checker/IO.cpp
+++ b/QA/Checker/IO.cpp
@@ -6,6 +6,7 @@
#include <QA/Checker/IO.h>
+#include <algorithm>
#include <iostream>
std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s) {
@@ -55,3 +56,10 @@ std::ostream& operator<<(std::ostream& os, const std::vector<size_t>& s) {
os << std::endl;
return os;
}
+
+bool operator==(const Swift::ByteArray& a, const Swift::ByteArray& b) {
+ if (a.size() != b.size()) {
+ return false;
+ }
+ return std::equal(a.begin(), a.end(), b.begin());
+}
diff --git a/QA/Checker/IO.h b/QA/Checker/IO.h
index 2545d24..3e40f7e 100644
--- a/QA/Checker/IO.h
+++ b/QA/Checker/IO.h
@@ -10,6 +10,7 @@
#include <Swiften/Base/SafeByteArray.h>
std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s);
+bool operator==(const Swift::ByteArray& a, const Swift::ByteArray& b);
std::ostream& operator<<(std::ostream& os, const Swift::SafeByteArray& s);
std::ostream& operator<<(std::ostream& os, const std::vector<int>& s);
std::ostream& operator<<(std::ostream& os, const std::vector<size_t>& s);
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index f4aa745..a3d9fb5 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -20,8 +20,13 @@
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swiften/Client/NickResolver.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/StatusUtil.h>
#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Base/foreach.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+
namespace Swift {
@@ -29,7 +34,7 @@ namespace Swift {
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider)
- : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider) {
+ : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider), eventStream_(eventStream) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider);
@@ -60,6 +65,10 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
chatWindow_->addSystemMessage(startMessage);
chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+ chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2));
+ chatWindow_->onFileTransferAccept.connect(boost::bind(&ChatController::handleFileTransferAccept, this, _1, _2));
+ chatWindow_->onFileTransferCancel.connect(boost::bind(&ChatController::handleFileTransferCancel, this, _1));
+ chatWindow_->onSendFileRequest.connect(boost::bind(&ChatController::handleSendFileRequest, this, _1));
handleBareJIDCapsChanged(toJID_);
}
@@ -171,6 +180,45 @@ void ChatController::setOnline(bool online) {
ChatControllerBase::setOnline(online);
}
+void ChatController::handleNewFileTransferController(FileTransferController* ftc) {
+ std::string nick = senderDisplayNameFromMessage(ftc->getOtherParty());
+ std::string ftID = ftc->setChatWindow(chatWindow_, nick);
+
+ ftControllers[ftID] = ftc;
+}
+
+void ChatController::handleFileTransferCancel(std::string id) {
+ std::cout << "handleFileTransferCancel(" << id << ")" << std::endl;
+ if (ftControllers.find(id) != ftControllers.end()) {
+ ftControllers[id]->cancel();
+ } else {
+ std::cerr << "unknown file transfer UI id" << std::endl;
+ }
+}
+
+void ChatController::handleFileTransferStart(std::string id, std::string description) {
+ std::cout << "handleFileTransferStart(" << id << ", " << description << ")" << std::endl;
+ if (ftControllers.find(id) != ftControllers.end()) {
+ ftControllers[id]->start(description);
+ } else {
+ std::cerr << "unknown file transfer UI id" << std::endl;
+ }
+}
+
+void ChatController::handleFileTransferAccept(std::string id, std::string filename) {
+ std::cout << "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
+ if (ftControllers.find(id) != ftControllers.end()) {
+ ftControllers[id]->accept(filename);
+ } else {
+ std::cerr << "unknown file transfer UI id" << std::endl;
+ }
+}
+
+void ChatController::handleSendFileRequest(std::string filename) {
+ std::cout << "ChatController::handleSendFileRequest(" << filename << ")" << std::endl;
+ eventStream_->send(boost::make_shared<SendFileUIEvent>(getToJID(), filename));
+}
+
std::string ChatController::senderDisplayNameFromMessage(const JID& from) {
return nickResolver_->jidToNick(from);
}
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index f6b8763..2531adb 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -8,12 +8,16 @@
#include "Swift/Controllers/Chat/ChatControllerBase.h"
+#include <map>
+#include <string>
+
namespace Swift {
class AvatarManager;
class ChatStateNotifier;
class ChatStateTracker;
class NickResolver;
class EntityCapsProvider;
+ class FileTransferController;
class ChatController : public ChatControllerBase {
public:
@@ -21,6 +25,7 @@ namespace Swift {
virtual ~ChatController();
virtual void setToJID(const JID& jid);
virtual void setOnline(bool online);
+ virtual void handleNewFileTransferController(FileTransferController* ftc);
private:
void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
@@ -37,6 +42,11 @@ namespace Swift {
void handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/);
void handleBareJIDCapsChanged(const JID& jid);
+ void handleFileTransferCancel(std::string /* id */);
+ void handleFileTransferStart(std::string /* id */, std::string /* description */);
+ void handleFileTransferAccept(std::string /* id */, std::string /* filename */);
+ void handleSendFileRequest(std::string filename);
+
private:
NickResolver* nickResolver_;
ChatStateNotifier* chatStateNotifier_;
@@ -47,6 +57,9 @@ namespace Swift {
std::string lastStatusChangeString_;
std::map<boost::shared_ptr<Stanza>, std::string> unackedStanzas_;
StatusShow::Type lastShownStatus_;
+ UIEventStream* eventStream_;
+
+ std::map<std::string, FileTransferController*> ftControllers;
};
}
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index d631494..c61479c 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -29,6 +29,8 @@
#include <Swiften/MUC/MUCManager.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
+#include <Swift/Controllers/FileTransfer/FileTransferController.h>
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/ProfileSettingsProvider.h>
#include <Swiften/Avatars/AvatarManager.h>
@@ -56,13 +58,15 @@ ChatsManager::ChatsManager(
EntityCapsProvider* entityCapsProvider,
MUCManager* mucManager,
MUCSearchWindowFactory* mucSearchWindowFactory,
- ProfileSettingsProvider* settings) :
+ ProfileSettingsProvider* settings,
+ FileTransferOverview* ftOverview) :
jid_(jid),
joinMUCWindowFactory_(joinMUCWindowFactory),
useDelayForLatency_(useDelayForLatency),
mucRegistry_(mucRegistry),
entityCapsProvider_(entityCapsProvider),
- mucManager(mucManager) {
+ mucManager(mucManager),
+ ftOverview_(ftOverview) {
timerFactory_ = timerFactory;
eventController_ = eventController;
stanzaChannel_ = stanzaChannel;
@@ -86,6 +90,7 @@ ChatsManager::ChatsManager(
joinMUCWindow_ = NULL;
mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings);
mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));
+ ftOverview_->onNewFileTransferController.connect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1));
setupBookmarks();
loadRecents();
}
@@ -535,6 +540,12 @@ void ChatsManager::handleMUCBookmarkActivated(const MUCBookmark& mucBookmark) {
uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(mucBookmark.getRoom(), mucBookmark.getNick()));
}
+void ChatsManager::handleNewFileTransferController(FileTransferController* ftc) {
+ ChatController* chatController = getChatControllerOrCreate(ftc->getOtherParty());
+ chatController->handleNewFileTransferController(ftc);
+ chatController->activateChatWindow();
+}
+
void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) {
if (chat.isMUC) {
uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(chat.jid, chat.nick));
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index b4db523..46c104d 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -43,10 +43,12 @@ namespace Swift {
class MUCSearchWindowFactory;
class ProfileSettingsProvider;
class MUCSearchController;
-
+ class FileTransferOverview;
+ class FileTransferController;
+
class ChatsManager {
public:
- ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings);
+ ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings, FileTransferOverview* ftOverview);
virtual ~ChatsManager();
void setAvatarManager(AvatarManager* avatarManager);
void setOnline(bool enabled);
@@ -67,6 +69,7 @@ namespace Swift {
void handleUserLeftMUC(MUCController* mucController);
void handleBookmarksReady();
void handleChatActivity(const JID& jid, const std::string& activity, bool isMUC);
+ void handleNewFileTransferController(FileTransferController*);
void appendRecent(const ChatListWindow::Chat& chat);
void prependRecent(const ChatListWindow::Chat& chat);
void setupBookmarks();
@@ -110,5 +113,6 @@ namespace Swift {
MUCSearchController* mucSearchController_;
std::list<ChatListWindow::Chat> recentChats_;
ProfileSettingsProvider* profileSettings_;
+ FileTransferOverview* ftOverview_;
};
}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index b8cb368..5339703 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -8,6 +8,8 @@
#include <cppunit/extensions/TestFactoryRegistry.h>
#include "3rdParty/hippomocks.h"
+#include <boost/bind.hpp>
+
#include "Swift/Controllers/Chat/ChatsManager.h"
#include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h"
@@ -36,11 +38,14 @@
#include "Swiften/Client/DummyStanzaChannel.h"
#include "Swiften/Queries/DummyIQChannel.h"
#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Jingle/JingleSessionManager.h"
+#include "Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h"
#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include <Swift/Controllers/ProfileSettingsProvider.h>
-
+#include "Swift/Controllers/FileTransfer/FileTransferOverview.h"
+#include <Swiften/Base/Algorithm.h>
using namespace Swift;
@@ -86,19 +91,23 @@ public:
settings_ = new DummySettingsProvider();
profileSettings_ = new ProfileSettingsProvider("a", settings_);
chatListWindow_ = new MockChatListWindow();
+ ftManager_ = new DummyFileTransferManager();
+ ftOverview_ = new FileTransferOverview(ftManager_);
mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_);
- manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_);
+ manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_);
avatarManager_ = new NullAvatarManager();
manager_->setAvatarManager(avatarManager_);
};
void tearDown() {
- //delete chatListWindowFactory_;
+ //delete chatListWindowFactory
delete settings_;
delete profileSettings_;
delete avatarManager_;
delete manager_;
+ delete ftOverview_;
+ delete ftManager_;
delete directedPresenceSender_;
delete presenceSender_;
delete presenceOracle_;
@@ -354,6 +363,8 @@ private:
DummySettingsProvider* settings_;
ProfileSettingsProvider* profileSettings_;
ChatListWindow* chatListWindow_;
+ FileTransferOverview* ftOverview_;
+ FileTransferManager* ftManager_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/FileTransfer/FileTransferController.cpp b/Swift/Controllers/FileTransfer/FileTransferController.cpp
new file mode 100644
index 0000000..afa907d
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferController.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "FileTransferController.h"
+#include "Swiften/FileTransfer/OutgoingJingleFileTransfer.h"
+#include "Swiften/FileTransfer/FileTransferManager.h"
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <boost/bind.hpp>
+#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+
+FileTransferController::FileTransferController(const JID& receipient, const std::string& filename, FileTransferManager* fileTransferManager) :
+ sending(true), otherParty(receipient), filename(filename), ftManager(fileTransferManager), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) {
+
+}
+
+FileTransferController::FileTransferController(IncomingFileTransfer::ref transfer) :
+ sending(false), otherParty(transfer->getSender()), filename(transfer->filename), transfer(transfer), ftManager(0), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) {
+
+}
+
+FileTransferController::~FileTransferController() {
+ delete ftProgressInfo;
+}
+
+const JID &FileTransferController::getOtherParty() const {
+ return otherParty;
+}
+
+std::string FileTransferController::setChatWindow(ChatWindow* wnd, std::string nickname) {
+ chatWindow = wnd;
+ if (sending) {
+ uiID = wnd->addFileTransfer("me", true, filename, boost::filesystem::file_size(boost::filesystem::path(filename)));
+ } else {
+ uiID = wnd->addFileTransfer(nickname, false, filename, transfer->fileSizeInBytes);
+ }
+ return uiID;
+}
+
+void FileTransferController::setReceipient(const JID& receipient) {
+ this->otherParty = receipient;
+}
+
+bool FileTransferController::isIncoming() const {
+ return !sending;
+}
+
+FileTransfer::State FileTransferController::getState() const {
+ return currentState;
+}
+
+int FileTransferController::getProgress() const {
+ return ftProgressInfo ? ftProgressInfo->getPercentage() : 0;
+}
+
+boost::uintmax_t FileTransferController::getSize() const {
+ if (transfer) {
+ return transfer->fileSizeInBytes;
+ } else {
+ return 0;
+ }
+}
+
+void FileTransferController::start(std::string& description) {
+ std::cout << "FileTransferController::start" << std::endl;
+ fileReadStream = boost::make_shared<FileReadBytestream>(boost::filesystem::path(filename));
+ OutgoingFileTransfer::ref outgoingTransfer = ftManager->createOutgoingFileTransfer(otherParty, boost::filesystem::path(filename), description, fileReadStream);
+ if (outgoingTransfer) {
+ ftProgressInfo = new FileTransferProgressInfo(outgoingTransfer->fileSizeInBytes);
+ ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1));
+ outgoingTransfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
+ outgoingTransfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1));
+ outgoingTransfer->start();
+ transfer = outgoingTransfer;
+ } else {
+ std::cerr << "File transfer not supported!" << std::endl;
+ }
+}
+
+void FileTransferController::accept(std::string& file) {
+ std::cout << "FileTransferController::accept" << std::endl;
+ IncomingFileTransfer::ref incomingTransfer = boost::dynamic_pointer_cast<IncomingFileTransfer>(transfer);
+ if (incomingTransfer) {
+ fileWriteStream = boost::make_shared<FileWriteBytestream>(boost::filesystem::path(file));
+
+ ftProgressInfo = new FileTransferProgressInfo(transfer->fileSizeInBytes);
+ ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1));
+ transfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
+ transfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1));
+ incomingTransfer->accept(fileWriteStream);
+ } else {
+ std::cerr << "Expected an incoming transfer in this situation!" << std::endl;
+ }
+}
+
+void FileTransferController::cancel() {
+ if (transfer) {
+ transfer->cancel();
+ } else {
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::Canceled);
+ }
+}
+
+void FileTransferController::handleFileTransferStateChange(FileTransfer::State state) {
+ currentState = state;
+ onStateChage();
+ switch(state.state) {
+ case FileTransfer::State::Negotiating:
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::Negotiating);
+ return;
+ case FileTransfer::State::Transferring:
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::Transferring);
+ return;
+ case FileTransfer::State::Canceled:
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::Canceled);
+ return;
+ case FileTransfer::State::Finished:
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::Finished);
+ if (fileWriteStream) {
+ fileWriteStream->close();
+ }
+ return;
+ case FileTransfer::State::Failed:
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::FTFailed);
+ return;
+ case FileTransfer::State::WaitingForAccept:
+ chatWindow->setFileTransferStatus(uiID, ChatWindow::WaitingForAccept);
+ return;
+ case FileTransfer::State::WaitingForStart:
+ return;
+ }
+ std::cerr << "Unhandled FileTransfer::State!" << std::endl;
+}
+
+void FileTransferController::handleProgressPercentageChange(int percentage) {
+ onProgressChange();
+ chatWindow->setFileTransferProgress(uiID, percentage);
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferController.h b/Swift/Controllers/FileTransfer/FileTransferController.h
new file mode 100644
index 0000000..5d98468
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferController.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/FileTransfer/FileWriteBytestream.h>
+#include <Swift/Controllers/FileTransfer/FileTransferProgressInfo.h>
+
+namespace Swift {
+
+class FileTransferManager;
+class ChatWindow;
+
+class FileTransferController {
+public:
+ /**
+ * For outgoing file transfers. It'll create a file transfer via FileTransferManager as soon as the descriptive information is available.
+ */
+ FileTransferController(const JID&, const std::string&, FileTransferManager*);
+
+ /**
+ * For incoming file transfers.
+ */
+ FileTransferController(IncomingFileTransfer::ref transfer);
+ ~FileTransferController();
+
+ std::string setChatWindow(ChatWindow*, std::string nickname);
+ void setReceipient(const JID& otherParty);
+
+ void start(std::string& description);
+ void accept(std::string& file);
+ void cancel();
+
+ const JID &getOtherParty() const;
+ bool isIncoming() const;
+ FileTransfer::State getState() const;
+ int getProgress() const;
+ boost::uintmax_t getSize() const;
+
+ boost::signal<void ()> onStateChage;
+ boost::signal<void ()> onProgressChange;
+
+private:
+ void handleFileTransferStateChange(FileTransfer::State);
+ void handleProgressPercentageChange(int percentage);
+
+private:
+ bool sending;
+ JID otherParty;
+ std::string filename;
+ FileTransfer::ref transfer;
+ boost::shared_ptr<FileReadBytestream> fileReadStream;
+ boost::shared_ptr<FileWriteBytestream> fileWriteStream;
+ FileTransferManager* ftManager;
+ FileTransferProgressInfo* ftProgressInfo;
+ ChatWindow* chatWindow;
+ std::string uiID;
+ FileTransfer::State currentState;
+};
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferOverview.cpp b/Swift/Controllers/FileTransfer/FileTransferOverview.cpp
new file mode 100644
index 0000000..c3ffc5c
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferOverview.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "FileTransferOverview.h"
+
+#include <boost/bind.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+
+FileTransferOverview::FileTransferOverview(FileTransferManager* ftm) : fileTransferManager(ftm) {
+ fileTransferManager->onIncomingFileTransfer.connect(boost::bind(&FileTransferOverview::handleIncomingFileTransfer, this, _1));
+}
+
+FileTransferOverview::~FileTransferOverview() {
+
+}
+
+void FileTransferOverview::sendFile(const JID& jid, const std::string& filename) {
+ FileTransferController* controller = new FileTransferController(jid, filename, fileTransferManager);
+ fileTransfers.push_back(controller);
+
+ onNewFileTransferController(controller);
+}
+
+void FileTransferOverview::handleIncomingFileTransfer(IncomingFileTransfer::ref transfer) {
+ FileTransferController* controller = new FileTransferController(transfer);
+ fileTransfers.push_back(controller);
+ onNewFileTransferController(controller);
+}
+
+const std::vector<FileTransferController*>& FileTransferOverview::getFileTransfers() const {
+ return fileTransfers;
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferOverview.h b/Swift/Controllers/FileTransfer/FileTransferOverview.h
new file mode 100644
index 0000000..716666a
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferOverview.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "Swift/Controllers/FileTransfer/FileTransferController.h"
+
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+class ChatsManager;
+class FileTransferManager;
+
+class FileTransferOverview {
+public:
+ FileTransferOverview(FileTransferManager*);
+ ~FileTransferOverview();
+
+ void sendFile(const JID&, const std::string&);
+ const std::vector<FileTransferController*>& getFileTransfers() const;
+
+ boost::signal<void (FileTransferController*)> onNewFileTransferController;
+
+private:
+ void handleIncomingFileTransfer(IncomingFileTransfer::ref transfer);
+
+private:
+ std::vector<FileTransferController*> fileTransfers;
+ FileTransferManager *fileTransferManager;
+};
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp
new file mode 100644
index 0000000..6d19fa1
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "FileTransferProgressInfo.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+FileTransferProgressInfo::FileTransferProgressInfo(boost::uintmax_t completeBytes) : completeBytes(completeBytes), completedBytes(0), percentage(0) {
+ onProgressPercentage(0);
+}
+
+void FileTransferProgressInfo::setBytesProcessed(int processedBytes) {
+ int oldPercentage = int(double(completedBytes) / double(completeBytes) * 100.0);
+ completedBytes += processedBytes;
+ int newPercentage = int(double(completedBytes) / double(completeBytes) * 100.0);
+ if (oldPercentage != newPercentage) {
+ onProgressPercentage(newPercentage);
+ }
+ percentage = newPercentage;
+}
+
+int FileTransferProgressInfo::getPercentage() const {
+ return percentage;
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h
new file mode 100644
index 0000000..bb3c0fc
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+class FileTransferProgressInfo {
+public:
+ FileTransferProgressInfo(boost::uintmax_t completeBytes);
+
+public:
+ void setBytesProcessed(int processedBytes);
+
+ int getPercentage() const;
+ boost::signal<void (int)> onProgressPercentage;
+
+private:
+ boost::uintmax_t completeBytes;
+ boost::uintmax_t completedBytes;
+ int percentage;
+};
+
+}
diff --git a/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp
new file mode 100644
index 0000000..da0606c
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamProxyFinder.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Queries/IQRouter.h>
+
+namespace Swift {
+
+SOCKS5BytestreamProxyFinder::SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter) : iqRouter(iqRouter) {
+ serviceWalker = boost::make_shared<DiscoServiceWalker>(service, iqRouter);
+ serviceWalker->onServiceFound.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleServiceFound, this, _1, _2));
+}
+
+void SOCKS5BytestreamProxyFinder::start() {
+ serviceWalker->beginWalk();
+}
+
+void SOCKS5BytestreamProxyFinder::sendBytestreamQuery(const JID& jid) {
+ S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+ boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Get, jid, proxyRequest, iqRouter);
+ request->onResponse.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleProxyResponse, this, _1, _2));
+ request->send();
+}
+
+void SOCKS5BytestreamProxyFinder::handleServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> discoInfo) {
+ if (discoInfo->hasFeature(DiscoInfo::Bytestream)) {
+ sendBytestreamQuery(jid);
+ }
+}
+
+void SOCKS5BytestreamProxyFinder::handleProxyResponse(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error) {
+ if (error) {
+ SWIFT_LOG(debug) << "ERROR" << std::endl;
+ } else {
+ if (request) {
+ onProxyFound(request);
+ } else {
+ //assert(false);
+ }
+ }
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h
new file mode 100644
index 0000000..1727a63
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swift/Controllers/DiscoServiceWalker.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+
+namespace Swift {
+
+class JID;
+class IQRouter;
+
+class SOCKS5BytestreamProxyFinder {
+public:
+ SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter);
+ void start();
+
+ boost::signal<void(boost::shared_ptr<S5BProxyRequest>)> onProxyFound;
+
+private:
+ void sendBytestreamQuery(const JID&);
+
+ void handleServiceFound(const JID&, boost::shared_ptr<DiscoInfo>);
+ void handleProxyResponse(boost::shared_ptr<S5BProxyRequest>, ErrorPayload::ref);
+private:
+ boost::shared_ptr<DiscoServiceWalker> serviceWalker;
+ IQRouter* iqRouter;
+ std::vector<boost::shared_ptr<GenericRequest<S5BProxyRequest> > > requests;
+};
+
+}
diff --git a/Swift/Controllers/FileTransferListController.cpp b/Swift/Controllers/FileTransferListController.cpp
new file mode 100644
index 0000000..093a3c4
--- /dev/null
+++ b/Swift/Controllers/FileTransferListController.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "Swift/Controllers/FileTransferListController.h"
+
+#include <boost/bind.hpp>
+
+#include "Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h"
+#include "Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h"
+
+namespace Swift {
+
+FileTransferListController::FileTransferListController(UIEventStream* uiEventStream, FileTransferListWidgetFactory* fileTransferListWidgetFactory) : fileTransferListWidgetFactory(fileTransferListWidgetFactory), fileTransferListWidget(NULL), fileTransferOverview(0) {
+ uiEventStream->onUIEvent.connect(boost::bind(&FileTransferListController::handleUIEvent, this, _1));
+}
+
+FileTransferListController::~FileTransferListController() {
+ delete fileTransferListWidget;
+}
+
+void FileTransferListController::setFileTransferOverview(FileTransferOverview *overview) {
+ fileTransferOverview = overview;
+ if (fileTransferListWidget) {
+ fileTransferListWidget->setFileTransferOverview(fileTransferOverview);
+ }
+}
+
+void FileTransferListController::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) {
+ boost::shared_ptr<RequestFileTransferListUIEvent> event = boost::dynamic_pointer_cast<RequestFileTransferListUIEvent>(rawEvent);
+ if (event != NULL) {
+ if (fileTransferListWidget == NULL) {
+ fileTransferListWidget = fileTransferListWidgetFactory->createFileTransferListWidget();
+ if (fileTransferOverview) {
+ fileTransferListWidget->setFileTransferOverview(fileTransferOverview);
+ }
+ }
+ fileTransferListWidget->show();
+ fileTransferListWidget->activate();
+ }
+}
+
+}
diff --git a/Swift/Controllers/FileTransferListController.h b/Swift/Controllers/FileTransferListController.h
new file mode 100644
index 0000000..c5c8893
--- /dev/null
+++ b/Swift/Controllers/FileTransferListController.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swift/Controllers/UIEvents/UIEventStream.h"
+
+namespace Swift {
+
+class FileTransferListWidgetFactory;
+class FileTransferListWidget;
+class FileTransferOverview;
+
+class FileTransferListController {
+public:
+ FileTransferListController(UIEventStream* uiEventStream, FileTransferListWidgetFactory* fileTransferListWidgetFactory);
+ ~FileTransferListController();
+
+ void setFileTransferOverview(FileTransferOverview* overview);
+
+private:
+ void handleUIEvent(boost::shared_ptr<UIEvent> event);
+
+private:
+ FileTransferListWidgetFactory* fileTransferListWidgetFactory;
+ FileTransferListWidget* fileTransferListWidget;
+ FileTransferOverview* fileTransferOverview;
+};
+
+}
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 24c5303..b84e7d6 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -37,6 +37,7 @@
#include "Swift/Controllers/SystemTray.h"
#include "Swift/Controllers/SystemTrayController.h"
#include "Swift/Controllers/XMLConsoleController.h"
+#include "Swift/Controllers/FileTransferListController.h"
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include "Swift/Controllers/PresenceNotifier.h"
#include "Swift/Controllers/EventNotifier.h"
@@ -68,6 +69,9 @@
#include <Swift/Controllers/XMPPURIController.h>
#include "Swift/Controllers/AdHocManager.h"
#include <SwifTools/Idle/IdleDetector.h>
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Client/ClientXMLTracer.h>
namespace Swift {
@@ -101,7 +105,8 @@ MainController::MainController(
idleDetector_(idleDetector),
loginWindow_(NULL) ,
useDelayForLatency_(useDelayForLatency),
- eagleMode_(eagleMode) {
+ eagleMode_(eagleMode),
+ ftOverview_(NULL) {
storages_ = NULL;
certificateStorage_ = NULL;
statusTracker_ = NULL;
@@ -164,6 +169,8 @@ MainController::MainController(
xmlConsoleController_ = new XMLConsoleController(uiEventStream_, uiFactory_);
+ fileTransferListController_ = new FileTransferListController(uiEventStream_, uiFactory_);
+
uiEventStream_->onUIEvent.connect(boost::bind(&MainController::handleUIEvent, this, _1));
bool enabled = settings_->getBoolSetting(SHOW_NOTIFICATIONS, true);
uiEventStream_->send(boost::shared_ptr<ToggleNotificationsUIEvent>(new ToggleNotificationsUIEvent(enabled)));
@@ -184,7 +191,7 @@ MainController::~MainController() {
eventController_->disconnectAll();
resetClient();
-
+ delete fileTransferListController_;
delete xmlConsoleController_;
delete xmppURIController_;
delete soundEventController_;
@@ -211,6 +218,10 @@ void MainController::resetClient() {
eventWindowController_ = NULL;
delete chatsManager_;
chatsManager_ = NULL;
+ delete s5bProxyFinder_;
+ s5bProxyFinder_ = NULL;
+ delete ftOverview_;
+ ftOverview_ = NULL;
delete rosterController_;
rosterController_ = NULL;
delete eventNotifier_;
@@ -270,13 +281,22 @@ void MainController::handleConnected() {
myStatusLooksOnline_ = true;
if (freshLogin) {
profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
- rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_);
+ srand(time(NULL));
+ int randomPort = 10000 + rand() % 10000;
+ client_->getFileTransferManager()->startListeningOnPort(randomPort);
+ s5bProxyFinder_ = new SOCKS5BytestreamProxyFinder(client_->getJID().getDomain(), client_->getIQRouter());
+ s5bProxyFinder_->onProxyFound.connect(boost::bind(&FileTransferManager::addS5BProxy, client_->getFileTransferManager(), _1));
+ s5bProxyFinder_->start();
+ ftOverview_ = new FileTransferOverview(client_->getFileTransferManager());
+ fileTransferListController_->setFileTransferOverview(ftOverview_);
+ rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_);
rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
contactEditController_ = new ContactEditController(rosterController_, uiFactory_, uiEventStream_);
- chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_);
+ chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_);
+
client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
chatsManager_->setAvatarManager(client_->getAvatarManager());
@@ -289,6 +309,10 @@ void MainController::handleConnected() {
discoInfo.addFeature(DiscoInfo::ChatStatesFeature);
discoInfo.addFeature(DiscoInfo::SecurityLabelsFeature);
discoInfo.addFeature(DiscoInfo::MessageCorrectionFeature);
+ discoInfo.addFeature(DiscoInfo::JingleFeature);
+ discoInfo.addFeature(DiscoInfo::JingleFTFeature);
+ discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+ discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
client_->getDiscoManager()->setCapsNode(CLIENT_NODE);
client_->getDiscoManager()->setDiscoInfo(discoInfo);
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 7ac6648..2aaa542 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -23,6 +23,8 @@
#include "Swiften/Elements/CapsInfo.h"
#include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
#include "Swift/Controllers/UIEvents/UIEvent.h"
+#include "Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h"
+#include "Swiften/Client/ClientXMLTracer.h"
namespace Swift {
class IdleDetector;
@@ -51,6 +53,7 @@ namespace Swift {
class SoundEventController;
class SoundPlayer;
class XMLConsoleController;
+ class FileTransferListController;
class UIEventStream;
class EventWindowFactory;
class EventWindowController;
@@ -65,6 +68,7 @@ namespace Swift {
class XMPPURIController;
class AdHocManager;
class AdHocCommandWindowFactory;
+ class FileTransferOverview;
class MainController {
public:
@@ -140,6 +144,7 @@ namespace Swift {
LoginWindow* loginWindow_;
UIEventStream* uiEventStream_;
XMLConsoleController* xmlConsoleController_;
+ FileTransferListController* fileTransferListController_;
ChatsManager* chatsManager_;
ProfileController* profileController_;
ContactEditController* contactEditController_;
@@ -162,5 +167,7 @@ namespace Swift {
bool quitRequested_;
static const int SecondsToWaitBeforeForceQuitting;
bool eagleMode_;
+ FileTransferOverview* ftOverview_;
+ SOCKS5BytestreamProxyFinder* s5bProxyFinder_;
};
}
diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp
index bbe81e6..6d707bb 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.cpp
+++ b/Swift/Controllers/Roster/ContactRosterItem.cpp
@@ -113,6 +113,14 @@ void ContactRosterItem::removeGroup(const std::string& group) {
groups_.erase(std::remove(groups_.begin(), groups_.end(), group), groups_.end());
}
+void ContactRosterItem::setSupportedFeatures(const std::set<Feature>& features) {
+ features_ = features;
+}
+
+bool ContactRosterItem::supportsFeature(const Feature feat) const {
+ return features_.find(feat) != features_.end();
+}
+
}
diff --git a/Swift/Controllers/Roster/ContactRosterItem.h b/Swift/Controllers/Roster/ContactRosterItem.h
index 7aa948c..67ffaa9 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.h
+++ b/Swift/Controllers/Roster/ContactRosterItem.h
@@ -13,6 +13,7 @@
#include "Swiften/Elements/Presence.h"
#include <map>
+#include <set>
#include <boost/bind.hpp>
#include "Swiften/Base/boost_bsignals.h"
#include <boost/shared_ptr.hpp>
@@ -22,6 +23,11 @@ namespace Swift {
class GroupRosterItem;
class ContactRosterItem : public RosterItem {
public:
+ enum Feature {
+ FileTransferFeature,
+ };
+
+ public:
ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent);
virtual ~ContactRosterItem();
@@ -40,6 +46,9 @@ class ContactRosterItem : public RosterItem {
/** Only used so a contact can know about the groups it's in*/
void addGroup(const std::string& group);
void removeGroup(const std::string& group);
+
+ void setSupportedFeatures(const std::set<Feature>& features);
+ bool supportsFeature(Feature feat) const;
private:
JID jid_;
JID displayJID_;
@@ -48,6 +57,7 @@ class ContactRosterItem : public RosterItem {
boost::shared_ptr<Presence> offlinePresence_;
boost::shared_ptr<Presence> shownPresence_;
std::vector<std::string> groups_;
+ std::set<Feature> features_;
};
}
diff --git a/Swift/Controllers/Roster/Roster.cpp b/Swift/Controllers/Roster/Roster.cpp
index a62a18a..f3d058e 100644
--- a/Swift/Controllers/Roster/Roster.cpp
+++ b/Swift/Controllers/Roster/Roster.cpp
@@ -17,6 +17,7 @@
#include <boost/bind.hpp>
#include <iostream>
+#include <set>
#include <deque>
namespace Swift {
@@ -60,6 +61,13 @@ GroupRosterItem* Roster::getGroup(const std::string& groupName) {
return group;
}
+void Roster::setAvailableFeatures(const JID& jid, const std::set<ContactRosterItem::Feature>& features) {
+ if (itemMap_[fullJIDMapping_ ? jid : jid.toBare()].empty()) return;
+ foreach(ContactRosterItem* item, itemMap_[fullJIDMapping_ ? jid : jid.toBare()]) {
+ item->setSupportedFeatures(features);
+ }
+}
+
void Roster::removeGroup(const std::string& group) {
root_->removeGroupChild(group);
}
@@ -74,7 +82,7 @@ void Roster::handleChildrenChanged(GroupRosterItem* item) {
void Roster::addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& groupName, const std::string& avatarPath) {
GroupRosterItem* group(getGroup(groupName));
- ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group);
+ ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group);
item->setAvatarPath(avatarPath);
group->addChild(item);
if (itemMap_[fullJIDMapping_ ? jid : jid.toBare()].size() > 0) {
diff --git a/Swift/Controllers/Roster/Roster.h b/Swift/Controllers/Roster/Roster.h
index 53161a8..2b4dd27 100644
--- a/Swift/Controllers/Roster/Roster.h
+++ b/Swift/Controllers/Roster/Roster.h
@@ -10,6 +10,7 @@
#include "Swiften/JID/JID.h"
#include "Swift/Controllers/Roster/RosterItemOperation.h"
#include "Swift/Controllers/Roster/RosterFilter.h"
+#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <vector>
#include <map>
@@ -43,6 +44,8 @@ class Roster {
boost::signal<void (GroupRosterItem*)> onGroupAdded;
boost::signal<void (RosterItem*)> onDataChanged;
GroupRosterItem* getGroup(const std::string& groupName);
+ void setAvailableFeatures(const JID& jid, const std::set<ContactRosterItem::Feature>& features);
+
private:
void handleDataChanged(RosterItem* item);
void handleChildrenChanged(GroupRosterItem* item);
diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp
index 5b61abf..66948c1 100644
--- a/Swift/Controllers/Roster/RosterController.cpp
+++ b/Swift/Controllers/Roster/RosterController.cpp
@@ -36,9 +36,14 @@
#include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h"
#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h"
#include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h"
+#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
+#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/Client/NickManager.h>
#include <Swift/Controllers/Intl.h>
#include <Swiften/Base/format.h>
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
namespace Swift {
@@ -47,8 +52,9 @@ static const std::string SHOW_OFFLINE = "showOffline";
/**
* The controller does not gain ownership of these parameters.
*/
-RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings)
- : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream) {
+RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview)
+ : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview) {
+ assert(fileTransferOverview);
iqRouter_ = iqRouter;
presenceOracle_ = presenceOracle;
subscriptionManager_ = subscriptionManager;
@@ -74,15 +80,17 @@ RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, Avata
nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
mainWindow_->setMyJID(jid);
mainWindow_->setMyNick(nickManager_->getOwnNick());
+
+ entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1));
if (settings->getBoolSetting(SHOW_OFFLINE, false)) {
uiEventStream->onUIEvent(boost::shared_ptr<UIEvent>(new ToggleShowOfflineUIEvent(true)));
}
}
-RosterController::~RosterController() {
+RosterController::~RosterController() {
nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
-
+
delete offlineFilter_;
delete expandiness_;
@@ -91,6 +99,7 @@ RosterController::~RosterController() {
delete mainWindow_;
}
delete roster_;
+
}
void RosterController::setEnabled(bool enabled) {
@@ -226,6 +235,10 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
}
}
}
+ else if (boost::shared_ptr<SendFileUIEvent> sendFileEvent = boost::dynamic_pointer_cast<SendFileUIEvent>(event)) {
+ //TODO add send file dialog to ChatView of receipient jid
+ ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename());
+ }
}
void RosterController::setContactGroups(const JID& jid, const std::vector<std::string>& groups) {
@@ -302,4 +315,15 @@ std::set<std::string> RosterController::getGroups() const {
return xmppRoster_->getGroups();
}
+void RosterController::handleOnCapsChanged(const JID& jid) {
+ DiscoInfo::ref info = entityCapsManager_->getCaps(jid);
+ if (info) {
+ std::set<ContactRosterItem::Feature> features;
+ if (info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
+ features.insert(ContactRosterItem::FileTransferFeature);
+ }
+ roster_->setAvailableFeatures(jid, features);
+ }
+}
+
}
diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h
index 0a2b818..66748ca 100644
--- a/Swift/Controllers/Roster/RosterController.h
+++ b/Swift/Controllers/Roster/RosterController.h
@@ -8,12 +8,14 @@
#include "Swiften/JID/JID.h"
#include <string>
+#include <set>
#include "Swiften/Elements/Presence.h"
#include "Swiften/Elements/ErrorPayload.h"
#include "Swiften/Elements/RosterPayload.h"
#include "Swiften/Avatars/AvatarManager.h"
#include "Swift/Controllers/UIEvents/UIEvent.h"
#include "RosterGroupExpandinessPersister.h"
+#include "Swift/Controllers/FileTransfer/FileTransferOverview.h"
#include "Swiften/Base/boost_bsignals.h"
#include <boost/shared_ptr.hpp>
@@ -35,10 +37,12 @@ namespace Swift {
class IQRouter;
class SettingsProvider;
class NickManager;
-
+ class EntityCapsProvider;
+ class FileTransferManager;
+
class RosterController {
public:
- RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings);
+ RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview);
~RosterController();
void showRosterWindow();
MainWindow* getWindow() {return mainWindow_;};
@@ -69,6 +73,7 @@ namespace Swift {
void handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload);
void applyAllPresenceTo(const JID& jid);
void handleEditProfileRequest();
+ void handleOnCapsChanged(const JID& jid);
JID myJID_;
XMPPRoster* xmppRoster_;
@@ -86,6 +91,9 @@ namespace Swift {
IQRouter* iqRouter_;
SettingsProvider* settings_;
UIEventStream* uiEventStream_;
+ EntityCapsProvider* entityCapsManager_;
+ FileTransferOverview* ftOverview_;
+
boost::bsignals::scoped_connection changeStatusConnection_;
boost::bsignals::scoped_connection signOutConnection_;
boost::bsignals::scoped_connection uiEventConnection_;
diff --git a/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp b/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp
index 3acab12..963c5cd 100644
--- a/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp
+++ b/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp
@@ -4,12 +4,12 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include <cppunit/extensions/HelperMacros.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/assign/list_of.hpp>
#include <functional>
#include <QA/Checker/IO.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
#include <Swift/Controllers/Roster/LeastCommonSubsequence.h>
using namespace Swift;
diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
index ca74dbb..fbee894 100644
--- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
@@ -31,11 +31,21 @@
#include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h"
#include "Swiften/MUC/MUCRegistry.h"
#include <Swiften/Client/DummyNickManager.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Disco/CapsProvider.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
using namespace Swift;
#define CHILDREN mainWindow_->roster->getRoot()->getChildren()
+class DummyCapsProvider : public CapsProvider {
+ DiscoInfo::ref getCaps(const std::string&) const {return DiscoInfo::ref(new DiscoInfo());}
+};
+
class RosterControllerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(RosterControllerTest);
CPPUNIT_TEST(testAdd);
@@ -66,12 +76,21 @@ class RosterControllerTest : public CppUnit::TestFixture {
uiEventStream_ = new UIEventStream();
settings_ = new DummySettingsProvider();
nickManager_ = new DummyNickManager();
- rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_);
+ capsProvider_ = new DummyCapsProvider();
+ entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_);
+ jingleSessionManager_ = new JingleSessionManager(router_);
+
+ ftManager_ = new DummyFileTransferManager();
+ ftOverview_ = new FileTransferOverview(ftManager_);
+ rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_);
mainWindow_ = mainWindowFactory_->last;
};
void tearDown() {
delete rosterController_;
+ delete ftManager_;
+ delete jingleSessionManager_;
+
delete nickManager_;
delete nickResolver_;
delete mucRegistry_;
@@ -313,6 +332,11 @@ class RosterControllerTest : public CppUnit::TestFixture {
UIEventStream* uiEventStream_;
MockMainWindow* mainWindow_;
DummySettingsProvider* settings_;
+ DummyCapsProvider* capsProvider_;
+ EntityCapsManager* entityCapsManager_;
+ JingleSessionManager* jingleSessionManager_;
+ FileTransferManager* ftManager_;
+ FileTransferOverview* ftOverview_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest);
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index 031e93a..289f055 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -31,6 +31,10 @@ if env["SCONS_STAGE"] == "build" :
"MainController.cpp",
"ProfileController.cpp",
"ContactEditController.cpp",
+ "FileTransfer/FileTransferController.cpp",
+ "FileTransfer/FileTransferOverview.cpp",
+ "FileTransfer/FileTransferProgressInfo.cpp",
+ "FileTransfer/SOCKS5BytestreamProxyFinder.cpp",
"Roster/RosterController.cpp",
"Roster/RosterGroupExpandinessPersister.cpp",
"Roster/ContactRosterItem.cpp",
@@ -42,6 +46,7 @@ if env["SCONS_STAGE"] == "build" :
"SoundEventController.cpp",
"SystemTrayController.cpp",
"XMLConsoleController.cpp",
+ "FileTransferListController.cpp",
"StatusTracker.cpp",
"PresenceNotifier.cpp",
"EventNotifier.cpp",
diff --git a/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h b/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h
new file mode 100644
index 0000000..aff6909
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+
+class RequestFileTransferListUIEvent : public UIEvent {
+};
+
+}
diff --git a/Swift/Controllers/UIEvents/SendFileUIEvent.h b/Swift/Controllers/UIEvents/SendFileUIEvent.h
new file mode 100644
index 0000000..3bfa69d
--- /dev/null
+++ b/Swift/Controllers/UIEvents/SendFileUIEvent.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swiften/JID/JID.h>
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+ class SendFileUIEvent : public UIEvent {
+ public:
+ typedef boost::shared_ptr<SendFileUIEvent> ref;
+
+ SendFileUIEvent(const JID& jid, const std::string& filename) : jid(jid), filename(filename) {
+ }
+
+ const JID& getJID() const {
+ return jid;
+ }
+
+ const std::string& getFilename() const {
+ return filename;
+ }
+
+ private:
+ JID jid;
+ std::string filename;
+ };
+}
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index faef5c8..b90efd9 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -24,12 +24,14 @@ namespace Swift {
class TabComplete;
class RosterItem;
class ContactRosterItem;
+ class FileTransferController;
class ChatWindow {
public:
enum AckState {Pending, Received, Failed};
enum Tristate {Yes, No, Maybe};
enum OccupantAction {Kick};
+ enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};
ChatWindow() {}
virtual ~ChatWindow() {};
@@ -45,6 +47,11 @@ namespace Swift {
virtual void addPresenceMessage(const std::string& message) = 0;
virtual void addErrorMessage(const std::string& message) = 0;
virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;
+
+ // File transfer related stuff
+ virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
+ virtual void setFileTransferProgress(std::string, const int percentageDone) = 0;
+ virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0;
virtual void setContactChatState(ChatState::ChatStateType state) = 0;
virtual void setName(const std::string& name) = 0;
@@ -88,6 +95,12 @@ namespace Swift {
boost::signal<void ()> onAlertButtonClicked;
boost::signal<void (ContactRosterItem*)> onOccupantSelectionChanged;
boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;
+
+ // File transfer related
+ boost::signal<void (std::string /* id */)> onFileTransferCancel;
+ boost::signal<void (std::string /* id */, std::string /* description */)> onFileTransferStart;
+ boost::signal<void (std::string /* id */, std::string /* path */)> onFileTransferAccept;
+ boost::signal<void (std::string /* path */)> onSendFileRequest;
};
}
#endif
diff --git a/Swift/Controllers/UIInterfaces/FileTransferListWidget.h b/Swift/Controllers/UIInterfaces/FileTransferListWidget.h
new file mode 100644
index 0000000..01dcfd3
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/FileTransferListWidget.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+
+class FileTransferOverview;
+
+class FileTransferListWidget {
+public:
+ virtual ~FileTransferListWidget() {}
+
+ virtual void show() = 0;
+ virtual void activate() = 0;
+
+ virtual void setFileTransferOverview(FileTransferOverview*) = 0;
+};
+
+}
diff --git a/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h b/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h
new file mode 100644
index 0000000..0b08fb3
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/FileTransferListWidget.h"
+
+namespace Swift {
+
+class FileTransferListWidgetFactory {
+public:
+ virtual ~FileTransferListWidgetFactory() {}
+
+ virtual FileTransferListWidget* createFileTransferListWidget() = 0;
+};
+
+}
diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h
index 57f55d0..cf89dab 100644
--- a/Swift/Controllers/UIInterfaces/UIFactory.h
+++ b/Swift/Controllers/UIInterfaces/UIFactory.h
@@ -18,6 +18,7 @@
#include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h>
namespace Swift {
class UIFactory :
@@ -27,12 +28,13 @@ namespace Swift {
public LoginWindowFactory,
public MainWindowFactory,
public MUCSearchWindowFactory,
- public XMLConsoleWidgetFactory,
+ public XMLConsoleWidgetFactory,
public UserSearchWindowFactory,
public JoinMUCWindowFactory,
public ProfileWindowFactory,
public ContactEditWindowFactory,
- public AdHocCommandWindowFactory {
+ public AdHocCommandWindowFactory,
+ public FileTransferListWidgetFactory {
public:
virtual ~UIFactory() {}
};
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 574248f..b410c69 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -20,6 +20,11 @@ namespace Swift {
virtual void addErrorMessage(const std::string& /*message*/) {};
virtual void addPresenceMessage(const std::string& /*message*/) {};
+ // File transfer related stuff
+ virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; };
+ virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { };
+ virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { };
+
virtual void setContactChatState(ChatState::ChatStateType /*state*/) {};
virtual void setName(const std::string& name) {name_ = name;};
virtual void show() {};
@@ -40,7 +45,7 @@ namespace Swift {
virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {};
virtual void cancelAlert() {};
virtual void setCorrectionEnabled(Tristate /*enabled*/) {}
- void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {}
+ void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
boost::signal<void ()> onClosed;
boost::signal<void ()> onAllMessagesRead;
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index 4c6a729..d51f74c 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -18,6 +18,8 @@
#include <QMessageBox>
#include <QApplication>
+#include <Swiften/Base/Log.h>
+
#include "QtWebView.h"
#include "QtChatTheme.h"
@@ -47,9 +49,11 @@ QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), f
#else
mainLayout->addWidget(webView_);
#endif
+ setAcceptDrops(true);
webPage_ = new QWebPage(this);
webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
+ webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
webView_->setPage(webPage_);
connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
@@ -151,6 +155,10 @@ QString QtChatView::getLastSentMessage() {
return lastElement_.toPlainText();
}
+void QtChatView::addToJSEnvironment(const QString& name, QObject* obj) {
+ webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj);
+}
+
void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) {
rememberScrolledToBottom();
QWebElement message = document_.findFirst("#" + id);
@@ -263,4 +271,69 @@ void QtChatView::resetView() {
connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
}
+QWebElement findDivElementWithID(QWebElement document, QString id) {
+ QWebElementCollection divs = document.findAll("div");
+ foreach(QWebElement div, divs) {
+ if (div.attribute("id") == id) {
+ return div;
+ }
+ }
+ return QWebElement();
+}
+
+void QtChatView::setFileTransferProgress(QString id, const int percentageDone) {
+ QWebElement ftElement = findDivElementWithID(document_, id);
+ if (ftElement.isNull()) {
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
+ return;
+ }
+ QWebElement progressBar = ftElement.findFirst("div.progressbar");
+ progressBar.setStyleProperty("width", QString::number(percentageDone) + "%");
+
+ QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value");
+ progressBarValue.setInnerXml(QString::number(percentageDone) + " %");
+}
+
+void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) {
+ QWebElement ftElement = findDivElementWithID(document_, id);
+ if (ftElement.isNull()) {
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << id.toStdString() << std::endl;
+ return;
+ }
+
+ QString newInnerHTML = "";
+ if (state == ChatWindow::WaitingForAccept) {
+ newInnerHTML = "Waiting for other side to accept the transfer.<br/>"
+ "<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+ }
+ if (state == ChatWindow::Negotiating) {
+ // replace with text "Negotiaging" + Cancel button
+ newInnerHTML = "Negotiating...<br/>"
+ "<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+ }
+ else if (state == ChatWindow::Transferring) {
+ // progress bar + Cancel Button
+ newInnerHTML = "<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">"
+ "<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">"
+ "<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">"
+ "0%"
+ "</div>"
+ "</div>"
+ "</div>"
+ "<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+ }
+ else if (state == ChatWindow::Canceled) {
+ newInnerHTML = "Transfer has been canceled!";
+ }
+ else if (state == ChatWindow::Finished) {
+ // text "Successful transfer"
+ newInnerHTML = "Transfer completed successfully.";
+ }
+ else if (state == ChatWindow::FTFailed) {
+ newInnerHTML = "Transfer failed.";
+ }
+
+ ftElement.setInnerXml(newInnerHTML);
+}
+
}
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index eda7e42..cf47029 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -16,6 +16,8 @@
#include "ChatSnippet.h"
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+
class QWebPage;
class QUrl;
@@ -34,6 +36,9 @@ namespace Swift {
void rememberScrolledToBottom();
void setAckXML(const QString& id, const QString& xml);
QString getLastSentMessage();
+ void addToJSEnvironment(const QString&, QObject*);
+ void setFileTransferProgress(QString id, const int percentageDone);
+ void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
signals:
void gotFocus();
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index a36bc32..238100a 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -19,8 +19,15 @@
#include "QtScaledAvatarCache.h"
#include "SwifTools/TabComplete.h"
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+#include "QtFileTransferJSBridge.h"
+
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
#include <QLabel>
+#include <QInputDialog>
#include <QApplication>
#include <QBoxLayout>
#include <QCloseEvent>
@@ -33,9 +40,12 @@
#include <QTime>
#include <QUrl>
#include <QPushButton>
+#include <QFileDialog>
+
+#include <QDebug>
namespace Swift {
-QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), eventStream_(eventStream) {
+QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), previousMessageWasFileTransfer_(false), eventStream_(eventStream) {
unreadCount_ = 0;
inputEnabled_ = true;
completer_ = NULL;
@@ -45,6 +55,8 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
updateTitleWithUnreadCount();
QtSettingsProvider settings;
+ setAcceptDrops(true);
+
alertStyleSheet_ = "background: rgb(255, 255, 153); color: black";
QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
@@ -117,15 +129,23 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));
resize(400,300);
connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
+
treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
+ fileTransferJS = new QtFileTransferJSBridge();
+ messageLog_->addToJSEnvironment("filetransfer", fileTransferJS);
+ connect(fileTransferJS, SIGNAL(setDescription(QString)), this, SLOT(handleFileTransferSetDescription(QString)));
+ connect(fileTransferJS, SIGNAL(sendRequest(QString)), this, SLOT(handleFileTransferStart(QString)));
+ connect(fileTransferJS, SIGNAL(acceptRequest(QString, QString)), this, SLOT(handleFileTransferAccept(QString, QString)));
+ connect(fileTransferJS, SIGNAL(cancel(QString)), this, SLOT(handleFileTransferCancel(QString)));
}
QtChatWindow::~QtChatWindow() {
-
+ delete fileTransferJS;
}
+
void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
}
@@ -306,6 +326,7 @@ void QtChatWindow::closeEvent(QCloseEvent* event) {
}
void QtChatWindow::convertToMUC() {
+ setAcceptDrops(false);
treeWidget_->show();
}
@@ -391,7 +412,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri
QString styleSpanEnd = style == "" ? "" : "</span>";
htmlString += styleSpanStart + messageHTML + styleSpanEnd;
- bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+ bool appendToPrevious = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
if (lastLineTracker_.getShouldMoveLastLine()) {
/* should this be queued? */
messageLog_->addLastSeenLine();
@@ -406,6 +427,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri
previousSenderName_ = P2QSTRING(senderName);
previousMessageWasSystem_ = false;
previousMessageWasPresence_ = false;
+ previousMessageWasFileTransfer_ = false;
return id;
}
@@ -431,6 +453,94 @@ std::string QtChatWindow::addAction(const std::string &message, const std::strin
return addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time);
}
+std::string formatSize(const uintmax_t bytes) {
+ static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL};
+ int power = 0;
+ double engBytes = bytes;
+ while (engBytes >= 1000) {
+ ++power;
+ engBytes = engBytes / 1000.0;
+ }
+ return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );
+}
+
+std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
+ qDebug() << "addFileTransfer";
+ std::string ft_id = id_.generateID();
+
+ std::string htmlString;
+ if (senderIsSelf) {
+ // outgoing
+ htmlString = "Send file: " + filename + " ( " + formatSize(sizeInBytes) + ") </br>" +
+ "<div id='" + ft_id + "'>" +
+ "<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" +
+ "<input id='description' type='submit' value='Set Description' onclick='filetransfer.setDescription(\"" + ft_id + "\");' />" +
+ "<input id='send' type='submit' value='Send' onclick='filetransfer.sendRequest(\"" + ft_id + "\");' />" +
+ "</div>";
+ } else {
+ // incoming
+ htmlString = "Receiving file: " + filename + " ( " + formatSize(sizeInBytes) + ") </br>" +
+ "<div id='" + ft_id + "'>" +
+ "<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" +
+ "<input id='accept' type='submit' value='Accept' onclick='filetransfer.acceptRequest(\"" + ft_id + "\", \"" + filename + "\");' />" +
+ "</div>";
+ }
+
+ //addMessage(message, senderName, senderIsSelf, boost::shared_ptr<SecurityLabel>(), "", boost::posix_time::second_clock::local_time());
+
+ bool appendToPrevious = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+ if (lastLineTracker_.getShouldMoveLastLine()) {
+ /* should this be queued? */
+ messageLog_->addLastSeenLine();
+ /* if the line is added we should break the snippet */
+ appendToPrevious = false;
+ }
+ QString qAvatarPath = "qrc:/icons/avatar.png";
+ std::string id = id_.generateID();
+ messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(QString::fromStdString(htmlString), Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+
+
+ return ft_id;
+}
+
+void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) {
+ messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone);
+}
+
+void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) {
+ messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg));
+}
+
+void QtChatWindow::handleFileTransferCancel(QString id) {
+ qDebug() << "QtChatWindow::handleFileTransferCancel(" << id << ")";
+ onFileTransferCancel(id.toStdString());
+}
+
+void QtChatWindow::handleFileTransferSetDescription(QString id) {
+ bool ok = false;
+ QString text = QInputDialog::getText(this, tr("File transfer description"),
+ tr("Description:"), QLineEdit::Normal, "", &ok);
+ if (ok) {
+ descriptions[id] = text;
+ }
+}
+
+void QtChatWindow::handleFileTransferStart(QString id) {
+ qDebug() << "QtChatWindow::handleFileTransferStart(" << id << ")";
+
+ QString text = descriptions.find(id) == descriptions.end() ? QString() : descriptions[id];
+ onFileTransferStart(id.toStdString(), text.toStdString());
+}
+
+void QtChatWindow::handleFileTransferAccept(QString id, QString filename) {
+ qDebug() << "QtChatWindow::handleFileTransferAccept(" << id << ", " << filename << ")";
+
+ QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename);
+ if (!path.isEmpty()) {
+ onFileTransferAccept(id.toStdString(), path.toStdString());
+ }
+}
+
void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
if (isWidgetSelected()) {
onAllMessagesRead();
@@ -443,6 +553,7 @@ void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
previousMessageWasSelf_ = false;
previousMessageWasSystem_ = true;
previousMessageWasPresence_ = false;
+ previousMessageWasFileTransfer_ = false;
}
void QtChatWindow::addSystemMessage(const std::string& message) {
@@ -458,6 +569,7 @@ void QtChatWindow::addSystemMessage(const std::string& message) {
previousMessageWasSelf_ = false;
previousMessageWasSystem_ = true;
previousMessageWasPresence_ = false;
+ previousMessageWasFileTransfer_ = false;
}
void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
@@ -532,6 +644,21 @@ void QtChatWindow::moveEvent(QMoveEvent*) {
emit geometryChanged();
}
+void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
+ if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
+ // TODO: check whether contact actually supports file transfer
+ event->acceptProposedAction();
+ }
+}
+
+void QtChatWindow::dropEvent(QDropEvent *event) {
+ if (event->mimeData()->urls().size() == 1) {
+ onSendFileRequest(event->mimeData()->urls().at(0).toLocalFile().toStdString());
+ } else {
+ addSystemMessage("Sending of multiple files at once isn't supported at this time.");
+ }
+}
+
void QtChatWindow::replaceLastMessage(const std::string& message) {
messageLog_->replaceLastMessage(P2QSTRING(Linkify::linkify(message)));
}
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index d38e9c6..f0c078c 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -14,6 +14,8 @@
#include "Swiften/Base/IDGenerator.h"
+#include <map>
+
class QTextEdit;
class QLineEdit;
class QComboBox;
@@ -28,6 +30,7 @@ namespace Swift {
class TreeWidget;
class QtTextEdit;
class UIEventStream;
+ class QtFileTransferJSBridge;
class QtChatWindow : public QtTabbable, public ChatWindow {
Q_OBJECT
public:
@@ -39,6 +42,11 @@ namespace Swift {
void addPresenceMessage(const std::string& message);
void addErrorMessage(const std::string& errorMessage);
void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
+ // File transfer related stuff
+ std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
+ void setFileTransferProgress(std::string id, const int percentageDone);
+ void setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg);
+
void show();
void activate();
void setUnreadMessageCount(int count);
@@ -79,6 +87,9 @@ namespace Swift {
void resizeEvent(QResizeEvent* event);
void moveEvent(QMoveEvent* event);
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dropEvent(QDropEvent *event);
+
protected:
void showEvent(QShowEvent* event);
@@ -88,6 +99,13 @@ namespace Swift {
void handleKeyPressEvent(QKeyEvent* event);
void handleSplitterMoved(int pos, int index);
void handleAlertButtonClicked();
+
+
+ void handleFileTransferCancel(QString id);
+ void handleFileTransferSetDescription(QString id);
+ void handleFileTransferStart(QString id);
+ void handleFileTransferAccept(QString id, QString filename);
+
private:
void updateTitleWithUnreadCount();
void tabComplete();
@@ -116,6 +134,7 @@ namespace Swift {
bool previousMessageWasSelf_;
bool previousMessageWasSystem_;
bool previousMessageWasPresence_;
+ bool previousMessageWasFileTransfer_;
QString previousSenderName_;
bool inputClearing_;
UIEventStream* eventStream_;
@@ -124,5 +143,8 @@ namespace Swift {
QSplitter *logRosterSplitter_;
Tristate correctionEnabled_;
QString alertStyleSheet_;
+
+ std::map<QString, QString> descriptions;
+ QtFileTransferJSBridge* fileTransferJS;
};
}
diff --git a/Swift/QtUI/QtFileTransferJSBridge.cpp b/Swift/QtUI/QtFileTransferJSBridge.cpp
new file mode 100644
index 0000000..76c1509
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferJSBridge.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFileTransferJSBridge.h"
+
+namespace Swift {
+
+QtFileTransferJSBridge::QtFileTransferJSBridge() {
+
+}
+
+QtFileTransferJSBridge::~QtFileTransferJSBridge() {
+
+}
+
+} \ No newline at end of file
diff --git a/Swift/QtUI/QtFileTransferJSBridge.h b/Swift/QtUI/QtFileTransferJSBridge.h
new file mode 100644
index 0000000..bd884e5
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferJSBridge.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QObject>
+
+#include <map>
+
+namespace Swift {
+
+class FileTransferController;
+
+class QtFileTransferJSBridge : public QObject {
+ Q_OBJECT
+public:
+ QtFileTransferJSBridge();
+ virtual ~QtFileTransferJSBridge();
+signals:
+ void discard(QString id);
+ void sendRequest(QString id);
+ void setDescription(QString id);
+ void acceptRequest(QString id, QString filename);
+ void cancel(QString id);
+};
+
+}
diff --git a/Swift/QtUI/QtFileTransferListItemModel.cpp b/Swift/QtUI/QtFileTransferListItemModel.cpp
new file mode 100644
index 0000000..1b8ec51
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListItemModel.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFileTransferListItemModel.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swift/Controllers/FileTransfer/FileTransferController.h>
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
+
+namespace Swift {
+
+extern std::string formatSize(const uintmax_t bytes);
+
+QtFileTransferListItemModel::QtFileTransferListItemModel(QObject *parent) : QAbstractItemModel(parent), fileTransferOverview(0) {
+}
+
+void QtFileTransferListItemModel::setFileTransferOverview(FileTransferOverview *overview) {
+ fileTransferOverview = overview;
+ fileTransferOverview->onNewFileTransferController.connect(boost::bind(&QtFileTransferListItemModel::handleNewFileTransferController, this, _1));
+}
+
+void QtFileTransferListItemModel::handleNewFileTransferController(FileTransferController* newController) {
+ emit layoutAboutToBeChanged();
+ emit layoutChanged();
+ dataChanged(createIndex(0,0), createIndex(fileTransferOverview->getFileTransfers().size(),4));
+ newController->onStateChage.connect(boost::bind(&QtFileTransferListItemModel::handleStateChange, this, fileTransferOverview->getFileTransfers().size() - 1));
+ newController->onProgressChange.connect(boost::bind(&QtFileTransferListItemModel::handleProgressChange, this, fileTransferOverview->getFileTransfers().size() - 1));
+}
+
+void QtFileTransferListItemModel::handleStateChange(int index) {
+ emit dataChanged(createIndex(index, 2), createIndex(index, 2));
+}
+
+void QtFileTransferListItemModel::handleProgressChange(int index) {
+ emit dataChanged(createIndex(index, 3), createIndex(index, 3));
+}
+
+QVariant QtFileTransferListItemModel::headerData(int section, Qt::Orientation /* orientation */, int role) const {
+ if (role != Qt::DisplayRole) return QVariant();
+ if (section == Direction) return QVariant("Direction");
+ if (section == OtherParty) return QVariant("Other Party");
+ if (section == State) return QVariant("State");
+ if (section == Progress) return QVariant("Progress");
+ if (section == OverallSize) return QVariant("Size");
+ return QVariant();
+}
+
+int QtFileTransferListItemModel::columnCount(const QModelIndex& /* parent */) const {
+ return NoOfColumns;
+}
+
+QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) const {
+ if (role != Qt::DisplayRole || !index.isValid() ||
+ !fileTransferOverview || static_cast<size_t>(index.row()) >= fileTransferOverview->getFileTransfers().size()) {
+ return QVariant();
+ }
+ FileTransferController* controller = fileTransferOverview->getFileTransfers().at(index.row());
+ if (index.column() == Direction) {
+ return controller->isIncoming() ? QVariant("Incoming") : QVariant("Outgoing");
+ }
+ if (index.column() == OtherParty) {
+ return QVariant(QString::fromStdString(controller->getOtherParty().toString()));
+ }
+ if (index.column() == State) {
+ FileTransfer::State state = controller->getState();
+ switch(state.state) {
+ case FileTransfer::State::WaitingForStart:
+ return QVariant("Waiting for start");
+ case FileTransfer::State::WaitingForAccept:
+ return QVariant("Waiting for other side to accept");
+ case FileTransfer::State::Negotiating:
+ return QVariant("Negotiating");
+ case FileTransfer::State::Transferring:
+ return QVariant("Transferring");
+ case FileTransfer::State::Finished:
+ return QVariant("Finished");
+ case FileTransfer::State::Failed:
+ return QVariant("Failed");
+ case FileTransfer::State::Canceled:
+ return QVariant("Canceled");
+ }
+ }
+
+ if (index.column() == Progress) {
+ return QVariant(QString::number(controller->getProgress()));
+ }
+ if (index.column() == OverallSize) {
+ return QVariant(QString::fromStdString(formatSize((controller->getSize()))));
+ }
+ return QVariant();
+}
+
+QModelIndex QtFileTransferListItemModel::parent(const QModelIndex& /* child */) const {
+ return createIndex(0,0);
+}
+
+int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const {
+ return fileTransferOverview ? fileTransferOverview->getFileTransfers().size() : 0;
+}
+
+QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const {
+ return createIndex(row, column, 0);
+}
+
+}
diff --git a/Swift/QtUI/QtFileTransferListItemModel.h b/Swift/QtUI/QtFileTransferListItemModel.h
new file mode 100644
index 0000000..1d892a5
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListItemModel.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QAbstractItemModel>
+
+namespace Swift {
+
+class FileTransferController;
+class FileTransferOverview;
+
+class QtFileTransferListItemModel : public QAbstractItemModel {
+ Q_OBJECT
+public:
+ explicit QtFileTransferListItemModel(QObject *parent = 0);
+
+ void setFileTransferOverview(FileTransferOverview*);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ int columnCount(const QModelIndex &parent) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ QModelIndex parent(const QModelIndex &child) const;
+ int rowCount(const QModelIndex &parent) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent) const;
+
+private:
+ enum Columns {
+ Direction = 0,
+ OtherParty,
+ State,
+ Progress,
+ OverallSize,
+ NoOfColumns,
+ };
+
+private:
+ void handleNewFileTransferController(FileTransferController*);
+ void handleStateChange(int index);
+ void handleProgressChange(int index);
+
+signals:
+
+public slots:
+
+private:
+ FileTransferOverview *fileTransferOverview;
+
+};
+
+}
diff --git a/Swift/QtUI/QtFileTransferListWidget.cpp b/Swift/QtUI/QtFileTransferListWidget.cpp
new file mode 100644
index 0000000..01c632f
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListWidget.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFileTransferListWidget.h"
+
+#include <Swift/QtUI/QtFileTransferListItemModel.h>
+
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QWidget>
+#include <QPushButton>
+
+namespace Swift {
+
+QtFileTransferListWidget::QtFileTransferListWidget() : fileTransferOverview(0) {
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->setSpacing(0);
+ layout->setContentsMargins(0,0,0,0);
+
+ treeView = new QTreeView(this);
+ treeView->setRootIsDecorated(false);
+ treeView->setItemsExpandable(false);
+ layout->addWidget(treeView);
+
+ itemModel = new QtFileTransferListItemModel();
+ treeView->setModel(itemModel);
+
+ QWidget* bottom = new QWidget(this);
+ layout->addWidget(bottom);
+ bottom->setAutoFillBackground(true);
+
+ QHBoxLayout* buttonLayout = new QHBoxLayout(bottom);
+ buttonLayout->setContentsMargins(10,0,20,0);
+ buttonLayout->setSpacing(0);
+
+ QPushButton* clearFinished = new QPushButton(tr("Clear Finished Transfers"), bottom);
+ clearFinished->setEnabled(false);
+ //connect(clearButton, SIGNAL(clicked()), textEdit, SLOT(clear()));
+ buttonLayout->addWidget(clearFinished);
+
+ setWindowTitle(tr("File Transfer List"));
+ emit titleUpdated();
+}
+
+QtFileTransferListWidget::~QtFileTransferListWidget() {
+ delete itemModel;
+}
+
+void QtFileTransferListWidget::showEvent(QShowEvent* event) {
+ emit windowOpening();
+ emit titleUpdated(); /* This just needs to be somewhere after construction */
+ QWidget::showEvent(event);
+}
+
+void QtFileTransferListWidget::show() {
+ QWidget::show();
+ emit windowOpening();
+}
+
+void QtFileTransferListWidget::activate() {
+ emit wantsToActivate();
+}
+
+void QtFileTransferListWidget::setFileTransferOverview(FileTransferOverview *overview) {
+ fileTransferOverview = overview;
+ itemModel->setFileTransferOverview(overview);
+}
+
+void QtFileTransferListWidget::closeEvent(QCloseEvent* event) {
+ emit windowClosing();
+ event->accept();
+}
+
+}
diff --git a/Swift/QtUI/QtFileTransferListWidget.h b/Swift/QtUI/QtFileTransferListWidget.h
new file mode 100644
index 0000000..c828d4e
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListWidget.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/FileTransferListWidget.h"
+
+#include "QtTabbable.h"
+
+#include <QCloseEvent>
+#include <QShowEvent>
+#include <QTreeView>
+
+namespace Swift {
+
+class FileTransferOverview;
+class QtFileTransferListItemModel;
+
+class QtFileTransferListWidget : public QtTabbable, public FileTransferListWidget {
+ Q_OBJECT
+
+public:
+ QtFileTransferListWidget();
+ virtual ~QtFileTransferListWidget();
+
+ void show();
+ void activate();
+
+ void setFileTransferOverview(FileTransferOverview *);
+
+private:
+ virtual void closeEvent(QCloseEvent* event);
+ virtual void showEvent(QShowEvent* event);
+
+private:
+ QTreeView* treeView;
+
+ QtFileTransferListItemModel* itemModel;
+ FileTransferOverview* fileTransferOverview;
+};
+
+}
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
index 475863c..543af65 100644
--- a/Swift/QtUI/QtLoginWindow.cpp
+++ b/Swift/QtUI/QtLoginWindow.cpp
@@ -7,6 +7,7 @@
#include "QtLoginWindow.h"
#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
#include <algorithm>
#include <cassert>
@@ -28,6 +29,7 @@
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include "Swift/Controllers/UIEvents/RequestXMLConsoleUIEvent.h"
+#include "Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h"
#include "Swift/Controllers/UIEvents/ToggleSoundsUIEvent.h"
#include "Swift/Controllers/UIEvents/ToggleNotificationsUIEvent.h"
#include "Swiften/Base/Platform.h"
@@ -166,6 +168,10 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow(), forg
connect(xmlConsoleAction_, SIGNAL(triggered()), SLOT(handleShowXMLConsole()));
generalMenu_->addAction(xmlConsoleAction_);
+ fileTransferOverviewAction_ = new QAction(tr("Show &File Transfer Overview"), this);
+ connect(fileTransferOverviewAction_, SIGNAL(triggered()), SLOT(handleShowFileTransferOverview()));
+ generalMenu_->addAction(fileTransferOverviewAction_);
+
toggleSoundsAction_ = new QAction(tr("&Play Sounds"), this);
toggleSoundsAction_->setCheckable(true);
toggleSoundsAction_->setChecked(true);
@@ -356,6 +362,10 @@ void QtLoginWindow::handleShowXMLConsole() {
uiEventStream_->send(boost::shared_ptr<RequestXMLConsoleUIEvent>(new RequestXMLConsoleUIEvent()));
}
+void QtLoginWindow::handleShowFileTransferOverview() {
+ uiEventStream_->send(boost::make_shared<RequestFileTransferListUIEvent>());
+}
+
void QtLoginWindow::handleToggleSounds(bool enabled) {
uiEventStream_->send(boost::shared_ptr<ToggleSoundsUIEvent>(new ToggleSoundsUIEvent(enabled)));
}
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
index 4628af7..414bb20 100644
--- a/Swift/QtUI/QtLoginWindow.h
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -51,6 +51,7 @@ namespace Swift {
void handleCertficateChecked(bool);
void handleQuit();
void handleShowXMLConsole();
+ void handleShowFileTransferOverview();
void handleToggleSounds(bool enabled);
void handleToggleNotifications(bool enabled);
void handleAbout();
@@ -88,5 +89,6 @@ namespace Swift {
QPointer<QtAboutWidget> aboutDialog_;
bool forgetful_;
QAction* xmlConsoleAction_;
+ QAction* fileTransferOverviewAction_;
};
}
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index bd936d4..9de700c 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -24,6 +24,7 @@
#include "QtProfileWindow.h"
#include "QtContactEditWindow.h"
#include "QtAdHocCommandWindow.h"
+#include "QtFileTransferListWidget.h"
#define CHATWINDOW_FONT_SIZE "chatWindowFontSize"
@@ -43,6 +44,15 @@ XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() {
return widget;
}
+FileTransferListWidget* QtUIFactory::createFileTransferListWidget() {
+ QtFileTransferListWidget* widget = new QtFileTransferListWidget();
+ tabs->addTab(widget);
+ if (!tabs->isVisible()) {
+ tabs->show();
+ }
+ widget->show();
+ return widget;
+}
MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) {
lastMainWindow = new QtMainWindow(settings, eventStream);
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index e7ea6c6..8fc5395 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -39,6 +39,7 @@ namespace Swift {
virtual JoinMUCWindow* createJoinMUCWindow(UIEventStream* uiEventStream);
virtual ProfileWindow* createProfileWindow();
virtual ContactEditWindow* createContactEditWindow();
+ virtual FileTransferListWidget* createFileTransferListWidget();
virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);
private slots:
diff --git a/Swift/QtUI/QtWebView.cpp b/Swift/QtUI/QtWebView.cpp
index 5f9539e..d849ce7 100644
--- a/Swift/QtUI/QtWebView.cpp
+++ b/Swift/QtUI/QtWebView.cpp
@@ -45,7 +45,9 @@ void QtWebView::setFontSizeIsMinimal(bool minimum) {
void QtWebView::contextMenuEvent(QContextMenuEvent* ev) {
// Filter out the relevant actions from the standard actions
+
QMenu* menu = page()->createStandardContextMenu();
+ /*
QList<QAction*> actions(menu->actions());
for (int i = 0; i < actions.size(); ++i) {
QAction* action = actions.at(i);
@@ -59,7 +61,7 @@ void QtWebView::contextMenuEvent(QContextMenuEvent* ev) {
if (removeAction) {
menu->removeAction(action);
}
- }
+ }*/
// Add our own custom actions
menu->addAction(tr("Clear"), this, SIGNAL(clearRequested()));
diff --git a/Swift/QtUI/QtXMLConsoleWidget.cpp b/Swift/QtUI/QtXMLConsoleWidget.cpp
index b0c0385..0c88ce6 100644
--- a/Swift/QtUI/QtXMLConsoleWidget.cpp
+++ b/Swift/QtUI/QtXMLConsoleWidget.cpp
@@ -6,6 +6,8 @@
#include "QtXMLConsoleWidget.h"
+#include <Swiften/Client/XMLBeautifier.h>
+
#include <QCloseEvent>
#include <QTextEdit>
#include <QVBoxLayout>
@@ -49,6 +51,11 @@ QtXMLConsoleWidget::QtXMLConsoleWidget() {
setWindowTitle(tr("Debug Console"));
emit titleUpdated();
+ beautifier = new XMLBeautifier(true, false);
+}
+
+QtXMLConsoleWidget::~QtXMLConsoleWidget() {
+ delete beautifier;
}
void QtXMLConsoleWidget::showEvent(QShowEvent* event) {
@@ -72,11 +79,11 @@ void QtXMLConsoleWidget::closeEvent(QCloseEvent* event) {
}
void QtXMLConsoleWidget::handleDataRead(const SafeByteArray& data) {
- appendTextIfEnabled(std::string(tr("<!-- IN -->").toUtf8()) + "\n" + safeByteArrayToString(data) + "\n", QColor(33,98,33));
+ appendTextIfEnabled(std::string(tr("<!-- IN -->").toUtf8()) + "\n" + beautifier->beautify(safeByteArrayToString(data)) + "\n", QColor(33,98,33));
}
void QtXMLConsoleWidget::handleDataWritten(const SafeByteArray& data) {
- appendTextIfEnabled(std::string(tr("<!-- OUT -->").toUtf8()) + "\n" + safeByteArrayToString(data) + "\n", QColor(155,1,0));
+ appendTextIfEnabled(std::string(tr("<!-- OUT -->").toUtf8()) + "\n" + beautifier->beautify(safeByteArrayToString(data)) + "\n", QColor(155,1,0));
}
void QtXMLConsoleWidget::appendTextIfEnabled(const std::string& data, const QColor& color) {
diff --git a/Swift/QtUI/QtXMLConsoleWidget.h b/Swift/QtUI/QtXMLConsoleWidget.h
index 73a3bad..c22251f 100644
--- a/Swift/QtUI/QtXMLConsoleWidget.h
+++ b/Swift/QtUI/QtXMLConsoleWidget.h
@@ -14,11 +14,14 @@ class QCheckBox;
class QColor;
namespace Swift {
+ class XMLBeautifier;
+
class QtXMLConsoleWidget : public QtTabbable, public XMLConsoleWidget {
Q_OBJECT
public:
QtXMLConsoleWidget();
+ ~QtXMLConsoleWidget();
void show();
void activate();
@@ -35,5 +38,6 @@ namespace Swift {
private:
QTextEdit* textEdit;
QCheckBox* enabled;
+ XMLBeautifier* beautifier;
};
}
diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp
index 923f977..f3b0441 100644
--- a/Swift/QtUI/Roster/QtRosterWidget.cpp
+++ b/Swift/QtUI/Roster/QtRosterWidget.cpp
@@ -9,10 +9,12 @@
#include <QContextMenuEvent>
#include <QMenu>
#include <QInputDialog>
+#include <QFileDialog>
#include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h"
#include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h"
#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h"
+#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
#include "QtContactEditWindow.h"
#include "Swift/Controllers/Roster/ContactRosterItem.h"
#include "Swift/Controllers/Roster/GroupRosterItem.h"
@@ -54,6 +56,10 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
QAction* editContact = contextMenu.addAction(tr("Edit"));
QAction* removeContact = contextMenu.addAction(tr("Remove"));
+ QAction* sendFile = NULL;
+ if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
+ sendFile = contextMenu.addAction(tr("Send File"));
+ }
QAction* result = contextMenu.exec(event->globalPos());
if (result == editContact) {
eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID()));
@@ -63,6 +69,12 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
eventStream_->send(boost::make_shared<RemoveRosterItemUIEvent>(contact->getJID()));
}
}
+ else if (result == sendFile) {
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;"));
+ if (!fileName.isEmpty()) {
+ eventStream_->send(boost::make_shared<SendFileUIEvent>(contact->getJID(), fileName.toStdString()));
+ }
+ }
}
else if (GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item)) {
QAction* renameGroupAction = contextMenu.addAction(tr("Rename"));
diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp
index 96a078b..79dd6a2 100644
--- a/Swift/QtUI/Roster/QtTreeWidget.cpp
+++ b/Swift/QtUI/Roster/QtTreeWidget.cpp
@@ -8,11 +8,14 @@
#include <boost/smart_ptr/make_shared.hpp>
+#include <QUrl>
+
#include "Swiften/Base/Platform.h"
#include "Swift/Controllers/Roster/ContactRosterItem.h"
#include "Swift/Controllers/Roster/GroupRosterItem.h"
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
+#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
#include "QtSwiftUtil.h"
namespace Swift {
@@ -31,6 +34,7 @@ QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, QWidget* parent) : QTreeV
expandAll();
setAnimated(true);
setIndentation(0);
+ setAcceptDrops(true);
setRootIsDecorated(true);
connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&)));
connect(model_, SIGNAL(itemExpanded(const QModelIndex&, bool)), this, SLOT(handleModelItemExpanded(const QModelIndex&, bool)));
@@ -104,6 +108,41 @@ void QtTreeWidget::handleItemActivated(const QModelIndex& index) {
}
}
+void QtTreeWidget::dragEnterEvent(QDragEnterEvent *event) {
+ if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
+ event->acceptProposedAction();
+ }
+}
+
+void QtTreeWidget::dropEvent(QDropEvent *event) {
+ QModelIndex index = indexAt(event->pos());
+ if (index.isValid()) {
+ RosterItem* item = static_cast<RosterItem*>(index.internalPointer());
+ if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
+ if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
+ QString filename = event->mimeData()->urls().at(0).toLocalFile();
+ if (!filename.isEmpty()) {
+ eventStream_->send(boost::make_shared<SendFileUIEvent>(contact->getJID(), filename.toStdString()));
+ }
+ }
+ }
+ }
+}
+
+void QtTreeWidget::dragMoveEvent(QDragMoveEvent* event) {
+ QModelIndex index = indexAt(event->pos());
+ if (index.isValid()) {
+ RosterItem* item = static_cast<RosterItem*>(index.internalPointer());
+ if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
+ if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
+ event->accept();
+ return;
+ }
+ }
+ }
+ event->ignore();
+}
+
void QtTreeWidget::handleExpanded(const QModelIndex& index) {
GroupRosterItem* item = dynamic_cast<GroupRosterItem*>(static_cast<RosterItem*>(index.internalPointer()));
if (item) {
diff --git a/Swift/QtUI/Roster/QtTreeWidget.h b/Swift/QtUI/Roster/QtTreeWidget.h
index 1ad56d6..271fbd5 100644
--- a/Swift/QtUI/Roster/QtTreeWidget.h
+++ b/Swift/QtUI/Roster/QtTreeWidget.h
@@ -8,6 +8,9 @@
#include <QTreeView>
#include <QModelIndex>
+#include <QDragEnterEvent>
+#include <QDropEvent>
+#include <QDragMoveEvent>
#include "Swift/QtUI/Roster/RosterModel.h"
#include "Swift/QtUI/Roster/RosterDelegate.h"
@@ -31,6 +34,10 @@ class QtTreeWidget : public QTreeView{
void handleExpanded(const QModelIndex&);
void handleCollapsed(const QModelIndex&);
void handleClicked(const QModelIndex&);
+ protected:
+ void dragEnterEvent(QDragEnterEvent* event);
+ void dropEvent(QDropEvent* event);
+ void dragMoveEvent(QDragMoveEvent* event);
protected:
QModelIndexList getSelectedIndexes() const;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 33450ed..1d7ab78 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -84,6 +84,8 @@ sources = [
"QtTabWidget.cpp",
"QtTextEdit.cpp",
"QtXMLConsoleWidget.cpp",
+ "QtFileTransferListWidget.cpp",
+ "QtFileTransferListItemModel.cpp",
"QtAdHocCommandWindow.cpp",
"QtUtilities.cpp",
"QtBookmarkDetailWindow.cpp",
@@ -133,6 +135,7 @@ sources = [
"QtWebView.cpp",
"qrc_DefaultTheme.cc",
"qrc_Swift.cc",
+ "QtFileTransferJSBridge.cpp",
]
myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index c53bcaf..e95e704 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -26,6 +26,9 @@
#include <Swiften/TLS/BlindCertificateTrustChecker.h>
#include <Swiften/Client/NickManagerImpl.h>
#include <Swiften/Client/ClientSession.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Network/NetworkFactories.h>
+#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
namespace Swift {
@@ -59,9 +62,15 @@ Client::Client(const JID& jid, const SafeString& password, NetworkFactories* net
nickResolver = new NickResolver(jid.toBare(), roster, vcardManager, mucRegistry);
blindCertificateTrustChecker = new BlindCertificateTrustChecker();
+
+ jingleSessionManager = new JingleSessionManager(getIQRouter());
+ fileTransferManager = NULL;
}
Client::~Client() {
+ delete fileTransferManager;
+ delete jingleSessionManager;
+
delete blindCertificateTrustChecker;
delete nickResolver;
@@ -98,6 +107,10 @@ void Client::setSoftwareVersion(const std::string& name, const std::string& vers
softwareVersionResponder->setVersion(name, version, os);
}
+void Client::handleConnected() {
+ fileTransferManager = new FileTransferManagerImpl(getJID(), jingleSessionManager, getIQRouter(), getEntityCapsProvider(), presenceOracle, getNetworkFactories()->getConnectionFactory(), getNetworkFactories()->getConnectionServerFactory(), getNetworkFactories()->getTimerFactory(), getNetworkFactories()->getPlatformNATTraversalWorker());
+}
+
void Client::requestRoster() {
// FIXME: We should set this once when the session is finished, but there
// is currently no callback for this
@@ -139,4 +152,8 @@ NickManager* Client::getNickManager() const {
return nickManager;
}
+FileTransferManager* Client::getFileTransferManager() const {
+ return fileTransferManager;
+}
+
}
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index 08289a5..7269f10 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -33,6 +33,9 @@ namespace Swift {
class SubscriptionManager;
class ClientDiscoManager;
class NickManager;
+ class FileTransferManager;
+ class JingleSessionManager;
+ class FileTransferManagerImpl;
/**
* Provides the core functionality for writing XMPP client software.
@@ -131,6 +134,11 @@ namespace Swift {
ClientDiscoManager* getDiscoManager() const {
return discoManager;
}
+
+ /**
+ * Returns a FileTransferManager for the client. This is only available after the onConnected signal has been fired.
+ */
+ FileTransferManager* getFileTransferManager() const;
/**
* Configures the client to always trust a non-validating
@@ -149,6 +157,9 @@ namespace Swift {
private:
Storages* getStorages() const;
+ protected:
+ void handleConnected();
+
private:
Storages* storages;
MemoryStorages* memoryStorages;
@@ -168,6 +179,8 @@ namespace Swift {
SubscriptionManager* subscriptionManager;
MUCManager* mucManager;
ClientDiscoManager* discoManager;
+ JingleSessionManager* jingleSessionManager;
+ FileTransferManagerImpl* fileTransferManager;
BlindCertificateTrustChecker* blindCertificateTrustChecker;
};
}
diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp
index c1e398d..c1093eb 100644
--- a/Swiften/Client/ClientXMLTracer.cpp
+++ b/Swiften/Client/ClientXMLTracer.cpp
@@ -12,13 +12,18 @@
namespace Swift {
ClientXMLTracer::ClientXMLTracer(CoreClient* client) {
- client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, '<', _1));
- client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, '>', _1));
+ beautifier = new XMLBeautifier(true, true);
+ client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1));
+ client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1));
+}
+
+ClientXMLTracer::~ClientXMLTracer() {
+ delete beautifier;
}
void ClientXMLTracer::printData(char direction, const SafeByteArray& data) {
printLine(direction);
- std::cerr << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl;
+ std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl;
}
void ClientXMLTracer::printLine(char c) {
diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h
index dd94e0e..0752faa 100644
--- a/Swiften/Client/ClientXMLTracer.h
+++ b/Swiften/Client/ClientXMLTracer.h
@@ -7,15 +7,19 @@
#pragma once
#include <Swiften/Client/CoreClient.h>
+#include <Swiften/Client/XMLBeautifier.h>
#include <Swiften/Base/SafeByteArray.h>
namespace Swift {
class ClientXMLTracer {
public:
ClientXMLTracer(CoreClient* client);
+ ~ClientXMLTracer();
+ private:
+ void printData(char direction, const SafeByteArray& data);
+ void printLine(char c);
private:
- static void printData(char direction, const SafeByteArray& data);
- static void printLine(char c);
+ XMLBeautifier *beautifier;
};
}
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index cceec74..37055e4 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -276,6 +276,7 @@ void CoreClient::handleDataWritten(const SafeByteArray& data) {
void CoreClient::handleStanzaChannelAvailableChanged(bool available) {
if (available) {
+ handleConnected();
onConnected();
}
}
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 16813de..3472e76 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -188,6 +188,15 @@ namespace Swift {
return session_;
}
+ NetworkFactories* getNetworkFactories() const {
+ return networkFactories;
+ }
+
+ /**
+ * Called before onConnected signal is emmitted.
+ */
+ virtual void handleConnected() {};
+
private:
void handleConnectorFinished(boost::shared_ptr<Connection>);
void handleStanzaChannelAvailableChanged(bool available);
diff --git a/Swiften/Client/XMLBeautifier.cpp b/Swiften/Client/XMLBeautifier.cpp
new file mode 100644
index 0000000..b70fc48
--- /dev/null
+++ b/Swiften/Client/XMLBeautifier.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <sstream>
+#include <stack>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/XMLBeautifier.h>
+#include <Swiften/Parser/PlatformXMLParserFactory.h>
+
+namespace Swift {
+
+XMLBeautifier::XMLBeautifier(bool indention, bool coloring) : doIndention(indention), doColoring(coloring), intLevel(0), parser(NULL), lastWasStepDown(false) {
+ factory = new PlatformXMLParserFactory();
+}
+
+XMLBeautifier::~XMLBeautifier() {
+ delete factory;
+}
+
+std::string XMLBeautifier::beautify(const std::string &text) {
+ parser = factory->createXMLParser(this);
+ intLevel = 0;
+ buffer.str(std::string());
+ parser->parse(text);
+ delete parser;
+ return buffer.str();
+}
+
+void XMLBeautifier::indent() {
+ for (int i = 0; i < intLevel; ++i) {
+ buffer << " ";
+ }
+}
+
+// all bold but reset
+const char colorBlue[] = "\x1b[01;34m";
+const char colorCyan[] = "\x1b[01;36m";
+const char colorGreen[] = "\x1b[01;32m";
+const char colorMagenta[] = "\x1b[01;35m";
+const char colorRed[] = "\x1b[01;31m";
+const char colorReset[] = "\x1b[0m";
+const char colorYellow[] = "\x1b[01;33m";
+
+
+
+std::string XMLBeautifier::styleTag(const std::string& text) const {
+ std::string result;
+ result += colorYellow;
+ result += text;
+ result += colorReset;
+ return result;
+}
+
+std::string XMLBeautifier::styleNamespace(const std::string& text) const {
+ std::string result;
+ result += colorRed;
+ result += text;
+ result += colorReset;
+ return result;
+}
+
+std::string XMLBeautifier::styleAttribute(const std::string& text) const {
+ std::string result;
+ result += colorGreen;
+ result += text;
+ result += colorReset;
+ return result;
+}
+std::string XMLBeautifier::styleValue(const std::string& text) const {
+ std::string result;
+ result += colorCyan;
+ result += text;
+ result += colorReset;
+ return result;
+}
+
+void XMLBeautifier::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+ if (doIndention) {
+ if (intLevel) buffer << std::endl;
+ }
+ indent();
+ buffer << "<" << (doColoring ? styleTag(element) : element);
+ if (!ns.empty() && (!parentNSs.empty() && parentNSs.top() != ns)) {
+ buffer << " ";
+ buffer << (doColoring ? styleAttribute("xmlns") : "xmlns");
+ buffer << "=";
+ buffer << "\"" << (doColoring ? styleNamespace(ns) : ns) << "\"";
+ }
+ if (!attributes.getEntries().empty()) {
+ foreach(AttributeMap::Entry entry, attributes.getEntries()) {
+ buffer << " ";
+ buffer << (doColoring ? styleAttribute(entry.getAttribute().getName()) : entry.getAttribute().getName());
+ buffer << "=";
+ buffer << "\"" << (doColoring ? styleValue(entry.getValue()) : entry.getValue()) << "\"";
+ }
+ }
+ buffer << ">";
+ ++intLevel;
+ lastWasStepDown = false;
+ parentNSs.push(ns);
+}
+
+void XMLBeautifier::handleEndElement(const std::string& element, const std::string& /* ns */) {
+ --intLevel;
+ parentNSs.pop();
+ if (/*hadCDATA.top() ||*/ lastWasStepDown) {
+ if (doIndention) {
+ buffer << std::endl;
+ }
+ indent();
+ }
+ buffer << "</" << (doColoring ? styleTag(element) : element) << ">";
+ lastWasStepDown = true;
+}
+
+void XMLBeautifier::handleCharacterData(const std::string& data) {
+ buffer << data;
+ lastWasStepDown = false;
+}
+
+}
diff --git a/Swiften/Client/XMLBeautifier.h b/Swiften/Client/XMLBeautifier.h
new file mode 100644
index 0000000..44dfd20
--- /dev/null
+++ b/Swiften/Client/XMLBeautifier.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <stack>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Parser/XMLParserFactory.h>
+#include <Swiften/Parser/XMLParserClient.h>
+#include <Swiften/Parser/XMLParser.h>
+
+namespace Swift {
+
+class XMLBeautifier : public XMLParserClient {
+public:
+ XMLBeautifier(bool indention, bool coloring);
+ virtual ~XMLBeautifier();
+
+ std::string beautify(const std::string&);
+
+private:
+ void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes);
+ void handleEndElement(const std::string& element, const std::string& ns);
+ void handleCharacterData(const std::string& data);
+
+private:
+ void indent();
+
+private:
+ std::string styleTag(const std::string& text) const;
+ std::string styleNamespace(const std::string& text) const;
+ std::string styleAttribute(const std::string& text) const;
+ std::string styleValue(const std::string& text) const;
+
+private:
+ bool doIndention;
+ bool doColoring;
+
+ int intLevel;
+ std::string inputBuffer;
+ std::stringstream buffer;
+ XMLParserFactory* factory;
+ XMLParser* parser;
+
+ bool lastWasStepDown;
+ std::stack<std::string> parentNSs;
+};
+}
diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp
index f54b6bf..35d4d04 100644
--- a/Swiften/Elements/DiscoInfo.cpp
+++ b/Swiften/Elements/DiscoInfo.cpp
@@ -16,7 +16,11 @@ const std::string DiscoInfo::SecurityLabelsCatalogFeature = std::string("urn:xmp
const std::string DiscoInfo::JabberSearchFeature = std::string("jabber:iq:search");
const std::string DiscoInfo::CommandsFeature = std::string("http://jabber.org/protocol/commands");
const std::string DiscoInfo::MessageCorrectionFeature = std::string("urn:xmpp:message-correct:0");
-
+const std::string DiscoInfo::JingleFeature = std::string("urn:xmpp:jingle:1");
+const std::string DiscoInfo::JingleFTFeature = std::string("urn:xmpp:jingle:apps:file-transfer:3");
+const std::string DiscoInfo::JingleTransportsIBBFeature = std::string("urn:xmpp:jingle:transports:ibb:1");
+const std::string DiscoInfo::JingleTransportsS5BFeature = std::string("urn:xmpp:jingle:transports:s5b:1");
+const std::string DiscoInfo::Bytestream = std::string("http://jabber.org/protocol/bytestreams");
bool DiscoInfo::Identity::operator<(const Identity& other) const {
diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h
index c5c9e1c..6d6e722 100644
--- a/Swiften/Elements/DiscoInfo.h
+++ b/Swiften/Elements/DiscoInfo.h
@@ -23,6 +23,11 @@ namespace Swift {
static const std::string JabberSearchFeature;
static const std::string CommandsFeature;
static const std::string MessageCorrectionFeature;
+ static const std::string JingleFeature;
+ static const std::string JingleFTFeature;
+ static const std::string JingleTransportsIBBFeature;
+ static const std::string JingleTransportsS5BFeature;
+ static const std::string Bytestream;
class Identity {
public:
diff --git a/Swiften/Elements/JingleContentPayload.h b/Swiften/Elements/JingleContentPayload.h
index c44a806..183b8eb 100644
--- a/Swiften/Elements/JingleContentPayload.h
+++ b/Swiften/Elements/JingleContentPayload.h
@@ -21,6 +21,7 @@ namespace Swift {
typedef boost::shared_ptr<JingleContentPayload> ref;
enum Creator {
+ UnknownCreator,
InitiatorCreator,
ResponderCreator,
};
diff --git a/Swiften/Elements/JingleFileTransferDescription.h b/Swiften/Elements/JingleFileTransferDescription.h
index 19644bd..04f3f1f 100644
--- a/Swiften/Elements/JingleFileTransferDescription.h
+++ b/Swiften/Elements/JingleFileTransferDescription.h
@@ -7,7 +7,7 @@
#pragma once
#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
+#include <vector>
#include <Swiften/Elements/JingleDescription.h>
#include <Swiften/Elements/StreamInitiationFileInfo.h>
@@ -17,15 +17,25 @@ namespace Swift {
public:
typedef boost::shared_ptr<JingleFileTransferDescription> ref;
- void setOffer(const StreamInitiationFileInfo& offer) {
- this->offer = offer;
+ void addOffer(const StreamInitiationFileInfo& offer) {
+ offers.push_back(offer);
}
+
- const boost::optional<StreamInitiationFileInfo>& getOffer() const {
- return offer;
+ const std::vector<StreamInitiationFileInfo>& getOffers() const {
+ return offers;
+ }
+
+ void addRequest(const StreamInitiationFileInfo& request) {
+ reqeusts.push_back(request);
+ }
+
+ const std::vector<StreamInitiationFileInfo>& getRequests() const {
+ return reqeusts;
}
private:
- boost::optional<StreamInitiationFileInfo> offer;
+ std::vector<StreamInitiationFileInfo> offers;
+ std::vector<StreamInitiationFileInfo> reqeusts;
};
}
diff --git a/Swiften/Elements/JingleFileTransferHash.h b/Swiften/Elements/JingleFileTransferHash.h
new file mode 100644
index 0000000..5603531
--- /dev/null
+++ b/Swiften/Elements/JingleFileTransferHash.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+#include <Swiften/Elements/JingleDescription.h>
+
+namespace Swift {
+
+class JingleFileTransferHash : public Payload {
+public:
+ typedef std::map<std::string, std::string> HashesMap;
+public:
+ typedef boost::shared_ptr<JingleFileTransferHash> ref;
+
+ void setHash(const std::string& algo, const std::string& hash) {
+ hashes[algo] = hash;
+ }
+
+ const HashesMap& getHashes() const {
+ return hashes;
+ }
+
+private:
+ HashesMap hashes;
+};
+
+}
diff --git a/Swiften/Elements/JingleFileTransferReceived.h b/Swiften/Elements/JingleFileTransferReceived.h
new file mode 100644
index 0000000..75c95d9
--- /dev/null
+++ b/Swiften/Elements/JingleFileTransferReceived.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+
+class JingleFileTransferReceived : public Payload {
+ public:
+ typedef boost::shared_ptr<JingleFileTransferReceived> ref;
+
+ void setFileInfo(const StreamInitiationFileInfo& fileInfo) {
+ this->fileInfo = fileInfo;
+ }
+
+ const StreamInitiationFileInfo& getFileInfo() const {
+ return this->fileInfo;
+ }
+ private:
+ StreamInitiationFileInfo fileInfo;
+
+};
+
+}
diff --git a/Swiften/Elements/JingleIBBTransportPayload.h b/Swiften/Elements/JingleIBBTransportPayload.h
index 67aab09..8c174f0 100644
--- a/Swiften/Elements/JingleIBBTransportPayload.h
+++ b/Swiften/Elements/JingleIBBTransportPayload.h
@@ -29,14 +29,6 @@ namespace Swift {
return stanzaType;
}
- void setSessionID(const std::string& id) {
- sessionID = id;
- }
-
- const std::string& getSessionID() const {
- return sessionID;
- }
-
int getBlockSize() const {
return blockSize;
}
@@ -46,7 +38,6 @@ namespace Swift {
}
private:
- std::string sessionID;
int blockSize;
StanzaType stanzaType;
};
diff --git a/Swiften/Elements/JinglePayload.h b/Swiften/Elements/JinglePayload.h
index 5c766b8..31d4448 100644
--- a/Swiften/Elements/JinglePayload.h
+++ b/Swiften/Elements/JinglePayload.h
@@ -7,6 +7,7 @@
#pragma once
#include <vector>
+#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>
#include <string>
@@ -14,13 +15,15 @@
#include <Swiften/Elements/Payload.h>
#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Base/Log.h>
namespace Swift {
class JinglePayload : public Payload {
public:
typedef boost::shared_ptr<JinglePayload> ref;
- struct Reason {
+ struct Reason : public Payload {
enum Type {
+ UnknownType,
AlternativeSession,
Busy,
Cancel,
@@ -39,13 +42,15 @@ namespace Swift {
UnsupportedApplications,
UnsupportedTransports
};
-
+ Reason() : type(UnknownType), text("") {}
Reason(Type type, const std::string& text = "") : type(type), text(text) {}
+ ~Reason() {}
Type type;
std::string text;
};
enum Action {
+ UnknownAction,
ContentAccept,
ContentAdd,
ContentModify,
@@ -62,8 +67,11 @@ namespace Swift {
TransportReject,
TransportReplace
};
-
+ JinglePayload() : action(SessionTerminate), sessionID("") {
+ }
+
JinglePayload(Action action, const std::string& sessionID) : action(action), sessionID(sessionID) {
+
}
void setAction(Action action) {
@@ -99,11 +107,46 @@ namespace Swift {
}
void addContent(JingleContentPayload::ref content) {
- this->contents.push_back(content);
+ this->payloads.push_back(content);
+ }
+
+ void addPayload(boost::shared_ptr<Payload> payload) {
+ this->payloads.push_back(payload);
+ }
+
+ const std::vector<JingleContentPayload::ref> getContents() const {
+ return getPayloads<JingleContentPayload>();
+ }
+
+ const std::vector<boost::shared_ptr<Payload> > getPayloads() const {
+ return payloads;
+ }
+
+ template<typename T>
+ const std::vector<boost::shared_ptr<T> > getPayloads() const {
+ std::vector<boost::shared_ptr<T> > matched_payloads;
+ for (std::vector<boost::shared_ptr<Payload> >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) {
+ boost::shared_ptr<T> result = boost::dynamic_pointer_cast<T>(*i);
+ if (result) {
+ matched_payloads.push_back(result);
+ }
+ }
+
+ return matched_payloads;
+
}
- const std::vector<JingleContentPayload::ref>& getContents() const {
- return contents;
+ template<typename T>
+ const boost::shared_ptr<T> getPayload() const {
+ boost::shared_ptr<T> result;
+ for (std::vector<boost::shared_ptr<Payload> >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) {
+ result = boost::dynamic_pointer_cast<T>(*i);
+ if (result) {
+ return result;
+ }
+ }
+
+ return result;
}
void setReason(const Reason& reason) {
@@ -119,7 +162,7 @@ namespace Swift {
JID initiator;
JID responder;
std::string sessionID;
- std::vector<JingleContentPayload::ref> contents;
+ std::vector<boost::shared_ptr<Payload> > payloads;
boost::optional<Reason> reason;
};
}
diff --git a/Swiften/Elements/JingleS5BTransportPayload.h b/Swiften/Elements/JingleS5BTransportPayload.h
index 7b3089f..980af27 100644
--- a/Swiften/Elements/JingleS5BTransportPayload.h
+++ b/Swiften/Elements/JingleS5BTransportPayload.h
@@ -6,23 +6,107 @@
#pragma once
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
#include <Swiften/Elements/JingleTransportPayload.h>
#include <Swiften/Elements/Bytestreams.h>
+#include <Swiften/Network/HostAddressPort.h>
-// FIXME: Remove Bytestreams, and replace by our own candidate
namespace Swift {
class JingleS5BTransportPayload : public JingleTransportPayload {
public:
- const Bytestreams& getInfo() const {
- return info;
+ enum Mode {
+ TCPMode, // default case
+ UDPMode,
+ };
+
+ struct Candidate {
+ enum Type {
+ DirectType, // default case
+ AssistedType,
+ TunnelType,
+ ProxyType,
+ };
+
+ Candidate() : priority(0), type(DirectType) {}
+
+ std::string cid;
+ JID jid;
+ HostAddressPort hostPort;
+ int priority;
+ Type type;
+ };
+
+ struct CompareCandidate {
+ bool operator() (const JingleS5BTransportPayload::Candidate& c1, const JingleS5BTransportPayload::Candidate& c2) const {
+ if (c1.priority < c2.priority) return true;
+ return false;
+ }
+ };
+
+ public:
+ JingleS5BTransportPayload() : mode(TCPMode), candidateError(false), proxyError(false) {}
+
+ Mode getMode() const {
+ return mode;
+ }
+
+ void setMode(Mode mode) {
+ this->mode = mode;
+ }
+
+ const std::vector<Candidate>& getCandidates() {
+ return candidates;
+ }
+
+ void addCandidate(const Candidate& candidate) {
+ candidates.push_back(candidate);
+ }
+
+ void setCandidateUsed(const std::string& cid) {
+ candidateUsedCID = cid;
+ }
+
+ const std::string& getCandidateUsed() const {
+ return candidateUsedCID;
+ }
+
+ void setActivated(const std::string& cid) {
+ activatedCID = cid;
}
- void setInfo(const Bytestreams& info) {
- this->info = info;
+ const std::string& getActivated() const {
+ return activatedCID;
}
+ void setCandidateError(bool hasError) {
+ candidateError = hasError;
+ }
+
+ bool hasCandidateError() const {
+ return candidateError;
+ }
+
+ void setProxyError(bool hasError) {
+ proxyError = hasError;
+ }
+
+ bool hasProxyError() const {
+ return proxyError;
+ }
+ public:
+ typedef boost::shared_ptr<JingleS5BTransportPayload> ref;
+
private:
- Bytestreams info;
+ Mode mode;
+ std::vector<Candidate> candidates;
+
+ std::string candidateUsedCID;
+ std::string activatedCID;
+ bool candidateError;
+ bool proxyError;
};
}
diff --git a/Swiften/Elements/JingleTransportPayload.h b/Swiften/Elements/JingleTransportPayload.h
index 7a9ea29..b870be9 100644
--- a/Swiften/Elements/JingleTransportPayload.h
+++ b/Swiften/Elements/JingleTransportPayload.h
@@ -13,6 +13,18 @@
namespace Swift {
class JingleTransportPayload : public Payload {
public:
+ void setSessionID(const std::string& id) {
+ sessionID = id;
+ }
+
+ const std::string& getSessionID() const {
+ return sessionID;
+ }
+
+ public:
typedef boost::shared_ptr<JingleTransportPayload> ref;
+
+ private:
+ std::string sessionID;
};
}
diff --git a/Swiften/Elements/Payload.h b/Swiften/Elements/Payload.h
index 8b6d44a..f994ebc 100644
--- a/Swiften/Elements/Payload.h
+++ b/Swiften/Elements/Payload.h
@@ -6,9 +6,13 @@
#pragma once
+#include <boost/shared_ptr.hpp>
+
namespace Swift {
class Payload {
public:
+ typedef boost::shared_ptr<Payload> ref;
+ public:
virtual ~Payload();
};
}
diff --git a/Swiften/Elements/S5BProxyRequest.h b/Swiften/Elements/S5BProxyRequest.h
new file mode 100644
index 0000000..fcd0cb2
--- /dev/null
+++ b/Swiften/Elements/S5BProxyRequest.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Elements/Payload.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/HostAddressPort.h>
+
+namespace Swift {
+
+class S5BProxyRequest : public Payload {
+public:
+ typedef boost::shared_ptr<S5BProxyRequest> ref;
+
+public:
+ struct StreamHost {
+ HostAddressPort addressPort;
+ JID jid;
+ };
+
+public:
+ const boost::optional<StreamHost>& getStreamHost() const {
+ return streamHost;
+ }
+
+ void setStreamHost(const StreamHost& streamHost) {
+ this->streamHost = boost::optional<StreamHost>(streamHost);
+ }
+
+ const std::string& getSID() const {
+ return sid;
+ }
+
+ void setSID(const std::string& sid) {
+ this->sid = sid;
+ }
+
+ const boost::optional<JID>& getActivate() const {
+ return activate;
+ }
+
+ void setActivate(const JID& activate) {
+ this->activate = activate;
+ }
+
+private:
+ boost::optional<StreamHost> streamHost;
+
+ std::string sid;
+ boost::optional<JID> activate;
+};
+
+}
diff --git a/Swiften/Elements/StreamInitiationFileInfo.h b/Swiften/Elements/StreamInitiationFileInfo.h
index 92b9824..9484bc0 100644
--- a/Swiften/Elements/StreamInitiationFileInfo.h
+++ b/Swiften/Elements/StreamInitiationFileInfo.h
@@ -6,14 +6,97 @@
#pragma once
+#include <Swiften/Elements/Payload.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
#include <string>
namespace Swift {
- struct StreamInitiationFileInfo {
- StreamInitiationFileInfo(const std::string& name = "", const std::string& description = "", int size = -1) : name(name), description(description), size(size) {}
- std::string name;
- std::string description;
- int size;
- };
+class StreamInitiationFileInfo : public Payload {
+public:
+ typedef boost::shared_ptr<StreamInitiationFileInfo> ref;
+
+public:
+ StreamInitiationFileInfo(const std::string& name = "", const std::string& description = "", int size = 0,
+ const std::string& hash = "", const boost::posix_time::ptime &date = boost::posix_time::ptime(), const std::string& algo="md5") :
+ name(name), description(description), size(size), hash(hash), date(date), algo(algo), supportsRangeRequests(false), rangeOffset(0) {}
+
+ void setName(const std::string& name) {
+ this->name = name;;
+ }
+
+ const std::string& getName() const {
+ return this->name;
+ }
+
+ void setDescription(const std::string& description) {
+ this->description = description;
+ }
+
+ const std::string& getDescription() const {
+ return this->description;
+ }
+
+ void setSize(const boost::uintmax_t size) {
+ this->size = size;
+ }
+
+ boost::uintmax_t getSize() const {
+ return this->size;
+ }
+
+ void setHash(const std::string& hash) {
+ this->hash = hash;
+ }
+
+ const std::string& getHash() const {
+ return this->hash;
+ }
+
+ void setDate(const boost::posix_time::ptime& date) {
+ this->date = date;
+ }
+
+ const boost::posix_time::ptime& getDate() const {
+ return this->date;
+ }
+
+ void setAlgo(const std::string& algo) {
+ this->algo = algo;
+ }
+
+ const std::string& getAlgo() const {
+ return this->algo;
+ }
+
+ void setSupportsRangeRequests(const bool supportsIt) {
+ supportsRangeRequests = supportsIt;
+ }
+
+ bool getSupportsRangeRequests() const {
+ return supportsRangeRequests;
+ }
+
+ void setRangeOffset(const int offset) {
+ supportsRangeRequests = offset >= 0 ? true : false;
+ rangeOffset = offset;
+ }
+
+ int getRangeOffset() const {
+ return rangeOffset;
+ }
+
+private:
+ std::string name;
+ std::string description;
+ boost::uintmax_t size;
+ std::string hash;
+ boost::posix_time::ptime date;
+ std::string algo;
+ bool supportsRangeRequests;
+ boost::uintmax_t rangeOffset;
+};
+
}
diff --git a/Swiften/Examples/SendFile/ReceiveFile.cpp b/Swiften/Examples/SendFile/ReceiveFile.cpp
index effa1b7..f80f03a 100644
--- a/Swiften/Examples/SendFile/ReceiveFile.cpp
+++ b/Swiften/Examples/SendFile/ReceiveFile.cpp
@@ -12,12 +12,18 @@
#include <Swiften/Elements/Presence.h>
#include <Swiften/Base/foreach.h>
#include <Swiften/Client/Client.h>
+#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Network/BoostNetworkFactories.h>
#include <Swiften/EventLoop/SimpleEventLoop.h>
#include <Swiften/Client/ClientXMLTracer.h>
+#include <Swiften/Disco/ClientDiscoManager.h>
#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
#include <Swiften/FileTransfer/FileWriteBytestream.h>
#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
using namespace Swift;
@@ -26,19 +32,20 @@ BoostNetworkFactories networkFactories(&eventLoop);
int exitCode = 2;
+static const std::string CLIENT_NAME = "Swiften FT Test";
+static const std::string CLIENT_NODE = "http://swift.im";
+
class FileReceiver {
public:
FileReceiver(const JID& jid, const std::string& password) : jid(jid), password(password), jingleSessionManager(NULL), incomingFileTransferManager(NULL) {
client = new Swift::Client(jid, password, &networkFactories);
client->onConnected.connect(boost::bind(&FileReceiver::handleConnected, this));
client->onDisconnected.connect(boost::bind(&FileReceiver::handleDisconnected, this, _1));
- //tracer = new ClientXMLTracer(client);
+ tracer = new ClientXMLTracer(client);
}
~FileReceiver() {
- delete incomingFileTransferManager;
- delete jingleSessionManager;
- //delete tracer;
+ delete tracer;
client->onDisconnected.disconnect(boost::bind(&FileReceiver::handleDisconnected, this, _1));
client->onConnected.disconnect(boost::bind(&FileReceiver::handleConnected, this));
delete client;
@@ -57,13 +64,24 @@ class FileReceiver {
private:
void handleConnected() {
- client->sendPresence(Presence::create());
- jingleSessionManager = new JingleSessionManager(client->getIQRouter());
- incomingFileTransferManager = new IncomingFileTransferManager(jingleSessionManager, client->getIQRouter());
- incomingFileTransferManager->onIncomingFileTransfer.connect(boost::bind(&FileReceiver::handleIncomingFileTransfer, this, _1));
+ Swift::logging = true;
+ client->getFileTransferManager()->startListeningOnPort(9999);
+ client->getFileTransferManager()->onIncomingFileTransfer.connect(boost::bind(&FileReceiver::handleIncomingFileTransfer, this, _1));
+
+ DiscoInfo discoInfo;
+ discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
+ discoInfo.addFeature(DiscoInfo::JingleFeature);
+ discoInfo.addFeature(DiscoInfo::JingleFTFeature);
+ discoInfo.addFeature(DiscoInfo::Bytestream);
+ discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+ discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
+ client->getDiscoManager()->setCapsNode(CLIENT_NODE);
+ client->getDiscoManager()->setDiscoInfo(discoInfo);
+ client->getPresenceSender()->sendPresence(Presence::create());
}
void handleIncomingFileTransfer(IncomingFileTransfer::ref transfer) {
+ SWIFT_LOG(debug) << "foo" << std::endl;
incomingFileTransfers.push_back(transfer);
transfer->accept(boost::make_shared<FileWriteBytestream>("out"));
//transfer->onFinished.connect(boost::bind(&FileReceiver::handleFileTransferFinished, this, _1));
@@ -100,6 +118,9 @@ class FileReceiver {
JingleSessionManager* jingleSessionManager;
IncomingFileTransferManager* incomingFileTransferManager;
std::vector<IncomingFileTransfer::ref> incomingFileTransfers;
+ DefaultLocalJingleTransportCandidateGeneratorFactory *localFactory;
+ DefaultRemoteJingleTransportCandidateSelectorFactory *remoteFactory;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
};
diff --git a/Swiften/Examples/SendFile/SendFile.cpp b/Swiften/Examples/SendFile/SendFile.cpp
index 205b442..9b2105b 100644
--- a/Swiften/Examples/SendFile/SendFile.cpp
+++ b/Swiften/Examples/SendFile/SendFile.cpp
@@ -4,6 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <iostream>
@@ -20,6 +21,17 @@
#include <Swiften/FileTransfer/FileReadBytestream.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
#include <Swiften/Network/BoostConnectionServer.h>
+#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/StringCodecs/MD5.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
using namespace Swift;
@@ -30,45 +42,65 @@ int exitCode = 2;
class FileSender {
public:
- FileSender(const JID& jid, const std::string& password, const JID& recipient, const boost::filesystem::path& file, int port) : jid(jid), password(password), recipient(recipient), file(file), transfer(NULL) {
- connectionServer = BoostConnectionServer::create(port, networkFactories.getIOServiceThread()->getIOService(), &eventLoop);
- socksBytestreamServer = new SOCKS5BytestreamServer(connectionServer);
+ FileSender(const JID& jid, const std::string& password, const JID& recipient, const boost::filesystem::path& file) : jid(jid), password(password), recipient(recipient), file(file) {
client = new Swift::Client(jid, password, &networkFactories);
client->onConnected.connect(boost::bind(&FileSender::handleConnected, this));
client->onDisconnected.connect(boost::bind(&FileSender::handleDisconnected, this, _1));
- //tracer = new ClientXMLTracer(client);
+ tracer = new ClientXMLTracer(client);
+ client->getEntityCapsProvider()->onCapsChanged.connect(boost::bind(&FileSender::handleCapsChanged, this, _1));
}
~FileSender() {
- //delete tracer;
+ delete tracer;
client->onDisconnected.disconnect(boost::bind(&FileSender::handleDisconnected, this, _1));
client->onConnected.disconnect(boost::bind(&FileSender::handleConnected, this));
delete client;
- delete socksBytestreamServer;
}
-
+
void start() {
- connectionServer->start();
- socksBytestreamServer->start();
client->connect();
}
- void stop() {
- if (transfer) {
- transfer->stop();
- }
- client->disconnect();
- socksBytestreamServer->stop();
- connectionServer->stop();
- }
-
private:
void handleConnected() {
client->sendPresence(Presence::create());
+
+ client->getFileTransferManager()->startListeningOnPort(19999);
+ //ByteArray fileData;
+ //readByteArrayFromFile(fileData, file.string());
+
+ // gather file information
+ /*StreamInitiationFileInfo fileInfo;
+
+ fileInfo.setName(file.filename());
+ fileInfo.setSize(boost::filesystem::file_size(file));
+ fileInfo.setDescription("Some file!");
+ fileInfo.setDate(boost::posix_time::from_time_t(boost::filesystem::last_write_time(file)));*/
+ //fileInfo.setHash(Hexify::hexify(MD5::getHash(fileData)));
+ /*
transfer = new OutgoingSIFileTransfer("myid", client->getJID(), recipient, file.filename(), boost::filesystem::file_size(file), "A file", boost::shared_ptr<FileReadBytestream>(new FileReadBytestream(file)), client->getIQRouter(), socksBytestreamServer);
transfer->onFinished.connect(boost::bind(&FileSender::handleFileTransferFinished, this, _1));
transfer->start();
+ */
+ }
+
+ void handleCapsChanged(JID jid) {
+ if (jid.toBare() == recipient) {
+ // create ReadBytestream from file
+ boost::shared_ptr<FileReadBytestream> fileStream = boost::make_shared<FileReadBytestream>(file);
+
+ outgoingFileTransfer = client->getFileTransferManager()->createOutgoingFileTransfer(recipient, file, "Some File!", fileStream);
+
+ if (outgoingFileTransfer) {
+ std::cout << "started FT" << std::endl;
+ outgoingFileTransfer->start();
+ // TODO: getting notified about FT status and end
+ } else {
+ std::cout << "[ ERROR ] " << recipient << " doesn't support any kind of file transfer!" << std::endl;
+ //client->disconnect();
+ }
+ }
}
void handleDisconnected(const boost::optional<ClientError>&) {
@@ -85,23 +117,23 @@ class FileSender {
exit(0);
}
}
-
+
void exit(int code) {
exitCode = code;
- stop();
eventLoop.stop();
}
private:
BoostConnectionServer::ref connectionServer;
SOCKS5BytestreamServer* socksBytestreamServer;
+ SOCKS5BytestreamRegistry* registry;
+ OutgoingFileTransfer::ref outgoingFileTransfer;
JID jid;
std::string password;
JID recipient;
boost::filesystem::path file;
Client* client;
ClientXMLTracer* tracer;
- OutgoingSIFileTransfer* transfer;
};
@@ -113,9 +145,9 @@ int main(int argc, char* argv[]) {
JID sender(argv[1]);
JID recipient(argv[3]);
- FileSender fileSender(sender, std::string(argv[2]), recipient, boost::filesystem::path(argv[4]), 8888);
+ Swift::logging = true;
+ FileSender fileSender(sender, std::string(argv[2]), recipient, boost::filesystem::path(argv[4]));
fileSender.start();
-
{
/*BoostTimer::ref timer(BoostTimer::create(30000, &MainBoostIOServiceThread::getInstance().getIOService()));
timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
diff --git a/Swiften/FileTransfer/ByteArrayReadBytestream.h b/Swiften/FileTransfer/ByteArrayReadBytestream.h
index 4704db6..a6945c3 100644
--- a/Swiften/FileTransfer/ByteArrayReadBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayReadBytestream.h
@@ -22,6 +22,8 @@ namespace Swift {
readSize = data.size() - position;
}
std::vector<unsigned char> result(data.begin() + position, data.begin() + position + readSize);
+
+ onRead(result);
position += readSize;
return result;
}
diff --git a/Swiften/FileTransfer/ByteArrayWriteBytestream.h b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
index 6c360e6..ef97ed9 100644
--- a/Swiften/FileTransfer/ByteArrayWriteBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
@@ -16,6 +16,7 @@ namespace Swift {
virtual void write(const std::vector<unsigned char>& bytes) {
data.insert(data.end(), bytes.begin(), bytes.end());
+ onWrite(bytes);
}
const std::vector<unsigned char>& getData() const {
diff --git a/Swiften/FileTransfer/ConnectivityManager.cpp b/Swiften/FileTransfer/ConnectivityManager.cpp
new file mode 100644
index 0000000..174d6ab
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "ConnectivityManager.h"
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+ConnectivityManager::ConnectivityManager(PlatformNATTraversalWorker* worker) : natTraversalWorker(worker) {
+
+}
+
+ConnectivityManager::~ConnectivityManager() {
+ std::set<int> leftOpenPorts = ports;
+ foreach(int port, leftOpenPorts) {
+ removeListeningPort(port);
+ }
+}
+
+void ConnectivityManager::addListeningPort(int port) {
+ ports.insert(port);
+ boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> getIPRequest = natTraversalWorker->createGetPublicIPRequest();
+ if (getIPRequest) {
+ getIPRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalGetPublicIPResult, this, _1));
+ getIPRequest->run();
+ }
+
+ boost::shared_ptr<PlatformNATTraversalForwardPortRequest> forwardPortRequest = natTraversalWorker->createForwardPortRequest(port, port);
+ if (forwardPortRequest) {
+ forwardPortRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalForwardPortResult, this, _1));
+ forwardPortRequest->run();
+ }
+}
+
+void ConnectivityManager::removeListeningPort(int port) {
+ SWIFT_LOG(debug) << "remove listening port " << port << std::endl;
+ ports.erase(port);
+ boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> removePortForwardingRequest = natTraversalWorker->createRemovePortForwardingRequest(port, port);
+ if (removePortForwardingRequest) {
+ removePortForwardingRequest->run();
+ }
+}
+
+std::vector<HostAddressPort> ConnectivityManager::getHostAddressPorts() const {
+ PlatformNetworkEnvironment env;
+ std::vector<HostAddressPort> results;
+
+ std::vector<HostAddress> addresses;
+
+ foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+ foreach (HostAddress address, iface->getAddresses()) {
+ foreach (int port, ports) {
+ results.push_back(HostAddressPort(address, port));
+ }
+ }
+ }
+
+ return results;
+}
+
+std::vector<HostAddressPort> ConnectivityManager::getAssistedHostAddressPorts() const {
+ std::vector<HostAddressPort> results;
+
+ if (publicAddress) {
+ foreach (int port, ports) {
+ results.push_back(HostAddressPort(publicAddress.get(), port));
+ }
+ }
+
+ return results;
+}
+
+void ConnectivityManager::natTraversalGetPublicIPResult(boost::optional<HostAddress> address) {
+ if (address) {
+ publicAddress = address;
+ SWIFT_LOG(debug) << "Public IP discovered as " << publicAddress.get().toString() << "." << std::endl;
+ } else {
+ SWIFT_LOG(debug) << "No public IP discoverable." << std::endl;
+ }
+}
+
+void ConnectivityManager::natTraversalForwardPortResult(boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping> mapping) {
+ if (mapping) {
+ SWIFT_LOG(debug) << "Mapping port was successful." << std::endl;
+ } else {
+ SWIFT_LOG(debug) << "Mapping port has failed." << std::endl;
+ }
+}
+
+
+}
diff --git a/Swiften/FileTransfer/ConnectivityManager.h b/Swiften/FileTransfer/ConnectivityManager.h
new file mode 100644
index 0000000..87041b2
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class ConnectivityManager {
+public:
+ ConnectivityManager(PlatformNATTraversalWorker*);
+ ~ConnectivityManager();
+public:
+ void addListeningPort(int port);
+ void removeListeningPort(int port);
+
+ std::vector<HostAddressPort> getHostAddressPorts() const;
+ std::vector<HostAddressPort> getAssistedHostAddressPorts() const;
+
+private:
+ void natTraversalGetPublicIPResult(boost::optional<HostAddress> address);
+ void natTraversalForwardPortResult(boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping> mapping);
+
+private:
+ PlatformNATTraversalWorker* natTraversalWorker;
+
+ std::set<int> ports;
+ boost::optional<HostAddress> publicAddress;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
new file mode 100644
index 0000000..5b6da4c
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultLocalJingleTransportCandidateGenerator.h"
+
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/FileTransfer/ConnectivityManager.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+
+namespace Swift {
+
+DefaultLocalJingleTransportCandidateGenerator::DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) {
+}
+
+DefaultLocalJingleTransportCandidateGenerator::~DefaultLocalJingleTransportCandidateGenerator() {
+}
+
+void DefaultLocalJingleTransportCandidateGenerator::generateLocalTransportCandidates(JingleTransportPayload::ref transportPayload) {
+ if (boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload)) {
+ JingleTransportPayload::ref payL = boost::make_shared<JingleTransportPayload>();
+ payL->setSessionID(transportPayload->getSessionID());
+ onLocalTransportCandidatesGenerated(payL);
+ }
+ if (boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload)) {
+ JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>();
+ payL->setSessionID(transportPayload->getSessionID());
+ payL->setMode(JingleS5BTransportPayload::TCPMode);
+
+ const unsigned long localPreference = 0;
+
+ // get direct candidates
+ std::vector<HostAddressPort> directCandidates = connectivityManager->getHostAddressPorts();
+ foreach(HostAddressPort addressPort, directCandidates) {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::DirectType;
+ candidate.jid = ownJID;
+ candidate.hostPort = addressPort;
+ candidate.priority = 65536 * 126 + localPreference;
+ candidate.cid = idGenerator.generateID();
+ payL->addCandidate(candidate);
+ }
+
+ // get assissted candidates
+ std::vector<HostAddressPort> assisstedCandidates = connectivityManager->getAssistedHostAddressPorts();
+ foreach(HostAddressPort addressPort, assisstedCandidates) {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::AssistedType;
+ candidate.jid = ownJID;
+ candidate.hostPort = addressPort;
+ candidate.priority = 65536 * 120 + localPreference;
+ candidate.cid = idGenerator.generateID();
+ payL->addCandidate(candidate);
+ }
+
+ // get proxy candidates
+ std::vector<S5BProxyRequest::ref> proxyCandidates = s5bProxy->getS5BProxies();
+ foreach(S5BProxyRequest::ref proxy, proxyCandidates) {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::ProxyType;
+ candidate.jid = proxy->getStreamHost().get().jid;
+ candidate.hostPort = proxy->getStreamHost().get().addressPort;
+ candidate.priority = 65536 * 10 + localPreference;
+ candidate.cid = idGenerator.generateID();
+ payL->addCandidate(candidate);
+ }
+
+ onLocalTransportCandidatesGenerated(payL);
+ }
+
+}
+
+bool DefaultLocalJingleTransportCandidateGenerator::isActualCandidate(JingleTransportPayload::ref transportPayload) {
+ if (!transportPayload.get()) return false;
+ return false;
+}
+
+int DefaultLocalJingleTransportCandidateGenerator::getPriority(JingleTransportPayload::ref /* transportPayload */) {
+ return 0;
+}
+
+JingleTransport::ref DefaultLocalJingleTransportCandidateGenerator::selectTransport(JingleTransportPayload::ref /* transportPayload */) {
+ return JingleTransport::ref();
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h
new file mode 100644
index 0000000..7d45491
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+class ConnectivityManager;
+
+class DefaultLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+ DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID);
+ virtual ~DefaultLocalJingleTransportCandidateGenerator();
+
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref);
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref);
+ virtual int getPriority(JingleTransportPayload::ref);
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref);
+
+private:
+ IDGenerator idGenerator;
+ ConnectivityManager* connectivityManager;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ JID ownJID;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp
new file mode 100644
index 0000000..ed0386e
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultLocalJingleTransportCandidateGeneratorFactory.h"
+
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+DefaultLocalJingleTransportCandidateGeneratorFactory::DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) {
+}
+
+DefaultLocalJingleTransportCandidateGeneratorFactory::~DefaultLocalJingleTransportCandidateGeneratorFactory() {
+}
+
+LocalJingleTransportCandidateGenerator* DefaultLocalJingleTransportCandidateGeneratorFactory::createCandidateGenerator() {
+ return new DefaultLocalJingleTransportCandidateGenerator(connectivityManager, s5bRegistry, s5bProxy, ownJID);
+}
+
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h
new file mode 100644
index 0000000..511d0a1
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class ConnectivityManager;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class DefaultLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory{
+public:
+ DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID);
+ virtual ~DefaultLocalJingleTransportCandidateGeneratorFactory();
+
+ LocalJingleTransportCandidateGenerator* createCandidateGenerator();
+
+private:
+ ConnectivityManager* connectivityManager;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ JID ownJID;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp
new file mode 100644
index 0000000..32b4df8
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultRemoteJingleTransportCandidateSelector.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+
+namespace Swift {
+
+DefaultRemoteJingleTransportCandidateSelector::DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) {
+}
+
+DefaultRemoteJingleTransportCandidateSelector::~DefaultRemoteJingleTransportCandidateSelector() {
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::addRemoteTransportCandidates(JingleTransportPayload::ref transportPayload) {
+ JingleS5BTransportPayload::ref s5bPayload;
+ transportSID = transportPayload->getSessionID();
+ if ((s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload))) {
+ foreach(JingleS5BTransportPayload::Candidate c, s5bPayload->getCandidates()) {
+ candidates.push(c);
+ }
+ }
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::selectCandidate() {
+ tryNextCandidate(true);
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate(bool error) {
+ if (error) {
+ if (s5bSession) {
+ SWIFT_LOG(debug) << "failed to connect" << std::endl;
+ }
+ if (candidates.empty()) {
+ // failed to connect to any of the candidates
+ // issue an error
+ SWIFT_LOG(debug) << "out of candidates )=" << std::endl;
+ JingleS5BTransportPayload::ref failed = boost::make_shared<JingleS5BTransportPayload>();
+ failed->setCandidateError(true);
+ failed->setSessionID(transportSID);
+ onRemoteTransportCandidateSelectFinished(failed);
+ } else {
+ lastCandidate = candidates.top();
+ // only try direct or assisted for now
+ if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType ||
+ lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType || lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType ) {
+ // create connection
+ connection = connectionFactory->createConnection();
+ s5bSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, lastCandidate.hostPort, SOCKS5BytestreamRegistry::getHostname(transportSID, requester, target), timerFactory);
+
+ // bind onReady to this method
+ s5bSession->onSessionReady.connect(boost::bind(&DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate, this, _1));
+
+ std::string candidateType;
+ if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType) {
+ candidateType = "direct";
+ } else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType) {
+ candidateType = "assisted";
+ } else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ candidateType = "proxy";
+ }
+
+ // initiate connect
+ SWIFT_LOG(debug) << "try to connect to candidate of type " << candidateType << " : " << lastCandidate.hostPort.toString() << std::endl;
+ s5bSession->start();
+
+ // that's it. we're gonna be called back
+ candidates.pop();
+ } else {
+ s5bSession.reset();
+ candidates.pop();
+ tryNextCandidate(true);
+ }
+ }
+ } else {
+ // we have a working connection, hooray
+ JingleS5BTransportPayload::ref success = boost::make_shared<JingleS5BTransportPayload>();
+ success->setCandidateUsed(lastCandidate.cid);
+ success->setSessionID(transportSID);
+ onRemoteTransportCandidateSelectFinished(success);
+ }
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::setMinimumPriority(int priority) {
+ SWIFT_LOG(debug) << "priority: " << priority << std::endl;
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::setRequesterTargtet(const JID& requester, const JID& target) {
+ this->requester = requester;
+ this->target = target;
+}
+
+SOCKS5BytestreamClientSession::ref DefaultRemoteJingleTransportCandidateSelector::getS5BSession() {
+ return s5bSession;
+}
+
+bool DefaultRemoteJingleTransportCandidateSelector::isActualCandidate(JingleTransportPayload::ref /* transportPayload */) {
+ return false;
+}
+
+int DefaultRemoteJingleTransportCandidateSelector::getPriority(JingleTransportPayload::ref /* transportPayload */) {
+ return 0;
+}
+
+JingleTransport::ref DefaultRemoteJingleTransportCandidateSelector::selectTransport(JingleTransportPayload::ref /* transportPayload */) {
+ return JingleTransport::ref();
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h
new file mode 100644
index 0000000..255acd9
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <queue>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+
+
+namespace Swift {
+
+class ConnectionFactory;
+class TimerFactory;
+
+class DefaultRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+public:
+ DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory*, TimerFactory*);
+ virtual ~DefaultRemoteJingleTransportCandidateSelector();
+
+ virtual void addRemoteTransportCandidates(JingleTransportPayload::ref);
+ virtual void selectCandidate();
+ virtual void setMinimumPriority(int);
+ void setRequesterTargtet(const JID& requester, const JID& target);
+ virtual SOCKS5BytestreamClientSession::ref getS5BSession();
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref);
+ virtual int getPriority(JingleTransportPayload::ref);
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref);
+
+private:
+ void tryNextCandidate(bool error);
+
+private:
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+
+ std::priority_queue<JingleS5BTransportPayload::Candidate, std::vector<JingleS5BTransportPayload::Candidate>, JingleS5BTransportPayload::CompareCandidate> candidates;
+
+ std::string transportSID;
+ boost::shared_ptr<Connection> connection;
+ boost::shared_ptr<SOCKS5BytestreamClientSession> s5bSession;
+ JingleS5BTransportPayload::Candidate lastCandidate;
+ JID requester;
+ JID target;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp
new file mode 100644
index 0000000..8ebbf46
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultRemoteJingleTransportCandidateSelectorFactory.h"
+
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+DefaultRemoteJingleTransportCandidateSelectorFactory::DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) {
+}
+
+DefaultRemoteJingleTransportCandidateSelectorFactory::~DefaultRemoteJingleTransportCandidateSelectorFactory() {
+}
+
+RemoteJingleTransportCandidateSelector* DefaultRemoteJingleTransportCandidateSelectorFactory::createCandidateSelector() {
+ return new DefaultRemoteJingleTransportCandidateSelector(connectionFactory, timerFactory);
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h
new file mode 100644
index 0000000..ca29e1f
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+
+namespace Swift {
+
+class ConnectionFactory;
+class TimerFactory;
+
+class DefaultRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+ DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory*, TimerFactory*);
+ virtual ~DefaultRemoteJingleTransportCandidateSelectorFactory();
+
+ RemoteJingleTransportCandidateSelector* createCandidateSelector();
+
+private:
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+};
+
+}
diff --git a/Swiften/FileTransfer/FileReadBytestream.cpp b/Swiften/FileTransfer/FileReadBytestream.cpp
index 84d6490..f0139b8 100644
--- a/Swiften/FileTransfer/FileReadBytestream.cpp
+++ b/Swiften/FileTransfer/FileReadBytestream.cpp
@@ -30,6 +30,7 @@ std::vector<unsigned char> FileReadBytestream::read(size_t size) {
assert(stream->good());
stream->read(reinterpret_cast<char*>(&result[0]), size);
result.resize(stream->gcount());
+ onRead(result);
return result;
}
diff --git a/Swiften/FileTransfer/FileTransfer.h b/Swiften/FileTransfer/FileTransfer.h
new file mode 100644
index 0000000..6c37d8d
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransfer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+
+namespace Swift {
+
+class FileTransfer {
+public:
+ struct State {
+ enum FTState {
+ Canceled,
+ Failed,
+ Finished,
+ Negotiating,
+ Transferring,
+ WaitingForStart,
+ WaitingForAccept,
+ };
+
+ FTState state;
+ std::string message;
+
+ State(FTState state) : state(state), message("") {}
+ State(FTState state, std::string message) : state(state), message(message) {}
+ };
+
+public:
+ typedef boost::shared_ptr<FileTransfer> ref;
+
+public:
+ uintmax_t fileSizeInBytes;
+ std::string filename;
+ std::string algo;
+ std::string hash;
+
+public:
+ virtual void cancel() = 0;
+
+public:
+ boost::signal<void (int /* proccessedBytes */)> onProcessedBytes;
+ boost::signal<void (State)> onStateChange;
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+
+public:
+ virtual ~FileTransfer() {}
+};
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManager.cpp b/Swiften/FileTransfer/FileTransferManager.cpp
new file mode 100644
index 0000000..69be852
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+
+FileTransferManager::~FileTransferManager() {
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManager.h b/Swiften/FileTransfer/FileTransferManager.h
new file mode 100644
index 0000000..3a1628f
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+
+namespace Swift {
+ class ReadBytestream;
+ class S5BProxyRequest;
+
+ class FileTransferManager {
+ public:
+ virtual ~FileTransferManager();
+
+ virtual void startListeningOnPort(int port) = 0;
+ virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest> proxy) = 0;
+
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) = 0;
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) = 0;
+
+ boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
+ };
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.cpp b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
new file mode 100644
index 0000000..f89a3e9
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include "Swiften/Disco/EntityCapsProvider.h"
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/FileTransfer/ConnectivityManager.h>
+#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
+#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Presence/PresenceOracle.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/ConnectionServerFactory.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+
+namespace Swift {
+
+FileTransferManagerImpl::FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, PlatformNATTraversalWorker* natTraversalWorker) : ownJID(ownFullJID), jingleSM(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), presenceOracle(presOracle), timerFactory(timerFactory), connectionFactory(connectionFactory), connectionServerFactory(connectionServerFactory), natTraversalWorker(natTraversalWorker), bytestreamServer(NULL) {
+ assert(!ownFullJID.isBare());
+
+ connectivityManager = new ConnectivityManager(natTraversalWorker);
+ bytestreamRegistry = new SOCKS5BytestreamRegistry();
+ bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+ localCandidateGeneratorFactory = new DefaultLocalJingleTransportCandidateGeneratorFactory(connectivityManager, bytestreamRegistry, bytestreamProxy, ownFullJID);
+ remoteCandidateSelectorFactory = new DefaultRemoteJingleTransportCandidateSelectorFactory(connectionFactory, timerFactory);
+ outgoingFTManager = new OutgoingFileTransferManager(ownJID, jingleSM, iqRouter, capsProvider, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy);
+ incomingFTManager = new IncomingFileTransferManager(ownJID, jingleSM, iqRouter, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy, timerFactory);
+ incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer);
+}
+
+FileTransferManagerImpl::~FileTransferManagerImpl() {
+ if (bytestreamServer) {
+ bytestreamServer->stop();
+ }
+ delete incomingFTManager;
+ delete outgoingFTManager;
+ delete remoteCandidateSelectorFactory;
+ delete localCandidateGeneratorFactory;
+ delete connectivityManager;
+}
+
+void FileTransferManagerImpl::startListeningOnPort(int port) {
+ // TODO: create a server for each interface we're on
+ SWIFT_LOG(debug) << "Start listening on port " << port << " and hope it's not in use." << std::endl;
+ boost::shared_ptr<ConnectionServer> server = connectionServerFactory->createConnectionServer(HostAddress("0.0.0.0"), port);
+ server->start();
+ bytestreamServer = new SOCKS5BytestreamServer(server, bytestreamRegistry);
+ bytestreamServer->start();
+ connectivityManager->addListeningPort(port);
+}
+
+void FileTransferManagerImpl::addS5BProxy(S5BProxyRequest::ref proxy) {
+ bytestreamProxy->addS5BProxy(proxy);
+}
+
+boost::optional<JID> FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) {
+ JID fullReceipientJID;
+ int priority = INT_MIN;
+
+ //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
+ std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
+
+ //iterate over them
+ foreach(Presence::ref pres, presences) {
+ if (pres->getPriority() > priority) {
+ // look up caps from the jid
+ DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
+ if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) &&
+ info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
+
+ priority = pres->getPriority();
+ fullReceipientJID = pres->getFrom();
+ }
+ }
+ }
+
+ return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
+}
+
+OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) {
+ std::string filename = filepath.filename();
+ uintmax_t sizeInBytes = boost::filesystem::file_size(filepath);
+ boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath));
+ return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream);
+}
+
+OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) {
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setDate(lastModified);
+ fileInfo.setSize(sizeInBytes);
+ fileInfo.setName(filename);
+ fileInfo.setDescription(description);
+
+ JID receipient = to;
+
+ if(receipient.isBare()) {
+ boost::optional<JID> fullJID = highestPriorityJIDSupportingFileTransfer(receipient);
+ if (fullJID.is_initialized()) {
+ receipient = fullJID.get();
+ } else {
+ return OutgoingFileTransfer::ref();
+ }
+ }
+
+ return outgoingFTManager->createOutgoingFileTransfer(receipient, bytestream, fileInfo);
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.h b/Swiften/FileTransfer/FileTransferManagerImpl.h
new file mode 100644
index 0000000..b38eaea
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+
+namespace Swift {
+
+class Client;
+class ConnectionFactory;
+class ConnectionServerFactory;
+class ConnectivityManager;
+class EntityCapsProvider;
+class IQRouter;
+class IncomingFileTransferManager;
+class JingleSessionManager;
+class LocalJingleTransportCandidateGeneratorFactory;
+class OutgoingFileTransferManager;
+class PlatformNATTraversalWorker;
+class PresenceOracle;
+class ReadBytestream;
+class RemoteJingleTransportCandidateSelectorFactory;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamServer;
+class SOCKS5BytestreamProxy;
+class TimerFactory;
+
+class FileTransferManagerImpl : public FileTransferManager {
+public:
+ FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, PlatformNATTraversalWorker* natTraversalWorker);
+ ~FileTransferManagerImpl();
+
+ void startListeningOnPort(int port);
+ void addS5BProxy(S5BProxyRequest::ref proxy);
+
+ OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream);
+ OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream);
+
+ private:
+ boost::optional<JID> highestPriorityJIDSupportingFileTransfer(const JID& bareJID);
+
+private:
+ JID ownJID;
+
+ OutgoingFileTransferManager* outgoingFTManager;
+ IncomingFileTransferManager* incomingFTManager;
+ RemoteJingleTransportCandidateSelectorFactory* remoteCandidateSelectorFactory;
+ LocalJingleTransportCandidateGeneratorFactory* localCandidateGeneratorFactory;
+ JingleSessionManager* jingleSM;
+ IQRouter* iqRouter;
+ EntityCapsProvider* capsProvider;
+ PresenceOracle* presenceOracle;
+
+ TimerFactory* timerFactory;
+ ConnectionFactory* connectionFactory;
+ ConnectionServerFactory* connectionServerFactory;
+ PlatformNATTraversalWorker* natTraversalWorker;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamServer* bytestreamServer;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ ConnectivityManager* connectivityManager;
+};
+
+}
diff --git a/Swiften/FileTransfer/FileWriteBytestream.cpp b/Swiften/FileTransfer/FileWriteBytestream.cpp
index c38338a..6a22c6a 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.cpp
+++ b/Swiften/FileTransfer/FileWriteBytestream.cpp
@@ -27,6 +27,14 @@ void FileWriteBytestream::write(const std::vector<unsigned char>& data) {
}
assert(stream->good());
stream->write(reinterpret_cast<const char*>(&data[0]), data.size());
+ onWrite(data);
+}
+
+void FileWriteBytestream::close() {
+ if (stream) {
+ stream->close();
+ stream = NULL;
+ }
}
}
diff --git a/Swiften/FileTransfer/FileWriteBytestream.h b/Swiften/FileTransfer/FileWriteBytestream.h
index 8cfa718..82c4a65 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.h
+++ b/Swiften/FileTransfer/FileWriteBytestream.h
@@ -18,6 +18,7 @@ namespace Swift {
~FileWriteBytestream();
virtual void write(const std::vector<unsigned char>&);
+ void close();
private:
boost::filesystem::path file;
diff --git a/Swiften/FileTransfer/IBBReceiveSession.cpp b/Swiften/FileTransfer/IBBReceiveSession.cpp
index 566dcca..43c26be 100644
--- a/Swiften/FileTransfer/IBBReceiveSession.cpp
+++ b/Swiften/FileTransfer/IBBReceiveSession.cpp
@@ -14,6 +14,8 @@
#include <Swiften/FileTransfer/BytestreamException.h>
#include <Swiften/Queries/SetResponder.h>
+#include <cassert>
+
namespace Swift {
class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
@@ -43,14 +45,17 @@ class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
}
}
else if (ibb->getAction() == IBB::Open) {
+ SWIFT_LOG(debug) << "IBB open received" << std::endl;
sendResponse(from, id, IBB::ref());
}
else if (ibb->getAction() == IBB::Close) {
+ SWIFT_LOG(debug) << "IBB close received" << std::endl;
sendResponse(from, id, IBB::ref());
session->finish(FileTransferError(FileTransferError::ClosedError));
}
return true;
}
+ SWIFT_LOG(debug) << "wrong from/sessionID: " << from << " == " << session->from << " / " <<ibb->getStreamID() << " == " << session->id << std::endl;
return false;
}
@@ -71,6 +76,8 @@ IBBReceiveSession::IBBReceiveSession(
size(size),
router(router),
active(false) {
+ assert(!id.empty());
+ assert(from.isValid());
responder = new IBBResponder(this, router);
}
@@ -82,11 +89,13 @@ IBBReceiveSession::~IBBReceiveSession() {
}
void IBBReceiveSession::start() {
+ SWIFT_LOG(debug) << "receive session started" << std::endl;
active = true;
responder->start();
}
void IBBReceiveSession::stop() {
+ SWIFT_LOG(debug) << "receive session stopped" << std::endl;
responder->stop();
if (active) {
if (router->isAvailable()) {
diff --git a/Swiften/FileTransfer/IBBSendSession.cpp b/Swiften/FileTransfer/IBBSendSession.cpp
index 5376276..a434cfb 100644
--- a/Swiften/FileTransfer/IBBSendSession.cpp
+++ b/Swiften/FileTransfer/IBBSendSession.cpp
@@ -35,7 +35,7 @@ void IBBSendSession::stop() {
}
void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
- if (!error) {
+ if (!error && active) {
if (!bytestream->isFinished()) {
try {
std::vector<unsigned char> data = bytestream->read(blockSize);
@@ -43,6 +43,7 @@ void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
sequenceNumber++;
request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
request->send();
+ onBytesSent(data.size());
}
catch (const BytestreamException&) {
finish(FileTransferError(FileTransferError::ReadError));
diff --git a/Swiften/FileTransfer/IBBSendSession.h b/Swiften/FileTransfer/IBBSendSession.h
index 6c741cf..325f66c 100644
--- a/Swiften/FileTransfer/IBBSendSession.h
+++ b/Swiften/FileTransfer/IBBSendSession.h
@@ -32,7 +32,7 @@ namespace Swift {
}
boost::signal<void (boost::optional<FileTransferError>)> onFinished;
-
+ boost::signal<void (int)> onBytesSent;
private:
void handleIBBResponse(IBB::ref, ErrorPayload::ref);
void finish(boost::optional<FileTransferError>);
diff --git a/Swiften/FileTransfer/IncomingFileTransfer.h b/Swiften/FileTransfer/IncomingFileTransfer.h
index 1ccd76c..a6cf05e 100644
--- a/Swiften/FileTransfer/IncomingFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingFileTransfer.h
@@ -9,15 +9,18 @@
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
#include <Swiften/FileTransfer/WriteBytestream.h>
namespace Swift {
- class IncomingFileTransfer {
+ class IncomingFileTransfer : public FileTransfer {
public:
typedef boost::shared_ptr<IncomingFileTransfer> ref;
virtual ~IncomingFileTransfer();
virtual void accept(WriteBytestream::ref) = 0;
+ virtual const JID& getSender() const = 0;
};
}
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.cpp b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
index 79d2391..c01c906 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.cpp
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
@@ -18,7 +18,9 @@
namespace Swift {
-IncomingFileTransferManager::IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router) : jingleSessionManager(jingleSessionManager), router(router) {
+IncomingFileTransferManager::IncomingFileTransferManager(const JID& ourFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router,
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+ LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory) : ourJID(ourFullJID), jingleSessionManager(jingleSessionManager), router(router), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), timerFactory(timerFactory) {
jingleSessionManager->addIncomingSessionHandler(this);
}
@@ -29,13 +31,19 @@ IncomingFileTransferManager::~IncomingFileTransferManager() {
bool IncomingFileTransferManager::handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents) {
if (JingleContentPayload::ref content = Jingle::getContentWithDescription<JingleFileTransferDescription>(contents)) {
if (content->getTransport<JingleIBBTransportPayload>() || content->getTransport<JingleS5BTransportPayload>()) {
- RemoteJingleTransportCandidateSelectorFactory* a;
- LocalJingleTransportCandidateGeneratorFactory* b;
- IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(session, content, a, b, router);
- onIncomingFileTransfer(transfer);
+
+ JingleFileTransferDescription::ref description = content->getDescription<JingleFileTransferDescription>();
+
+ if (description && description->getOffers().size() == 1) {
+ IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(ourJID, session, content, remoteFactory, localFactory, router, bytestreamRegistry, bytestreamProxy, timerFactory);
+ onIncomingFileTransfer(transfer);
+ } else {
+ std::cerr << "Received a file-transfer request with no description or more than one file!" << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedApplication);
+ }
}
else {
- session->terminate(JinglePayload::Reason::UnsupportedTransports);
+ session->sendTerminate(JinglePayload::Reason::UnsupportedTransports);
}
return true;
}
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.h b/Swiften/FileTransfer/IncomingFileTransferManager.h
index 428a838..d1573f6 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.h
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.h
@@ -17,10 +17,13 @@ namespace Swift {
class JingleSessionManager;
class RemoteJingleTransportCandidateSelectorFactory;
class LocalJingleTransportCandidateGeneratorFactory;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamProxy;
+ class TimerFactory;
class IncomingFileTransferManager : public IncomingJingleSessionHandler {
public:
- IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router);
+ IncomingFileTransferManager(const JID& ourFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory);
~IncomingFileTransferManager();
boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
@@ -29,7 +32,13 @@ namespace Swift {
bool handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents);
private:
+ JID ourJID;
JingleSessionManager* jingleSessionManager;
IQRouter* router;
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+ LocalJingleTransportCandidateGeneratorFactory* localFactory;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ TimerFactory* timerFactory;
};
}
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
index 904b53e..1189830 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
@@ -9,29 +9,47 @@
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
-#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
-#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
#include <Swiften/Elements/JingleIBBTransportPayload.h>
#include <Swiften/Elements/JingleS5BTransportPayload.h>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Queries/GenericRequest.h>
namespace Swift {
IncomingJingleFileTransfer::IncomingJingleFileTransfer(
+ const JID& ourJID,
JingleSession::ref session,
JingleContentPayload::ref content,
RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory,
LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory,
- IQRouter* router) :
+ IQRouter* router,
+ SOCKS5BytestreamRegistry* registry,
+ SOCKS5BytestreamProxy* proxy,
+ TimerFactory* timerFactory) :
+ ourJID(ourJID),
session(session),
router(router),
+ timerFactory(timerFactory),
initialContent(content),
- contentID(content->getName(), content->getCreator()),
state(Initial),
+ receivedBytes(0),
+ s5bRegistry(registry),
+ s5bProxy(proxy),
remoteTransportCandidateSelectFinished(false),
- localTransportCandidateSelectFinished(false) {
+ localTransportCandidateSelectFinished(false),
+ serverSession(0) {
candidateSelector = candidateSelectorFactory->createCandidateSelector();
candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
@@ -41,13 +59,26 @@ IncomingJingleFileTransfer::IncomingJingleFileTransfer(
session->onTransportInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
session->onTransportReplaceReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
- session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this));
+ session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
+ session->onSessionInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionInfoReceived, this, _1));
description = initialContent->getDescription<JingleFileTransferDescription>();
assert(description);
+ assert(description->getOffers().size() == 1);
+ StreamInitiationFileInfo fileInfo = description->getOffers().front();
+ fileSizeInBytes = fileInfo.getSize();
+ filename = fileInfo.getName();
+ hash = fileInfo.getHash();
+ algo = fileInfo.getAlgo();
+
+ waitOnHashTimer = timerFactory->createTimer(5000);
+ waitOnHashTimer->onTick.connect(boost::bind(&IncomingJingleFileTransfer::finishOffTransfer, this));
}
IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
+ stream->onWrite.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+ delete hashCalculator;
+
session->onSessionTerminateReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this));
session->onTransportReplaceReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
session->onTransportInfoReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
@@ -60,44 +91,87 @@ IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
}
void IncomingJingleFileTransfer::accept(WriteBytestream::ref stream) {
- assert(!stream);
+ assert(!this->stream);
this->stream = stream;
+ hashCalculator = new IncrementalBytestreamHashCalculator( algo == "md5" || hash.empty() , algo == "sha-1" || hash.empty() );
+ stream->onWrite.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+ stream->onWrite.connect(boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1));
+ onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>()) {
+ SWIFT_LOG(debug) << "Got IBB transport payload!" << std::endl;
setActiveTransport(createIBBTransport(ibbTransport));
- session->accept();
+ session->sendAccept(getContentID(), initialContent->getDescriptions()[0], ibbTransport);
}
else if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) {
+ SWIFT_LOG(debug) << "Got S5B transport payload!" << std::endl;
state = CreatingInitialTransports;
+ s5bSessionID = s5bTransport->getSessionID().empty() ? idGenerator.generateID() : s5bTransport->getSessionID();
+ s5bDestination = SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator());
+ s5bRegistry->addWriteBytestream(s5bDestination, stream);
+ fillCandidateMap(theirCandidates, s5bTransport);
candidateSelector->addRemoteTransportCandidates(s5bTransport);
- candidateGenerator->generateLocalTransportCandidates();
+ candidateSelector->setRequesterTargtet(session->getInitiator(), ourJID);
+ s5bTransport->setSessionID(s5bSessionID);
+ candidateGenerator->generateLocalTransportCandidates(s5bTransport);
}
else {
assert(false);
}
}
+const JID& IncomingJingleFileTransfer::getSender() const {
+ return session->getInitiator();
+}
+
+void IncomingJingleFileTransfer::cancel() {
+ session->sendTerminate(JinglePayload::Reason::Cancel);
+
+ if (activeTransport) activeTransport->stop();
+ if (serverSession) serverSession->stop();
+ if (clientSession) clientSession->stop();
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+}
void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates) {
if (state == CreatingInitialTransports) {
- if (!candidates) {
- localTransportCandidateSelectFinished = true;
+ if (JingleS5BTransportPayload::ref s5bCandidates = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(candidates)) {
+ //localTransportCandidateSelectFinished = true;
+ //JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
+ //emptyCandidates->setSessionID(s5bCandidates->getSessionID());
+ fillCandidateMap(ourCandidates, s5bCandidates);
+ session->sendAccept(getContentID(), initialContent->getDescriptions()[0], s5bCandidates);
+
+ state = NegotiatingTransport;
+ candidateSelector->selectCandidate();
}
- session->accept(candidates);
- state = NegotiatingTransport;
- candidateSelector->selectCandidate();
+ }
+ else {
+ SWIFT_LOG(debug) << "Unhandled state!" << std::endl;
}
}
void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) {
- remoteTransportCandidateSelectFinished = true;
- selectedRemoteTransportCandidate = transport;
- session->sendTransportInfo(contentID, transport);
- checkCandidateSelected();
+ SWIFT_LOG(debug) << std::endl;
+ if (state == Terminated) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+ //remoteTransportCandidateSelectFinished = true;
+ //selectedRemoteTransportCandidate = transport;
+ ourCandidate = s5bPayload;
+ //checkCandidateSelected();
+ decideOnUsedTransport();
+ session->sendTransportInfo(getContentID(), s5bPayload);
+ }
+ else {
+ SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+ }
}
void IncomingJingleFileTransfer::checkCandidateSelected() {
+ assert(false);
if (localTransportCandidateSelectFinished && remoteTransportCandidateSelectFinished) {
if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate) && candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) {
if (candidateGenerator->getPriority(selectedLocalTransportCandidate) > candidateSelector->getPriority(selectedRemoteTransportCandidate)) {
@@ -121,37 +195,293 @@ void IncomingJingleFileTransfer::checkCandidateSelected() {
void IncomingJingleFileTransfer::setActiveTransport(JingleTransport::ref transport) {
state = Transferring;
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
activeTransport = transport;
activeTransport->onDataReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1));
+ activeTransport->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
activeTransport->start();
}
-void IncomingJingleFileTransfer::handleSessionTerminateReceived() {
- // TODO
+bool IncomingJingleFileTransfer::verifyReceviedData() {
+ if (algo.empty() || hash.empty()) {
+ SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+ return true;
+ } else {
+ if (algo == "sha-1") {
+ SWIFT_LOG(debug) << "verify data via SHA-1 hash: " << (hash == hashCalculator->getSHA1String()) << std::endl;
+ return hash == hashCalculator->getSHA1String();
+ }
+ else if (algo == "md5") {
+ SWIFT_LOG(debug) << "verify data via MD5 hash: " << (hash == hashCalculator->getMD5String()) << std::endl;
+ return hash == hashCalculator->getMD5String();
+ }
+ else {
+ SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+ return true;
+ }
+ }
+}
+
+void IncomingJingleFileTransfer::finishOffTransfer() {
+ if (verifyReceviedData()) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ session->sendTerminate(JinglePayload::Reason::Success);
+ } else {
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
+ session->sendTerminate(JinglePayload::Reason::MediaError);
+ }
+ state = Terminated;
+ waitOnHashTimer->stop();
+}
+
+void IncomingJingleFileTransfer::handleSessionInfoReceived(JinglePayload::ref jinglePayload) {
+ if (state == Terminated) {
+ return;
+ }
+ JingleFileTransferHash::ref transferHash = jinglePayload->getPayload<JingleFileTransferHash>();
+ if (transferHash) {
+ SWIFT_LOG(debug) << "Recevied hash information." << std::endl;
+ if (transferHash->getHashes().find("sha-1") != transferHash->getHashes().end()) {
+ algo = "sha-1";
+ hash = transferHash->getHashes().at("sha-1");
+ }
+ else if (transferHash->getHashes().find("md5") != transferHash->getHashes().end()) {
+ algo = "md5";
+ hash = transferHash->getHashes().at("md5");
+ }
+ checkIfAllDataReceived();
+ }
+}
+
+void IncomingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
+ SWIFT_LOG(debug) << "session terminate received" << std::endl;
+ if (activeTransport) activeTransport->stop();
+ if (reason && reason.get().type == JinglePayload::Reason::Cancel) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Other user canceled the transfer."));
+ }
+ else if (reason && reason.get().type == JinglePayload::Reason::Success) {
+ /*if (verifyReceviedData()) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ } else {
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
+ }*/
+ }
state = Terminated;
}
+void IncomingJingleFileTransfer::checkIfAllDataReceived() {
+ if (receivedBytes == fileSizeInBytes) {
+ SWIFT_LOG(debug) << "All data received." << std::endl;
+ if (hash.empty()) {
+ SWIFT_LOG(debug) << "No hash information yet. Waiting 5 seconds on hash info." << std::endl;
+ waitOnHashTimer->start();
+ } else {
+ SWIFT_LOG(debug) << "We already have hash info using " << algo << " algorithm. Finishing off transfer." << std::endl;
+ finishOffTransfer();
+ }
+ }
+ else if (receivedBytes > fileSizeInBytes) {
+ SWIFT_LOG(debug) << "We got more than we could handle!" << std::endl;
+ }
+}
+
void IncomingJingleFileTransfer::handleTransportDataReceived(const std::vector<unsigned char>& data) {
+ SWIFT_LOG(debug) << data.size() << " bytes received" << std::endl;
+ onProcessedBytes(data.size());
stream->write(data);
+ receivedBytes += data.size();
+ checkIfAllDataReceived();
+}
+
+void IncomingJingleFileTransfer::handleWriteStreamDataReceived(const std::vector<unsigned char>& data) {
+ receivedBytes += data.size();
+ checkIfAllDataReceived();
+}
+
+void IncomingJingleFileTransfer::useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << std::endl;
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // get proxy client session from remoteCandidateSelector
+ clientSession = candidateSelector->getS5BSession();
+
+ // wait on <activated/> transport-info
+ } else {
+ // ask s5b client
+ clientSession = candidateSelector->getS5BSession();
+ if (clientSession) {
+ state = Transferring;
+ SWIFT_LOG(debug) << clientSession->getAddressPort().toString() << std::endl;
+ clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startReceiving(stream);
+ } else {
+ SWIFT_LOG(debug) << "No S5B client session found!!!" << std::endl;
+ }
+ }
}
+void IncomingJingleFileTransfer::useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << std::endl;
+
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // get proxy client session from s5bRegistry
+ clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator()));
+ clientSession->onSessionReady.connect(boost::bind(&IncomingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
+ clientSession->start();
+
+ // on reply send activate
+ } else {
+ // ask s5b server
+ serverSession = s5bRegistry->getConnectedSession(s5bDestination);
+ if (serverSession) {
+ state = Transferring;
+ serverSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ serverSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ serverSession->startTransfer();
+ } else {
+ SWIFT_LOG(debug) << "No S5B server session found!!!" << std::endl;
+ }
+ }
+}
+
+void IncomingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
+ map.clear();
+ foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
+ map[candidate.cid] = candidate;
+ }
+}
+
+
+void IncomingJingleFileTransfer::decideOnUsedTransport() {
+ if (ourCandidate && theirCandidate) {
+ if (ourCandidate->hasCandidateError() && theirCandidate->hasCandidateError()) {
+ state = WaitingForFallbackOrTerminate;
+ return;
+ }
+ std::string our_cid = ourCandidate->getCandidateUsed();
+ std::string their_cid = theirCandidate->getCandidateUsed();
+ if (ourCandidate->hasCandidateError() && !their_cid.empty()) {
+ useTheirCandidateChoiceForTransfer(ourCandidates[their_cid]);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else if (theirCandidate->hasCandidateError() && !our_cid.empty()) {
+ useOurCandidateChoiceForTransfer(theirCandidates[our_cid]);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else if (!our_cid.empty() && !their_cid.empty()) {
+ // compare priorites, if same they win
+ if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
+ SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedTransport);
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Failed to negotiate candidate."));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ return;
+ }
+
+ JingleS5BTransportPayload::Candidate our_candidate = theirCandidates[our_cid];
+ JingleS5BTransportPayload::Candidate their_candidate = ourCandidates[their_cid];
+ if (our_candidate.priority > their_candidate.priority) {
+ useOurCandidateChoiceForTransfer(our_candidate);
+ }
+ else if (our_candidate.priority < their_candidate.priority) {
+ useTheirCandidateChoiceForTransfer(their_candidate);
+ }
+ else {
+ useTheirCandidateChoiceForTransfer(their_candidate);
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else {
+ assert(false);
+ }
+ } else {
+ SWIFT_LOG(debug) << "Can't make a transport decision yet." << std::endl;
+ }
+}
+
+void IncomingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
+ if (error) {
+ // indicate proxy error
+ } else {
+ // activate proxy
+ activateProxySession(proxy);
+ }
+}
+
+void IncomingJingleFileTransfer::activateProxySession(const JID &proxy) {
+ S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+ proxyRequest->setSID(s5bSessionID);
+ proxyRequest->setActivate(session->getInitiator());
+
+ boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
+ request->onResponse.connect(boost::bind(&IncomingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
+ request->send();
+}
+
+void IncomingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+ SWIFT_LOG(debug) << std::endl;
+ if (error) {
+ SWIFT_LOG(debug) << "ERROR" << std::endl;
+ } else {
+ // send activated to other jingle party
+ JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
+ proxyActivate->setActivated(theirCandidate->getCandidateUsed());
+ session->sendTransportInfo(getContentID(), proxyActivate);
+
+ // start transferring
+ clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startReceiving(stream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+}
void IncomingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref transport) {
- localTransportCandidateSelectFinished = true;
+ SWIFT_LOG(debug) << "transport info received" << std::endl;
+ if (state == Terminated) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+ if (!s5bPayload->getActivated().empty()) {
+ if (ourCandidate->getCandidateUsed() == s5bPayload->getActivated()) {
+ clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startReceiving(stream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ } else {
+ SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
+ JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
+ proxyError->setProxyError(true);
+ proxyError->setSessionID(s5bSessionID);
+ session->sendTransportInfo(getContentID(), proxyError);
+ }
+ } else {
+ theirCandidate = s5bPayload;
+ decideOnUsedTransport();
+ }
+ }
+ else {
+ SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+ }
+ /*localTransportCandidateSelectFinished = true;
selectedLocalTransportCandidate = transport;
if (candidateGenerator->isActualCandidate(transport)) {
candidateSelector->setMinimumPriority(candidateGenerator->getPriority(transport));
- }
- checkCandidateSelected();
+ }*/
+ //checkCandidateSelected();
}
void IncomingJingleFileTransfer::handleTransportReplaceReceived(const JingleContentID& content, JingleTransportPayload::ref transport) {
+ if (state == Terminated) {
+ return;
+ }
if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+ SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl;
setActiveTransport(createIBBTransport(ibbTransport));
- session->acceptTransport(content, transport);
- }
- else {
- session->rejectTransport(content, transport);
+ session->sendTransportAccept(content, ibbTransport);
+ } else {
+ SWIFT_LOG(debug) << "transport replaced failed" << std::endl;
+ session->sendTransportReject(content, transport);
}
}
@@ -163,7 +493,25 @@ void IncomingJingleFileTransfer::stopActiveTransport() {
}
JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) {
- return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffer()->size, router);
+ // TODO: getOffer() -> getOffers correction
+ return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffers()[0].getSize(), router);
+}
+
+JingleContentID IncomingJingleFileTransfer::getContentID() const {
+ return JingleContentID(initialContent->getName(), initialContent->getCreator());
+}
+
+void IncomingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+ if (state == Terminated) {
+ return;
+ }
+
+ if (error) {
+ session->sendTerminate(JinglePayload::Reason::ConnectivityError);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(error);
+ }
+ //
}
}
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.h b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
index 164d868..4ae0d1d 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
@@ -8,14 +8,21 @@
#include <boost/shared_ptr.hpp>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Network/Timer.h>
#include <Swiften/Jingle/JingleSession.h>
#include <Swiften/Jingle/JingleContentID.h>
#include <Swiften/FileTransfer/IncomingFileTransfer.h>
#include <Swiften/FileTransfer/JingleTransport.h>
#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
#include <Swiften/Elements/JingleContentPayload.h>
#include <Swiften/Elements/JingleFileTransferDescription.h>
#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Elements/ErrorPayload.h>
namespace Swift {
class IQRouter;
@@ -23,6 +30,9 @@ namespace Swift {
class LocalJingleTransportCandidateGeneratorFactory;
class RemoteJingleTransportCandidateSelector;
class LocalJingleTransportCandidateGenerator;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamProxy;
+ class IncrementalBytestreamHashCalculator;
class IncomingJingleFileTransfer : public IncomingFileTransfer {
public:
@@ -37,42 +47,86 @@ namespace Swift {
};
IncomingJingleFileTransfer(
+ const JID& ourJID,
JingleSession::ref,
JingleContentPayload::ref content,
RemoteJingleTransportCandidateSelectorFactory*,
LocalJingleTransportCandidateGeneratorFactory*,
- IQRouter* router);
+ IQRouter* router,
+ SOCKS5BytestreamRegistry* bytestreamRegistry,
+ SOCKS5BytestreamProxy* bytestreamProxy,
+ TimerFactory*);
~IncomingJingleFileTransfer();
virtual void accept(WriteBytestream::ref);
+ virtual const JID& getSender() const;
+ void cancel();
private:
- void handleSessionTerminateReceived();
+ void handleSessionTerminateReceived(boost::optional<JinglePayload::Reason>);
+ void handleSessionInfoReceived(JinglePayload::ref);
void handleTransportReplaceReceived(const JingleContentID&, JingleTransportPayload::ref);
void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref);
void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates);
void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref candidate);
void setActiveTransport(JingleTransport::ref transport);
void handleTransportDataReceived(const std::vector<unsigned char>& data);
+ void handleWriteStreamDataReceived(const std::vector<unsigned char>& data);
void stopActiveTransport();
void checkCandidateSelected();
JingleIncomingIBBTransport::ref createIBBTransport(JingleIBBTransportPayload::ref ibbTransport);
+ JingleContentID getContentID() const;
+ void checkIfAllDataReceived();
+ bool verifyReceviedData();
+ void finishOffTransfer();
+ void handleTransferFinished(boost::optional<FileTransferError>);
private:
+ typedef std::map<std::string, JingleS5BTransportPayload::Candidate> CandidateMap;
+
+ private:
+ void activateProxySession(const JID &proxy);
+ void handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error);
+ void proxySessionReady(const JID& proxy, bool error);
+
+ private:
+ void useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate);
+ void useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate);
+ void decideOnUsedTransport();
+ void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload);
+
+ private:
+ JID ourJID;
JingleSession::ref session;
IQRouter* router;
+ TimerFactory* timerFactory;
JingleContentPayload::ref initialContent;
- JingleContentID contentID;
State state;
JingleFileTransferDescription::ref description;
WriteBytestream::ref stream;
+ uintmax_t receivedBytes;
+ IncrementalBytestreamHashCalculator* hashCalculator;
+ Timer::ref waitOnHashTimer;
+ IDGenerator idGenerator;
+
RemoteJingleTransportCandidateSelector* candidateSelector;
LocalJingleTransportCandidateGenerator* candidateGenerator;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
bool remoteTransportCandidateSelectFinished;
JingleTransportPayload::ref selectedRemoteTransportCandidate;
bool localTransportCandidateSelectFinished;
JingleTransportPayload::ref selectedLocalTransportCandidate;
+ JingleS5BTransportPayload::ref ourCandidate;
+ JingleS5BTransportPayload::ref theirCandidate;
+ CandidateMap ourCandidates;
+ CandidateMap theirCandidates;
+ SOCKS5BytestreamClientSession::ref clientSession;
+ std::string s5bDestination;
+ std::string s5bSessionID;
+ SOCKS5BytestreamServerSession* serverSession;
+
JingleTransport::ref activeTransport;
};
}
diff --git a/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp
new file mode 100644
index 0000000..6b53a1b
--- /dev/null
+++ b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/StringCodecs/MD5.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+namespace Swift {
+
+IncrementalBytestreamHashCalculator::IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1) {
+ md5Hasher = doMD5 ? new MD5() : NULL;
+ sha1Hasher = doSHA1 ? new SHA1() : NULL;
+}
+
+IncrementalBytestreamHashCalculator::~IncrementalBytestreamHashCalculator() {
+ delete md5Hasher;
+ delete sha1Hasher;
+}
+
+void IncrementalBytestreamHashCalculator::feedData(const ByteArray& data) {
+ if (md5Hasher) {
+ md5Hasher->update(data);
+ }
+ if (sha1Hasher) {
+ sha1Hasher->update(data);
+ }
+}
+/*
+void IncrementalBytestreamHashCalculator::feedData(const SafeByteArray& data) {
+ if (md5Hasher) {
+ md5Hasher->update(createByteArray(data.data(), data.size()));
+ }
+ if (sha1Hasher) {
+ sha1Hasher->update(createByteArray(data.data(), data.size()));
+ }
+}*/
+
+std::string IncrementalBytestreamHashCalculator::getSHA1String() {
+ if (sha1Hasher) {
+ ByteArray result = sha1Hasher->getHash();
+ return Hexify::hexify(result);
+ } else {
+ return std::string();
+ }
+}
+
+std::string IncrementalBytestreamHashCalculator::getMD5String() {
+ if (md5Hasher) {
+ ByteArray result = md5Hasher->getHash();
+ return Hexify::hexify(result);
+ } else {
+ return std::string();
+ }
+}
+
+}
diff --git a/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h
new file mode 100644
index 0000000..64f4b5f
--- /dev/null
+++ b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/SafeByteArray.h>
+
+namespace Swift {
+
+class MD5;
+class SHA1;
+
+class IncrementalBytestreamHashCalculator {
+public:
+ IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1);
+ ~IncrementalBytestreamHashCalculator();
+
+ void feedData(const ByteArray& data);
+ //void feedData(const SafeByteArray& data);
+
+ std::string getSHA1String();
+ std::string getMD5String();
+
+private:
+ MD5* md5Hasher;
+ SHA1* sha1Hasher;
+};
+
+}
diff --git a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
index 0ca899f..9b5c354 100644
--- a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
+++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
@@ -8,8 +8,9 @@
namespace Swift {
-JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(from, id, size, router) {
+JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(id, from, size, router) {
ibbSession.onDataReceived.connect(boost::ref(onDataReceived));
+ ibbSession.onFinished.connect(boost::ref(onFinished));
}
void JingleIncomingIBBTransport::start() {
diff --git a/Swiften/FileTransfer/JingleTransport.h b/Swiften/FileTransfer/JingleTransport.h
index 1d163d0..fa296e8 100644
--- a/Swiften/FileTransfer/JingleTransport.h
+++ b/Swiften/FileTransfer/JingleTransport.h
@@ -9,6 +9,7 @@
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
namespace Swift {
class JingleTransport {
@@ -21,5 +22,6 @@ namespace Swift {
virtual void stop() = 0;
boost::signal<void (const std::vector<unsigned char>&)> onDataReceived;
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
};
}
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
index c111005..041afe3 100644
--- a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
@@ -15,8 +15,10 @@ namespace Swift {
class LocalJingleTransportCandidateGenerator {
public:
virtual ~LocalJingleTransportCandidateGenerator();
-
- virtual void generateLocalTransportCandidates() = 0;
+ /**
+ * Should call onLocalTransportCandidatesGenerated if it has finished discovering local candidates.
+ */
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref) = 0;
virtual bool isActualCandidate(JingleTransportPayload::ref) = 0;
virtual int getPriority(JingleTransportPayload::ref) = 0;
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.h b/Swiften/FileTransfer/OutgoingFileTransfer.h
index a8c1e81..1ec1ae3 100644
--- a/Swiften/FileTransfer/OutgoingFileTransfer.h
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.h
@@ -6,8 +6,14 @@
#pragma once
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/FileTransfer/FileTransfer.h>
+
namespace Swift {
- class OutgoingFileTransfer {
+ class OutgoingFileTransfer : public FileTransfer {
+ public:
+ typedef boost::shared_ptr<OutgoingFileTransfer> ref;
public:
virtual ~OutgoingFileTransfer();
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.cpp b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
new file mode 100644
index 0000000..321a57a
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "OutgoingFileTransferManager.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Jingle/JingleSessionImpl.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
+#include <Swiften/Base/IDGenerator.h>
+
+namespace Swift {
+
+OutgoingFileTransferManager::OutgoingFileTransferManager(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy) : ownJID(ownFullJID), jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy) {
+ idGenerator = new IDGenerator();
+}
+
+OutgoingFileTransferManager::~OutgoingFileTransferManager() {
+ delete idGenerator;
+}
+
+boost::shared_ptr<OutgoingFileTransfer> OutgoingFileTransferManager::createOutgoingFileTransfer(const JID& receipient, boost::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
+ // check if receipient support Jingle FT
+
+
+ JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(ownJID, receipient, idGenerator->generateID(), iqRouter);
+
+ //jsManager->getSession(receipient, idGenerator->generateID());
+ assert(jingleSession);
+ jsManager->registerOutgoingSession(ownJID, jingleSession);
+ boost::shared_ptr<OutgoingJingleFileTransfer> jingleFT = boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy));
+
+ // otherwise try SI
+
+ // else fail
+
+ return jingleFT;
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.h b/Swiften/FileTransfer/OutgoingFileTransferManager.h
new file mode 100644
index 0000000..d57c14d
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class JingleSessionManager;
+class IQRouter;
+class EntityCapsProvider;
+class RemoteJingleTransportCandidateSelectorFactory;
+class LocalJingleTransportCandidateGeneratorFactory;
+class OutgoingFileTransfer;
+class JID;
+class IDGenerator;
+class ReadBytestream;
+class StreamInitiationFileInfo;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class OutgoingFileTransferManager {
+public:
+ OutgoingFileTransferManager(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy);
+ ~OutgoingFileTransferManager();
+
+ boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID&, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
+
+private:
+ JID ownJID;
+ JingleSessionManager* jsManager;
+ IQRouter* iqRouter;
+ EntityCapsProvider* capsProvider;
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+ LocalJingleTransportCandidateGeneratorFactory* localFactory;
+ IDGenerator *idGenerator;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+};
+
+}
diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
new file mode 100644
index 0000000..9b71165
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "OutgoingJingleFileTransfer.h"
+
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JingleTransportPayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/FileTransfer/IBBSendSession.h>
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+OutgoingJingleFileTransfer::OutgoingJingleFileTransfer(JingleSession::ref session,
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+ LocalJingleTransportCandidateGeneratorFactory* localFactory,
+ IQRouter* router,
+ IDGenerator *idGenerator,
+ const JID& toJID,
+ boost::shared_ptr<ReadBytestream> readStream,
+ const StreamInitiationFileInfo &fileInfo,
+ SOCKS5BytestreamRegistry* bytestreamRegistry,
+ SOCKS5BytestreamProxy* bytestreamProxy) :
+ session(session), remoteFactory(remoteFactory), localFactory(localFactory), router(router), idGenerator(idGenerator), toJID(toJID), readStream(readStream), fileInfo(fileInfo), s5bRegistry(bytestreamRegistry), s5bProxy(bytestreamProxy), serverSession(NULL), contentID(JingleContentID(idGenerator->generateID(), JingleContentPayload::InitiatorCreator)), canceled(false) {
+ session->onSessionAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionAcceptReceived, this, _1, _2, _3));
+ session->onSessionTerminateReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
+ session->onTransportInfoReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
+ session->onTransportAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportAcceptReceived, this, _1, _2));
+ fileSizeInBytes = fileInfo.getSize();
+ filename = fileInfo.getName();
+
+ localCandidateGenerator = localFactory->createCandidateGenerator();
+ localCandidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
+
+ remoteCandidateSelector = remoteFactory->createCandidateSelector();
+ remoteCandidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
+ // calculate both, MD5 and SHA-1 since we don't know which one the other side supports
+ hashCalculator = new IncrementalBytestreamHashCalculator(true, true);
+ this->readStream->onRead.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+}
+
+OutgoingJingleFileTransfer::~OutgoingJingleFileTransfer() {
+ readStream->onRead.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+ delete hashCalculator;
+}
+
+void OutgoingJingleFileTransfer::start() {
+ onStateChange(FileTransfer::State(FileTransfer::State::WaitingForStart));
+
+ s5bSessionID = s5bRegistry->generateSessionID();
+ SWIFT_LOG(debug) << "S5B SessionID: " << s5bSessionID << std::endl;
+
+ //s5bProxy->connectToProxies(s5bSessionID);
+
+ JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>();
+ localCandidateGenerator->generateLocalTransportCandidates(transport);
+}
+
+void OutgoingJingleFileTransfer::stop() {
+
+}
+
+void OutgoingJingleFileTransfer::cancel() {
+ canceled = true;
+ session->sendTerminate(JinglePayload::Reason::Cancel);
+
+ if (ibbSession) {
+ ibbSession->stop();
+ }
+ SOCKS5BytestreamServerSession *serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+ if (serverSession) {
+ serverSession->stop();
+ }
+ if (clientSession) {
+ clientSession->stop();
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+}
+
+void OutgoingJingleFileTransfer::handleSessionAcceptReceived(const JingleContentID& id, JingleDescription::ref /* decription */, JingleTransportPayload::ref transportPayload) {
+ if (canceled) {
+ return;
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
+
+ JingleIBBTransportPayload::ref ibbPayload;
+ JingleS5BTransportPayload::ref s5bPayload;
+ if ((ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload))) {
+ ibbSession = boost::make_shared<IBBSendSession>(ibbPayload->getSessionID(), toJID, readStream, router);
+ ibbSession->setBlockSize(ibbPayload->getBlockSize());
+ ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ ibbSession->start();
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else if ((s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload))) {
+ fillCandidateMap(theirCandidates, s5bPayload);
+ remoteCandidateSelector->setRequesterTargtet(toJID, session->getInitiator());
+ remoteCandidateSelector->addRemoteTransportCandidates(s5bPayload);
+ remoteCandidateSelector->selectCandidate();
+ }
+ else {
+ // TODO: error handling
+ SWIFT_LOG(debug) << "Unknown transport payload! Try replaceing with IBB." << std::endl;
+ replaceTransportWithIBB(id);
+ }
+}
+
+void OutgoingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
+ if (canceled) {
+ return;
+ }
+
+ if (ibbSession) {
+ ibbSession->stop();
+ }
+ if (clientSession) {
+ clientSession->stop();
+ }
+ if (serverSession) {
+ serverSession->stop();
+ }
+
+ if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Cancel) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ } else if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Success) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ onFinished(boost::optional<FileTransferError>());
+ } else {
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ }
+ canceled = true;
+}
+
+void OutgoingJingleFileTransfer::handleTransportAcceptReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) {
+ if (canceled) {
+ return;
+ }
+
+ if (JingleIBBTransportPayload::ref ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+ ibbSession = boost::make_shared<IBBSendSession>(ibbPayload->getSessionID(), toJID, readStream, router);
+ ibbSession->setBlockSize(ibbPayload->getBlockSize());
+ ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ ibbSession->start();
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ } else {
+ // error handling
+ SWIFT_LOG(debug) << "Replacing with anything other than IBB isn't supported yet." << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedTransport);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ }
+}
+
+void OutgoingJingleFileTransfer::startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << "Transferring data using our candidate." << std::endl;
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // get proxy client session from remoteCandidateSelector
+ clientSession = remoteCandidateSelector->getS5BSession();
+
+ // wait on <activated/> transport-info
+ } else {
+ clientSession = remoteCandidateSelector->getS5BSession();
+ clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startSending(readStream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ assert(clientSession);
+}
+
+void OutgoingJingleFileTransfer::startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << "Transferring data using their candidate." << std::endl;
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // connect to proxy
+ clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+ clientSession->onSessionReady.connect(boost::bind(&OutgoingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
+ clientSession->start();
+
+ // on reply send activate
+
+ } else {
+ serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+ serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ serverSession->startTransfer();
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+}
+
+// decide on candidates according to http://xmpp.org/extensions/xep-0260.html#complete
+void OutgoingJingleFileTransfer::decideOnCandidates() {
+ if (ourCandidateChoice && theirCandidateChoice) {
+ std::string our_cid = ourCandidateChoice->getCandidateUsed();
+ std::string their_cid = theirCandidateChoice->getCandidateUsed();
+ if (ourCandidateChoice->hasCandidateError() && theirCandidateChoice->hasCandidateError()) {
+ replaceTransportWithIBB(contentID);
+ }
+ else if (!our_cid.empty() && theirCandidateChoice->hasCandidateError()) {
+ // use our candidate
+ startTransferViaOurCandidateChoice(theirCandidates[our_cid]);
+ }
+ else if (!their_cid.empty() && ourCandidateChoice->hasCandidateError()) {
+ // use their candidate
+ startTransferViaTheirCandidateChoice(ourCandidates[their_cid]);
+ }
+ else if (!our_cid.empty() && !their_cid.empty()) {
+ // compare priorites, if same we win
+ if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
+ SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedTransport);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ return;
+ }
+
+ JingleS5BTransportPayload::Candidate ourCandidate = theirCandidates[our_cid];
+ JingleS5BTransportPayload::Candidate theirCandidate = ourCandidates[their_cid];
+ if (ourCandidate.priority > theirCandidate.priority) {
+ startTransferViaOurCandidateChoice(ourCandidate);
+ }
+ else if (ourCandidate.priority < theirCandidate.priority) {
+ startTransferViaTheirCandidateChoice(theirCandidate);
+ }
+ else {
+ startTransferViaOurCandidateChoice(ourCandidate);
+ }
+ }
+ } else {
+ SWIFT_LOG(debug) << "Can't make a decision yet!" << std::endl;
+ }
+}
+
+void OutgoingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
+ map.clear();
+ foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
+ map[candidate.cid] = candidate;
+ }
+}
+
+void OutgoingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
+ if (error) {
+ // indicate proxy error
+ } else {
+ // activate proxy
+ activateProxySession(proxy);
+ }
+}
+
+void OutgoingJingleFileTransfer::activateProxySession(const JID& proxy) {
+ S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+ proxyRequest->setSID(s5bSessionID);
+ proxyRequest->setActivate(toJID);
+
+ boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
+ request->onResponse.connect(boost::bind(&OutgoingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
+ request->send();
+}
+
+void OutgoingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+ if (error) {
+ SWIFT_LOG(debug) << "ERROR" << std::endl;
+ } else {
+ // send activated to other jingle party
+ JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
+ proxyActivate->setActivated(theirCandidateChoice->getCandidateUsed());
+ session->sendTransportInfo(contentID, proxyActivate);
+
+ // start transferring
+ clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startSending(readStream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+}
+
+void OutgoingJingleFileTransfer::sendSessionInfoHash() {
+ SWIFT_LOG(debug) << std::endl;
+ JingleFileTransferHash::ref hashElement = boost::make_shared<JingleFileTransferHash>();
+ hashElement->setHash("sha-1", hashCalculator->getSHA1String());
+ hashElement->setHash("md5", hashCalculator->getMD5String());
+ session->sendInfo(hashElement);
+}
+
+void OutgoingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) {
+ if (canceled) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+ if (s5bPayload->hasCandidateError() || !s5bPayload->getCandidateUsed().empty()) {
+ theirCandidateChoice = s5bPayload;
+ decideOnCandidates();
+ } else if(!s5bPayload->getActivated().empty()) {
+ if (ourCandidateChoice->getCandidateUsed() == s5bPayload->getActivated()) {
+ clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startSending(readStream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ } else {
+ SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
+ JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
+ proxyError->setProxyError(true);
+ proxyError->setSessionID(s5bSessionID);
+ session->sendTransportInfo(contentID, proxyError);
+ }
+ }
+ }
+}
+
+void OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+ if (canceled) {
+ return;
+ }
+ JingleFileTransferDescription::ref description = boost::make_shared<JingleFileTransferDescription>();
+ description->addOffer(fileInfo);
+
+ JingleTransportPayload::ref transport;
+ if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(payload)) {
+ ibbTransport->setBlockSize(4096);
+ ibbTransport->setSessionID(idGenerator->generateID());
+ transport = ibbTransport;
+ }
+ else if (JingleS5BTransportPayload::ref s5bTransport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(payload)) {
+ //fillCandidateMap(ourCandidates, s5bTransport);
+ //s5bTransport->setSessionID(s5bSessionID);
+
+ JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
+ emptyCandidates->setSessionID(s5bTransport->getSessionID());
+ fillCandidateMap(ourCandidates, emptyCandidates);
+
+ transport = emptyCandidates;
+ s5bRegistry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID), readStream);
+ }
+ else {
+ SWIFT_LOG(debug) << "Unknown tranport payload: " << typeid(*payload).name() << std::endl;
+ return;
+ }
+ session->sendInitiate(contentID, description, transport);
+ onStateChange(FileTransfer::State(FileTransfer::State::WaitingForAccept));
+}
+
+void OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref payload) {
+ if (canceled) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(payload)) {
+ ourCandidateChoice = s5bPayload;
+ session->sendTransportInfo(contentID, s5bPayload);
+ decideOnCandidates();
+ }
+}
+
+void OutgoingJingleFileTransfer::replaceTransportWithIBB(const JingleContentID& id) {
+ SWIFT_LOG(debug) << "Both parties failed. Replace transport with IBB." << std::endl;
+ JingleIBBTransportPayload::ref ibbTransport = boost::make_shared<JingleIBBTransportPayload>();
+ ibbTransport->setBlockSize(4096);
+ ibbTransport->setSessionID(idGenerator->generateID());
+ session->sendTransportReplace(id, ibbTransport);
+}
+
+void OutgoingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+ if (error) {
+ session->sendTerminate(JinglePayload::Reason::ConnectivityError);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(error);
+ } else {
+ sendSessionInfoHash();
+ /*
+ session->terminate(JinglePayload::Reason::Success);
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ */
+ }
+ //
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.h b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
new file mode 100644
index 0000000..fecfbdb
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Jingle/JingleSession.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+namespace Swift {
+
+class RemoteJingleTransportCandidateSelectorFactory;
+class RemoteJingleTransportCandidateSelector;
+class LocalJingleTransportCandidateGeneratorFactory;
+class LocalJingleTransportCandidateGenerator;
+class IQRouter;
+class ReadBytestream;
+class IBBSendSession;
+class IDGenerator;
+class IncrementalBytestreamHashCalculator;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class OutgoingJingleFileTransfer : public OutgoingFileTransfer {
+public:
+ OutgoingJingleFileTransfer(JingleSession::ref,
+ RemoteJingleTransportCandidateSelectorFactory*,
+ LocalJingleTransportCandidateGeneratorFactory*,
+ IQRouter*,
+ IDGenerator*,
+ const JID&,
+ boost::shared_ptr<ReadBytestream>,
+ const StreamInitiationFileInfo&,
+ SOCKS5BytestreamRegistry*,
+ SOCKS5BytestreamProxy*);
+ virtual ~OutgoingJingleFileTransfer();
+
+ void start();
+ void stop();
+
+ void cancel();
+
+private:
+ void handleSessionAcceptReceived(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+ void handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason);
+ void handleTransportAcceptReceived(const JingleContentID&, JingleTransportPayload::ref);
+ void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref);
+
+ void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref);
+ void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref);
+
+private:
+ void replaceTransportWithIBB(const JingleContentID&);
+ void handleTransferFinished(boost::optional<FileTransferError>);
+ void activateProxySession(const JID &proxy);
+ void handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error);
+ void proxySessionReady(const JID& proxy, bool error);
+
+private:
+ typedef std::map<std::string, JingleS5BTransportPayload::Candidate> CandidateMap;
+
+private:
+ void startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate);
+ void startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate);
+ void decideOnCandidates();
+ void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload);
+
+private:
+ void sendSessionInfoHash();
+
+private:
+ JingleSession::ref session;
+ RemoteJingleTransportCandidateSelector* remoteCandidateSelector;
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+ LocalJingleTransportCandidateGenerator* localCandidateGenerator;
+ LocalJingleTransportCandidateGeneratorFactory* localFactory;
+
+ IQRouter* router;
+ IDGenerator* idGenerator;
+ JID toJID;
+ boost::shared_ptr<ReadBytestream> readStream;
+ StreamInitiationFileInfo fileInfo;
+ IncrementalBytestreamHashCalculator *hashCalculator;
+
+ boost::shared_ptr<IBBSendSession> ibbSession;
+
+ JingleS5BTransportPayload::ref ourCandidateChoice;
+ JingleS5BTransportPayload::ref theirCandidateChoice;
+ CandidateMap ourCandidates;
+ CandidateMap theirCandidates;
+
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ SOCKS5BytestreamClientSession::ref clientSession;
+ SOCKS5BytestreamServerSession* serverSession;
+ JingleContentID contentID;
+ std::string s5bSessionID;
+
+ bool canceled;
+};
+
+}
diff --git a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
index 85ac505..dfcf028 100644
--- a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
+++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
@@ -38,7 +38,7 @@ void OutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiat
}
else {
if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
- socksServer->addBytestream(id, from, to, bytestream);
+ socksServer->addReadBytestream(id, from, to, bytestream);
Bytestreams::ref bytestreams(new Bytestreams());
bytestreams->setStreamID(id);
HostAddressPort addressPort = socksServer->getAddressPort();
@@ -67,7 +67,7 @@ void OutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
ibbSession->onFinished.disconnect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
ibbSession.reset();
}
- socksServer->removeBytestream(id, from, to);
+ socksServer->removeReadBytestream(id, from, to);
onFinished(error);
}
diff --git a/Swiften/FileTransfer/ReadBytestream.h b/Swiften/FileTransfer/ReadBytestream.h
index 2601192..9e070f7 100644
--- a/Swiften/FileTransfer/ReadBytestream.h
+++ b/Swiften/FileTransfer/ReadBytestream.h
@@ -9,11 +9,16 @@
#include <vector>
#include <cstring>
+#include <Swiften/Base/boost_bsignals.h>
+
namespace Swift {
class ReadBytestream {
public:
virtual ~ReadBytestream();
virtual std::vector<unsigned char> read(size_t size) = 0;
virtual bool isFinished() const = 0;
+
+ public:
+ boost::signal<void (std::vector<unsigned char>)> onRead;
};
}
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
index b12b06b..f8df8f9 100644
--- a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
@@ -8,8 +8,10 @@
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
#include <Swiften/Elements/JingleTransportPayload.h>
#include <Swiften/FileTransfer/JingleTransport.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
namespace Swift {
class RemoteJingleTransportCandidateSelector {
@@ -19,6 +21,8 @@ namespace Swift {
virtual void addRemoteTransportCandidates(JingleTransportPayload::ref) = 0;
virtual void selectCandidate() = 0;
virtual void setMinimumPriority(int) = 0;
+ virtual void setRequesterTargtet(const JID&, const JID&) {}
+ virtual SOCKS5BytestreamClientSession::ref getS5BSession() { return SOCKS5BytestreamClientSession::ref(); }
virtual bool isActualCandidate(JingleTransportPayload::ref) = 0;
virtual int getPriority(JingleTransportPayload::ref) = 0;
diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript
index 24fc9e8..ef02557 100644
--- a/Swiften/FileTransfer/SConscript
+++ b/Swiften/FileTransfer/SConscript
@@ -3,6 +3,9 @@ Import("swiften_env", "env")
sources = [
"OutgoingFileTransfer.cpp",
"OutgoingSIFileTransfer.cpp",
+ "OutgoingJingleFileTransfer.cpp",
+ "OutgoingFileTransferManager.cpp",
+ "OutgoingFileTransferManager.cpp",
"IncomingFileTransfer.cpp",
"IncomingJingleFileTransfer.cpp",
"IncomingFileTransferManager.cpp",
@@ -10,23 +13,36 @@ sources = [
"RemoteJingleTransportCandidateSelectorFactory.cpp",
"LocalJingleTransportCandidateGenerator.cpp",
"LocalJingleTransportCandidateGeneratorFactory.cpp",
+ "DefaultRemoteJingleTransportCandidateSelectorFactory.cpp",
+ "DefaultLocalJingleTransportCandidateGeneratorFactory.cpp",
+ "DefaultRemoteJingleTransportCandidateSelector.cpp",
+ "DefaultLocalJingleTransportCandidateGenerator.cpp",
"JingleTransport.cpp",
"JingleIncomingIBBTransport.cpp",
"ReadBytestream.cpp",
"WriteBytestream.cpp",
"FileReadBytestream.cpp",
"FileWriteBytestream.cpp",
+ "SOCKS5BytestreamClientSession.cpp",
"SOCKS5BytestreamServer.cpp",
"SOCKS5BytestreamServerSession.cpp",
"SOCKS5BytestreamRegistry.cpp",
+ "SOCKS5BytestreamProxy.cpp",
"IBBSendSession.cpp",
"IBBReceiveSession.cpp",
+ "FileTransferManager.cpp",
+ "FileTransferManagerImpl.cpp",
+ "IncrementalBytestreamHashCalculator.cpp",
+ "ConnectivityManager.cpp",
]
swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
env.Append(UNITTEST_SOURCES = [
File("UnitTest/SOCKS5BytestreamServerSessionTest.cpp"),
+ File("UnitTest/SOCKS5BytestreamClientSessionTest.cpp"),
File("UnitTest/IBBSendSessionTest.cpp"),
File("UnitTest/IBBReceiveSessionTest.cpp"),
+ File("UnitTest/IncomingJingleFileTransferTest.cpp"),
+ File("UnitTest/OutgoingJingleFileTransferTest.cpp"),
])
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
new file mode 100644
index 0000000..f90b73b
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamClientSession.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/SafeByteArray.h>
+#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/FileTransfer/BytestreamException.h>
+#include <Swiften/Network/TimerFactory.h>
+
+namespace Swift {
+
+SOCKS5BytestreamClientSession::SOCKS5BytestreamClientSession(boost::shared_ptr<Connection> connection, const HostAddressPort& addressPort, const std::string& destination, TimerFactory* timerFactory) :
+ connection(connection), addressPort(addressPort), destination(destination), state(Initial), chunkSize(131072) {
+ connection->onConnectFinished.connect(boost::bind(&SOCKS5BytestreamClientSession::handleConnectFinished, this, _1));
+ connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDisconnected, this, _1));
+ weFailedTimeout = timerFactory->createTimer(2000);
+ weFailedTimeout->onTick.connect(boost::bind(&SOCKS5BytestreamClientSession::handleWeFailedTimeout, this));
+}
+
+void SOCKS5BytestreamClientSession::start() {
+ assert(state == Initial);
+ SWIFT_LOG(debug) << "Trying to connect via TCP to " << addressPort.toString() << "." << std::endl;
+ weFailedTimeout->start();
+ connection->connect(addressPort);
+}
+
+void SOCKS5BytestreamClientSession::stop() {
+ connection->disconnect();
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+ readBytestream.reset();
+ state = Finished;
+}
+
+void SOCKS5BytestreamClientSession::process() {
+ SWIFT_LOG(debug) << "unprocessedData.size(): " << unprocessedData.size() << std::endl;
+ ByteArray bndAddress;
+ switch(state) {
+ case Initial:
+ hello();
+ break;
+ case Hello:
+ if (unprocessedData.size() > 1) {
+ unsigned char version = unprocessedData[0];
+ unsigned char authMethod = unprocessedData[1];
+ if (version != 5 || authMethod != 0) {
+ // signal failure to upper level
+ finish(true);
+ return;
+ }
+ unprocessedData.clear();
+ authenticate();
+ }
+ break;
+ case Authenticating:
+ if (unprocessedData.size() < 5) {
+ // need more data to start progressing
+ break;
+ }
+ if (unprocessedData[0] != '\x05') {
+ // wrong version
+ // disconnect & signal failure
+ finish(true);
+ break;
+ }
+ if (unprocessedData[1] != '\x00') {
+ // no success
+ // disconnect & signal failure
+ finish(true);
+ break;
+ }
+ if (unprocessedData[3] != '\x03') {
+ // we expect x'03' = DOMAINNAME here
+ // discconect & signal failure
+ finish(true);
+ break;
+ }
+ if (static_cast<size_t>(unprocessedData[4]) + 1 > unprocessedData.size() + 5) {
+ // complete domainname and port not available yet
+ break;
+ }
+ bndAddress = createByteArray(&(unprocessedData.data()[5]), unprocessedData[4]);
+ if (unprocessedData[unprocessedData[4] + 5] != 0 && bndAddress == createByteArray(destination)) {
+ // we expect a 0 as port
+ // disconnect and fail
+ finish(true);
+ }
+ unprocessedData.clear();
+ state = Ready;
+ SWIFT_LOG(debug) << "session ready" << std::endl;
+ // issue ready signal so the bytestream can be used for reading or writing
+ weFailedTimeout->stop();
+ onSessionReady(false);
+ break;
+ case Ready:
+ SWIFT_LOG(debug) << "Received further data in Ready state." << std::endl;
+ break;
+ case Reading:
+ case Writing:
+ case Finished:
+ SWIFT_LOG(debug) << "Unexpected receive of data. Current state: " << state << std::endl;
+ SWIFT_LOG(debug) << "Data: " << Hexify::hexify(unprocessedData) << std::endl;
+ unprocessedData.clear();
+ //assert(false);
+ }
+}
+
+void SOCKS5BytestreamClientSession::hello() {
+ // Version 5, 1 auth method, No authentication
+ const SafeByteArray hello = createSafeByteArray("\x05\x01\x00", 3);
+ connection->write(hello);
+ state = Hello;
+}
+
+void SOCKS5BytestreamClientSession::authenticate() {
+ SWIFT_LOG(debug) << std::endl;
+ SafeByteArray header = createSafeByteArray("\x05\x01\x00\x03", 4);
+ SafeByteArray message = header;
+ append(message, createSafeByteArray(destination.size()));
+ authenticateAddress = createByteArray(destination);
+ append(message, authenticateAddress);
+ append(message, createSafeByteArray("\x00\x00", 2)); // 2 byte for port
+ connection->write(message);
+ state = Authenticating;
+}
+
+void SOCKS5BytestreamClientSession::startReceiving(boost::shared_ptr<WriteBytestream> writeStream) {
+ if (state == Ready) {
+ state = Reading;
+ writeBytestream = writeStream;
+ writeBytestream->write(unprocessedData);
+ onBytesReceived(unprocessedData.size());
+ unprocessedData.clear();
+ } else {
+ SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl;
+ }
+}
+
+void SOCKS5BytestreamClientSession::startSending(boost::shared_ptr<ReadBytestream> readStream) {
+ if (state == Ready) {
+ state = Writing;
+ readBytestream = readStream;
+ connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+ sendData();
+ } else {
+ SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl;
+ }
+}
+
+HostAddressPort SOCKS5BytestreamClientSession::getAddressPort() const {
+ return addressPort;
+}
+
+void SOCKS5BytestreamClientSession::sendData() {
+ if (!readBytestream->isFinished()) {
+ try {
+ SafeByteArray dataToSend = createSafeByteArray(readBytestream->read(chunkSize));
+ connection->write(dataToSend);
+ onBytesSent(dataToSend.size());
+ }
+ catch (const BytestreamException&) {
+ finish(true);
+ }
+ }
+ else {
+ finish(false);
+ }
+}
+
+void SOCKS5BytestreamClientSession::finish(bool error) {
+ weFailedTimeout->stop();
+ connection->disconnect();
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+ readBytestream.reset();
+ if (state == Initial || state == Hello || state == Authenticating) {
+ onSessionReady(true);
+ }
+ else {
+ state = Finished;
+ if (error) {
+ onFinished(boost::optional<FileTransferError>(FileTransferError::ReadError));
+ } else {
+ onFinished(boost::optional<FileTransferError>());
+ }
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleConnectFinished(bool error) {
+ if (error) {
+ SWIFT_LOG(debug) << "Failed to connect via TCP to " << addressPort.toString() << "." << std::endl;
+ finish(true);
+ } else {
+ SWIFT_LOG(debug) << "Successfully connected via TCP" << addressPort.toString() << "." << std::endl;
+ weFailedTimeout->start();
+ connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+ process();
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleDataRead(const SafeByteArray& data) {
+ SWIFT_LOG(debug) << "state: " << state << " data.size() = " << data.size() << std::endl;
+ if (state != Reading) {
+ append(unprocessedData, data);
+ process();
+ }
+ else {
+ writeBytestream->write(createByteArray(data.data(), data.size()));
+ onBytesReceived(data.size());
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleDisconnected(const boost::optional<Connection::Error>& error) {
+ SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl;
+ if (error) {
+ finish(true);
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleWeFailedTimeout() {
+ SWIFT_LOG(debug) << "Failed due to timeout!" << std::endl;
+ finish(true);
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h
new file mode 100644
index 0000000..894e977
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/Timer.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamRegistry;
+class Connection;
+class TimerFactory;
+
+/**
+ * A session which has been connected to a SOCKS5 server (requester).
+ *
+ */
+class SOCKS5BytestreamClientSession {
+public:
+ enum State {
+ Initial,
+ Hello,
+ Authenticating,
+ Ready,
+ Writing,
+ Reading,
+ Finished,
+ };
+
+public:
+ typedef boost::shared_ptr<SOCKS5BytestreamClientSession> ref;
+
+public:
+ SOCKS5BytestreamClientSession(boost::shared_ptr<Connection> connection, const HostAddressPort&, const std::string&, TimerFactory*);
+
+ void start();
+ void stop();
+
+ void startReceiving(boost::shared_ptr<WriteBytestream>);
+ void startSending(boost::shared_ptr<ReadBytestream>);
+
+ HostAddressPort getAddressPort() const;
+
+ boost::signal<void (bool /*error*/)> onSessionReady;
+
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+ boost::signal<void (int)> onBytesSent;
+ boost::signal<void (int)> onBytesReceived;
+
+private:
+ void process();
+ void hello();
+ void authenticate();
+
+ void handleConnectFinished(bool error);
+ void handleDataRead(const SafeByteArray&);
+ void handleDisconnected(const boost::optional<Connection::Error>&);
+ void handleWeFailedTimeout();
+
+ void finish(bool error);
+ void sendData();
+
+private:
+ boost::shared_ptr<Connection> connection;
+ HostAddressPort addressPort;
+ std::string destination; // hexify(SHA1(sessionID + requester + target))
+
+ State state;
+ int destinationPort;
+
+ ByteArray unprocessedData;
+ ByteArray authenticateAddress;
+
+ int chunkSize;
+ boost::shared_ptr<WriteBytestream> writeBytestream;
+ boost::shared_ptr<ReadBytestream> readBytestream;
+
+ Timer::ref weFailedTimeout;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp b/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp
new file mode 100644
index 0000000..9599fd1
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamProxy.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+SOCKS5BytestreamProxy::SOCKS5BytestreamProxy(ConnectionFactory *connFactory, TimerFactory *timeFactory) : connectionFactory(connFactory), timerFactory(timeFactory) {
+
+}
+
+void SOCKS5BytestreamProxy::addS5BProxy(S5BProxyRequest::ref proxy) {
+ localS5BProxies.push_back(proxy);
+}
+
+const std::vector<S5BProxyRequest::ref>& SOCKS5BytestreamProxy::getS5BProxies() const {
+ return localS5BProxies;
+}
+
+void SOCKS5BytestreamProxy::connectToProxies(const std::string& sessionID) {
+ SWIFT_LOG(debug) << "session ID: " << sessionID << std::endl;
+ ProxyJIDClientSessionMap clientSessions;
+
+ foreach(S5BProxyRequest::ref proxy, localS5BProxies) {
+ boost::shared_ptr<Connection> conn = connectionFactory->createConnection();
+
+ boost::shared_ptr<SOCKS5BytestreamClientSession> session = boost::make_shared<SOCKS5BytestreamClientSession>(conn, proxy->getStreamHost().get().addressPort, sessionID, timerFactory);
+ clientSessions[proxy->getStreamHost().get().jid] = session;
+ session->start();
+ }
+
+ proxySessions[sessionID] = clientSessions;
+}
+
+boost::shared_ptr<SOCKS5BytestreamClientSession> SOCKS5BytestreamProxy::getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID) {
+ // checking parameters
+ if (proxySessions.find(sessionID) == proxySessions.end()) {
+ return boost::shared_ptr<SOCKS5BytestreamClientSession>();
+ }
+ if (proxySessions[sessionID].find(proxyJID) == proxySessions[sessionID].end()) {
+ return boost::shared_ptr<SOCKS5BytestreamClientSession>();
+ }
+
+ // get active session
+ boost::shared_ptr<SOCKS5BytestreamClientSession> activeSession = proxySessions[sessionID][proxyJID];
+ proxySessions[sessionID].erase(proxyJID);
+
+ // close other sessions
+ foreach(const ProxyJIDClientSessionMap::value_type& myPair, proxySessions[sessionID]) {
+ myPair.second->stop();
+ }
+
+ proxySessions.erase(sessionID);
+
+ return activeSession;
+}
+
+boost::shared_ptr<SOCKS5BytestreamClientSession> SOCKS5BytestreamProxy::createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr) {
+ SOCKS5BytestreamClientSession::ref connection = boost::make_shared<SOCKS5BytestreamClientSession>(connectionFactory->createConnection(), addressPort, destAddr, timerFactory);
+ return connection;
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxy.h b/Swiften/FileTransfer/SOCKS5BytestreamProxy.h
new file mode 100644
index 0000000..8f80cea
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxy.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/TimerFactory.h>
+
+namespace Swift {
+
+/**
+ * - manages list of working S5B proxies
+ * - creates initial connections (for the candidates you provide)
+ */
+class SOCKS5BytestreamProxy {
+public:
+ SOCKS5BytestreamProxy(ConnectionFactory*, TimerFactory*);
+
+ void addS5BProxy(S5BProxyRequest::ref);
+ const std::vector<S5BProxyRequest::ref>& getS5BProxies() const;
+
+ void connectToProxies(const std::string& sessionID);
+ boost::shared_ptr<SOCKS5BytestreamClientSession> getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID);
+
+ boost::shared_ptr<SOCKS5BytestreamClientSession> createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr);
+
+private:
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+
+ typedef std::map<JID, boost::shared_ptr<SOCKS5BytestreamClientSession> > ProxyJIDClientSessionMap;
+ std::map<std::string, ProxyJIDClientSessionMap> proxySessions;
+
+ std::vector<S5BProxyRequest::ref> localS5BProxies;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
index 429c7f2..ffc4298 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
@@ -6,27 +6,67 @@
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+
namespace Swift {
SOCKS5BytestreamRegistry::SOCKS5BytestreamRegistry() {
}
-void SOCKS5BytestreamRegistry::addBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream) {
- byteStreams[destination] = byteStream;
+void SOCKS5BytestreamRegistry::addReadBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream) {
+ readBytestreams[destination] = byteStream;
}
-void SOCKS5BytestreamRegistry::removeBytestream(const std::string& destination) {
- byteStreams.erase(destination);
+void SOCKS5BytestreamRegistry::removeReadBytestream(const std::string& destination) {
+ readBytestreams.erase(destination);
}
-boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getBytestream(const std::string& destination) const {
- BytestreamMap::const_iterator i = byteStreams.find(destination);
- if (i != byteStreams.end()) {
+boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getReadBytestream(const std::string& destination) const {
+ ReadBytestreamMap::const_iterator i = readBytestreams.find(destination);
+ if (i != readBytestreams.end()) {
return i->second;
}
return boost::shared_ptr<ReadBytestream>();
}
+void SOCKS5BytestreamRegistry::addWriteBytestream(const std::string& destination, boost::shared_ptr<WriteBytestream> byteStream) {
+ writeBytestreams[destination] = byteStream;
+}
+
+void SOCKS5BytestreamRegistry::removeWriteBytestream(const std::string& destination) {
+ writeBytestreams.erase(destination);
+}
+
+boost::shared_ptr<WriteBytestream> SOCKS5BytestreamRegistry::getWriteBytestream(const std::string& destination) const {
+ WriteBytestreamMap::const_iterator i = writeBytestreams.find(destination);
+ if (i != writeBytestreams.end()) {
+ return i->second;
+ }
+ return boost::shared_ptr<WriteBytestream>();
+}
+
+std::string SOCKS5BytestreamRegistry::generateSessionID() {
+ return idGenerator.generateID();
+}
+
+SOCKS5BytestreamServerSession* SOCKS5BytestreamRegistry::getConnectedSession(const std::string& destination) {
+ if (serverSessions.find(destination) != serverSessions.end()) {
+ return serverSessions[destination];
+ } else {
+ SWIFT_LOG(debug) << "No active connction for stream ID " << destination << " found!" << std::endl;
+ return NULL;
+ }
+}
+
+std::string SOCKS5BytestreamRegistry::getHostname(const std::string& sessionID, const JID& requester, const JID& target) {
+ return Hexify::hexify(SHA1::getHash(createSafeByteArray(sessionID + requester.toString() + target.toString())));
+}
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
index 955b900..779aabb 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
@@ -7,23 +7,58 @@
#pragma once
#include <boost/shared_ptr.hpp>
-#include <map>
+#include <map>
#include <string>
+#include <vector>
+#include <set>
+
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/Network/HostAddressPort.h>
namespace Swift {
class SOCKS5BytestreamRegistry {
public:
SOCKS5BytestreamRegistry();
- boost::shared_ptr<ReadBytestream> getBytestream(const std::string& destination) const;
- void addBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream);
- void removeBytestream(const std::string& destination);
+ boost::shared_ptr<ReadBytestream> getReadBytestream(const std::string& destination) const;
+ void addReadBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream);
+ void removeReadBytestream(const std::string& destination);
+
+ boost::shared_ptr<WriteBytestream> getWriteBytestream(const std::string& destination) const;
+ void addWriteBytestream(const std::string& destination, boost::shared_ptr<WriteBytestream> byteStream);
+ void removeWriteBytestream(const std::string& destination);
+
+ /**
+ * Generate a new session ID to use for new S5B streams.
+ */
+ std::string generateSessionID();
+
+ /**
+ * Start an actual transfer.
+ */
+ SOCKS5BytestreamServerSession* getConnectedSession(const std::string& destination);
+
+ public:
+ static std::string getHostname(const std::string& sessionID, const JID& requester, const JID& target);
private:
- typedef std::map<std::string, boost::shared_ptr<ReadBytestream> > BytestreamMap;
- BytestreamMap byteStreams;
+ friend class SOCKS5BytestreamServerSession;
+
+ typedef std::map<std::string, boost::shared_ptr<ReadBytestream> > ReadBytestreamMap;
+ ReadBytestreamMap readBytestreams;
+
+ typedef std::map<std::string, boost::shared_ptr<WriteBytestream> > WriteBytestreamMap;
+ WriteBytestreamMap writeBytestreams;
+
+ std::map<std::string, SOCKS5BytestreamServerSession*> serverSessions;
+
+ IDGenerator idGenerator;
};
}
-
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
index 9731d2d..90fed7a 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
@@ -8,13 +8,15 @@
#include <boost/bind.hpp>
+#include <Swiften/Base/Log.h>
#include <Swiften/StringCodecs/Hexify.h>
#include <Swiften/StringCodecs/SHA1.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
namespace Swift {
-SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer) : connectionServer(connectionServer) {
+SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer, SOCKS5BytestreamRegistry* registry) : connectionServer(connectionServer), registry(registry) {
}
void SOCKS5BytestreamServer::start() {
@@ -25,12 +27,12 @@ void SOCKS5BytestreamServer::stop() {
connectionServer->onNewConnection.disconnect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1));
}
-void SOCKS5BytestreamServer::addBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
- bytestreams.addBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
+void SOCKS5BytestreamServer::addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
+ registry->addReadBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
}
-void SOCKS5BytestreamServer::removeBytestream(const std::string& id, const JID& from, const JID& to) {
- bytestreams.removeBytestream(getSOCKSDestinationAddress(id, from, to));
+void SOCKS5BytestreamServer::removeReadBytestream(const std::string& id, const JID& from, const JID& to) {
+ registry->removeReadBytestream(getSOCKSDestinationAddress(id, from, to));
}
std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string& id, const JID& from, const JID& to) {
@@ -38,7 +40,7 @@ std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string
}
void SOCKS5BytestreamServer::handleNewConnection(boost::shared_ptr<Connection> connection) {
- boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, &bytestreams));
+ boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, registry));
sessions.push_back(session);
session->start();
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.h b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
index 7fa709e..6bb598e 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
@@ -20,15 +20,15 @@ namespace Swift {
class SOCKS5BytestreamServer {
public:
- SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer);
+ SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer, SOCKS5BytestreamRegistry* registry);
HostAddressPort getAddressPort() const;
void start();
void stop();
- void addBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
- void removeBytestream(const std::string& id, const JID& from, const JID& to);
+ void addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
+ void removeReadBytestream(const std::string& id, const JID& from, const JID& to);
/*protected:
boost::shared_ptr<ReadBytestream> getBytestream(const std::string& dest);*/
@@ -42,7 +42,7 @@ namespace Swift {
friend class SOCKS5BytestreamServerSession;
boost::shared_ptr<ConnectionServer> connectionServer;
- SOCKS5BytestreamRegistry bytestreams;
+ SOCKS5BytestreamRegistry* registry;
std::vector<boost::shared_ptr<SOCKS5BytestreamServerSession> > sessions;
};
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
index 477af4b..6f33862 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
@@ -13,12 +13,14 @@
#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
#include <Swiften/FileTransfer/BytestreamException.h>
namespace Swift {
-SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(4096) {
+SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(131072) {
+ connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1));
}
SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
@@ -29,17 +31,55 @@ SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
}
void SOCKS5BytestreamServerSession::start() {
+ SWIFT_LOG(debug) << std::endl;
connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
state = WaitingForAuthentication;
}
void SOCKS5BytestreamServerSession::stop() {
- finish(false);
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
+ connection->disconnect();
+ state = Finished;
+}
+
+void SOCKS5BytestreamServerSession::startTransfer() {
+ if (state == ReadyForTransfer) {
+ if (readBytestream) {
+ state = WritingData;
+ connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ sendData();
+ }
+ else if(writeBytestream) {
+ state = ReadingData;
+ writeBytestream->write(unprocessedData);
+ onBytesReceived(unprocessedData.size());
+ unprocessedData.clear();
+ }
+ } else {
+ SWIFT_LOG(debug) << "Not ready for transfer!" << std::endl;
+ }
+}
+
+HostAddressPort SOCKS5BytestreamServerSession::getAddressPort() const {
+ return connection->getLocalAddress();
}
void SOCKS5BytestreamServerSession::handleDataRead(const SafeByteArray& data) {
- append(unprocessedData, data);
- process();
+ if (state != ReadingData) {
+ append(unprocessedData, data);
+ process();
+ } else {
+ writeBytestream->write(createByteArray(data.data(), data.size()));
+ onBytesReceived(data.size());
+ }
+}
+
+void SOCKS5BytestreamServerSession::handleDisconnected(const boost::optional<Connection::Error>& error) {
+ SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl;
+ if (error) {
+ finish(true);
+ }
}
void SOCKS5BytestreamServerSession::process() {
@@ -54,7 +94,7 @@ void SOCKS5BytestreamServerSession::process() {
if (i == 2 + authCount) {
// Authentication message is complete
if (i != unprocessedData.size()) {
- std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
}
unprocessedData.clear();
connection->write(createSafeByteArray("\x05\x00", 2));
@@ -71,26 +111,31 @@ void SOCKS5BytestreamServerSession::process() {
requestID.push_back(unprocessedData[i]);
++i;
}
- // Skip the port:
+ // Skip the port: 2 byte large, one already skipped. Add one for comparison with size
i += 2;
- if (i >= unprocessedData.size()) {
+ if (i <= unprocessedData.size()) {
if (i != unprocessedData.size()) {
- std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
}
- bytestream = bytestreams->getBytestream(byteArrayToString(requestID));
+ unprocessedData.clear();
+ std::string streamID = byteArrayToString(requestID);
+ readBytestream = bytestreams->getReadBytestream(streamID);
+ writeBytestream = bytestreams->getWriteBytestream(streamID);
SafeByteArray result = createSafeByteArray("\x05", 1);
- result.push_back(bytestream ? 0x0 : 0x4);
+ result.push_back((readBytestream || writeBytestream) ? 0x0 : 0x4);
append(result, createByteArray("\x00\x03", 2));
result.push_back(static_cast<char>(requestID.size()));
append(result, concat(requestID, createByteArray("\x00\x00", 2)));
- if (!bytestream) {
+ if (!readBytestream && !writeBytestream) {
+ SWIFT_LOG(debug) << "Readstream or Wrtiestream with ID " << streamID << " not found!" << std::endl;
connection->write(result);
finish(true);
}
else {
- state = SendingData;
- connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ SWIFT_LOG(deubg) << "Found " << (readBytestream ? "Readstream" : "Writestream") << ". Sent OK." << std::endl;
connection->write(result);
+ bytestreams->serverSessions[streamID] = this;
+ state = ReadyForTransfer;
}
}
}
@@ -98,9 +143,11 @@ void SOCKS5BytestreamServerSession::process() {
}
void SOCKS5BytestreamServerSession::sendData() {
- if (!bytestream->isFinished()) {
+ if (!readBytestream->isFinished()) {
try {
- connection->write(createSafeByteArray(bytestream->read(chunkSize)));
+ SafeByteArray dataToSend = createSafeByteArray(readBytestream->read(chunkSize));
+ connection->write(dataToSend);
+ onBytesSent(dataToSend.size());
}
catch (const BytestreamException&) {
finish(true);
@@ -114,9 +161,14 @@ void SOCKS5BytestreamServerSession::sendData() {
void SOCKS5BytestreamServerSession::finish(bool error) {
connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
- bytestream.reset();
+ connection->onDisconnected.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1));
+ readBytestream.reset();
state = Finished;
- onFinished(error);
+ if (error) {
+ onFinished(boost::optional<FileTransferError>(FileTransferError::PeerError));
+ } else {
+ onFinished(boost::optional<FileTransferError>());
+ }
}
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
index 761a365..3e1018f 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
@@ -11,17 +11,24 @@
#include <Swiften/Base/boost_bsignals.h>
#include <Swiften/Network/Connection.h>
#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
namespace Swift {
class SOCKS5BytestreamRegistry;
class SOCKS5BytestreamServerSession {
public:
+ typedef boost::shared_ptr<SOCKS5BytestreamServerSession> ref;
+
+ public:
enum State {
Initial,
WaitingForAuthentication,
WaitingForRequest,
- SendingData,
+ ReadyForTransfer,
+ ReadingData,
+ WritingData,
Finished,
};
@@ -35,12 +42,18 @@ namespace Swift {
void start();
void stop();
- boost::signal<void (bool /* error */)> onFinished;
+ void startTransfer();
+ HostAddressPort getAddressPort() const;
+
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+ boost::signal<void (int)> onBytesSent;
+ boost::signal<void (int)> onBytesReceived;
private:
void finish(bool error);
void process();
void handleDataRead(const SafeByteArray&);
+ void handleDisconnected(const boost::optional<Connection::Error>&);
void sendData();
private:
@@ -49,6 +62,7 @@ namespace Swift {
ByteArray unprocessedData;
State state;
int chunkSize;
- boost::shared_ptr<ReadBytestream> bytestream;
+ boost::shared_ptr<ReadBytestream> readBytestream;
+ boost::shared_ptr<WriteBytestream> writeBytestream;
};
}
diff --git a/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h
new file mode 100644
index 0000000..ae06cd3
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+ class DummyFileTransferManager : public FileTransferManager {
+ public:
+ DummyFileTransferManager() : FileTransferManager() {
+ }
+
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const boost::filesystem::path&, const std::string&, boost::shared_ptr<ReadBytestream>) {
+ return OutgoingFileTransfer::ref();
+ }
+
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const std::string&, const std::string&, const boost::uintmax_t, const boost::posix_time::ptime&, boost::shared_ptr<ReadBytestream>) {
+ return OutgoingFileTransfer::ref();
+ }
+
+ virtual void startListeningOnPort(int) {
+ }
+
+ virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest>) {
+ }
+
+ };
+}
diff --git a/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..7407f44
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h>
+#include <Swiften/FileTransfer/IncomingJingleFileTransfer.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Jingle/FakeJingleSession.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Network/DummyConnectionFactory.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Queries/IQRouter.h>
+
+#include <iostream>
+
+using namespace Swift;
+using namespace boost;
+
+class FakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+ void addRemoteTransportCandidates(JingleTransportPayload::ref cand) {
+ candidate = cand;
+ }
+
+ void selectCandidate() {
+ boost::shared_ptr<JingleS5BTransportPayload> payload = make_shared<JingleS5BTransportPayload>();
+ payload->setCandidateError(true);
+ payload->setSessionID(candidate->getSessionID());
+ onRemoteTransportCandidateSelectFinished(payload);
+ }
+
+ void setMinimumPriority(int) {
+
+ }
+
+ bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+
+private:
+ JingleTransportPayload::ref candidate;
+};
+
+class FakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+ virtual ~FakeRemoteJingleTransportCandidateSelectorFactory() {
+
+ }
+
+ virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() {
+ return new FakeRemoteJingleTransportCandidateSelector();
+ }
+};
+
+class FakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref payload) {
+ JingleS5BTransportPayload::ref payL = make_shared<JingleS5BTransportPayload>();
+ payL->setSessionID(payload->getSessionID());
+ onLocalTransportCandidatesGenerated(payL);
+ }
+
+ void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+ onLocalTransportCandidatesGenerated(payload);
+ }
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ virtual int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+};
+
+class FakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory {
+public:
+ virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() {
+ return new FakeLocalJingleTransportCandidateGenerator();
+ }
+};
+
+class IncomingJingleFileTransferTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IncomingJingleFileTransferTest);
+ CPPUNIT_TEST(test_AcceptOnyIBBSendsSessionAccept);
+ CPPUNIT_TEST(test_OnlyIBBTransferReceiveWorks);
+ CPPUNIT_TEST(test_AcceptFailingS5BFallsBackToIBB);
+ CPPUNIT_TEST_SUITE_END();
+public:
+ shared_ptr<IncomingJingleFileTransfer> createTestling() {
+ JID ourJID("our@jid.org/full");
+ return make_shared<IncomingJingleFileTransfer>(ourJID, shared_ptr<JingleSession>(fakeJingleSession), jingleContentPayload, fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, bytestreamRegistry, bytestreamProxy, timerFactory);
+ }
+
+ IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+ IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb);
+ request->setFrom(from);
+ return request;
+ }
+
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession");
+ jingleContentPayload = make_shared<JingleContentPayload>();
+ fakeRJTCSF = make_shared<FakeRemoteJingleTransportCandidateSelectorFactory>();
+ fakeLJTCF = make_shared<FakeLocalJingleTransportCandidateGeneratorFactory>();
+ stanzaChannel = new DummyStanzaChannel();
+ iqRouter = new IQRouter(stanzaChannel);
+ bytestreamRegistry = new SOCKS5BytestreamRegistry();
+ timerFactory = new DummyTimerFactory();
+ connectionFactory = new DummyConnectionFactory(eventLoop);
+ bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+ }
+
+ void tearDown() {
+ delete bytestreamProxy;
+ delete connectionFactory;
+ delete timerFactory;
+ delete bytestreamRegistry;
+ delete iqRouter;
+ delete stanzaChannel;
+ delete eventLoop;
+ }
+
+ // Tests whether IncomingJingleFileTransfer would accept a IBB only file transfer.
+ void test_AcceptOnyIBBSendsSessionAccept() {
+ //1. create your test incoming file transfer
+ shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>();
+ tpRef->setSessionID("mysession");
+ jingleContentPayload->addTransport(tpRef);
+
+ shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+ //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+ fileTransfer->accept(byteStream);
+
+ // check whether accept has been called
+ getCall<FakeJingleSession::AcceptCall>(0);
+ }
+
+ void test_OnlyIBBTransferReceiveWorks() {
+ //1. create your test incoming file transfer
+ shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>();
+ tpRef->setSessionID("mysession");
+ jingleContentPayload->addTransport(tpRef);
+
+ shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+ //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+ fileTransfer->accept(byteStream);
+
+ // check whether accept has been called
+ getCall<FakeJingleSession::AcceptCall>(0);
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+ CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData());
+ }
+
+ void test_AcceptFailingS5BFallsBackToIBB() {
+ //1. create your test incoming file transfer
+ addFileTransferDescription();
+
+ // add SOCKS5BytestreamTransportPayload
+ JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload();
+
+ shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+ //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+ fileTransfer->accept(byteStream);
+
+ // check whether accept has been called
+ FakeJingleSession::AcceptCall acceptCall = getCall<FakeJingleSession::AcceptCall>(0);
+ CPPUNIT_ASSERT_EQUAL(payLoad->getSessionID(), acceptCall.payload->getSessionID());
+
+ // check for candidate error
+ FakeJingleSession::InfoTransportCall infoTransportCall = getCall<FakeJingleSession::InfoTransportCall>(1);
+ JingleS5BTransportPayload::ref s5bPayload = dynamic_pointer_cast<JingleS5BTransportPayload>(infoTransportCall.payload);
+ CPPUNIT_ASSERT(s5bPayload->hasCandidateError());
+
+ // indicate transport replace (Romeo)
+ fakeJingleSession->onTransportReplaceReceived(getContentID(), addJingleIBBPayload());
+
+ FakeJingleSession::AcceptTransportCall acceptTransportCall = getCall<FakeJingleSession::AcceptTransportCall>(2);
+
+ // send a bit of data
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+ CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData());
+ }
+
+ void test_S5BTransferReceiveTest() {
+ addFileTransferDescription();
+ JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload();
+ }
+
+private:
+ void addFileTransferDescription() {
+ shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ }
+
+ shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() {
+ JingleS5BTransportPayload::ref payLoad = make_shared<JingleS5BTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() {
+ JingleIBBTransportPayload::ref payLoad = make_shared<JingleIBBTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ JingleContentID getContentID() const {
+ return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator());
+ }
+
+ template <typename T> T getCall(int i) const {
+ size_t index = static_cast<size_t>(i);
+ CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size());
+ T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]);
+ CPPUNIT_ASSERT(cmd);
+ return *cmd;
+ }
+
+private:
+ EventLoop* eventLoop;
+ FakeJingleSession* fakeJingleSession;
+ shared_ptr<JingleContentPayload> jingleContentPayload;
+ shared_ptr<FakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF;
+ shared_ptr<FakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF;
+ DummyStanzaChannel* stanzaChannel;
+ IQRouter* iqRouter;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ DummyConnectionFactory* connectionFactory;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ DummyTimerFactory* timerFactory;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IncomingJingleFileTransferTest);
diff --git a/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..582c3e5
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/bind.hpp>
+#include <boost/optional.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
+#include <Swiften/Jingle/FakeJingleSession.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/DummyConnectionFactory.h>
+
+#include <Swiften/Base/Log.h>
+
+#include <iostream>
+
+using namespace Swift;
+
+class OFakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+ void addRemoteTransportCandidates(JingleTransportPayload::ref cand) {
+ candidate = cand;
+ }
+
+ void selectCandidate() {
+ JingleS5BTransportPayload::ref payload = boost::make_shared<JingleS5BTransportPayload>();
+ payload->setCandidateError(true);
+ payload->setSessionID(candidate->getSessionID());
+ onRemoteTransportCandidateSelectFinished(payload);
+ }
+
+ void setMinimumPriority(int) {
+
+ }
+
+ bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+
+private:
+ JingleTransportPayload::ref candidate;
+};
+
+class OFakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+ virtual ~OFakeRemoteJingleTransportCandidateSelectorFactory() {
+
+ }
+
+ virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() {
+ return new OFakeRemoteJingleTransportCandidateSelector();
+ }
+};
+
+class OFakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref /* payload */) {
+ //JingleTransportPayload::ref payL = make_shared<JingleTransportPayload>();
+ //payL->setSessionID(payload->getSessionID());
+ JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>();
+
+ onLocalTransportCandidatesGenerated(payL);
+ }
+
+ void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+ onLocalTransportCandidatesGenerated(payload);
+ }
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ virtual int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+};
+
+class OFakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory {
+public:
+ virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() {
+ return new OFakeLocalJingleTransportCandidateGenerator();
+ }
+};
+
+class OutgoingJingleFileTransferTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(OutgoingJingleFileTransferTest);
+ CPPUNIT_TEST(test_SendSessionInitiateOnStart);
+ CPPUNIT_TEST(test_IBBStartsAfterSendingSessionAccept);
+ CPPUNIT_TEST(test_ReceiveSessionTerminateAfterSessionInitiate);
+ CPPUNIT_TEST_SUITE_END();
+
+ class FTStatusHelper {
+ public:
+ bool finishedCalled;
+ FileTransferError::Type error;
+ void handleFileTransferFinished(boost::optional<FileTransferError> error) {
+ finishedCalled = true;
+ if (error.is_initialized()) this->error = error.get().getType();
+ }
+ };
+public:
+
+ boost::shared_ptr<OutgoingJingleFileTransfer> createTestling() {
+ JID to("test@foo.com/bla");
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setDescription("some file");
+ fileInfo.setName("test.bin");
+ fileInfo.setHash("asdjasdas");
+ fileInfo.setSize(1024 * 1024);
+ return boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(boost::shared_ptr<JingleSession>(fakeJingleSession), fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, idGen, to, stream, fileInfo, s5bRegistry, s5bProxy));
+ }
+
+ IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+ IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb);
+ request->setFrom(from);
+ return request;
+ }
+
+ void setUp() {
+ fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession");
+ jingleContentPayload = boost::make_shared<JingleContentPayload>();
+ fakeRJTCSF = boost::make_shared<OFakeRemoteJingleTransportCandidateSelectorFactory>();
+ fakeLJTCF = boost::make_shared<OFakeLocalJingleTransportCandidateGeneratorFactory>();
+ stanzaChannel = new DummyStanzaChannel();
+ iqRouter = new IQRouter(stanzaChannel);
+ eventLoop = new DummyEventLoop();
+ timerFactory = new DummyTimerFactory();
+ connectionFactory = new DummyConnectionFactory(eventLoop);
+ s5bRegistry = new SOCKS5BytestreamRegistry();
+ s5bProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+ data.clear();
+ for (int n=0; n < 1024 * 1024; ++n) {
+ data.push_back(34);
+ }
+
+ stream = boost::make_shared<ByteArrayReadBytestream>(data);
+
+ idGen = new IDGenerator();
+ }
+
+ void tearDown() {
+ delete idGen;
+ delete s5bRegistry;
+ delete connectionFactory;
+ delete timerFactory;
+ delete eventLoop;
+ delete iqRouter;
+ delete stanzaChannel;
+ }
+
+
+ void test_SendSessionInitiateOnStart() {
+ boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+ transfer->start();
+ FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0);
+ JingleFileTransferDescription::ref description = boost::dynamic_pointer_cast<JingleFileTransferDescription>(call.description);
+ CPPUNIT_ASSERT(description);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), description->getOffers().size());
+ CPPUNIT_ASSERT(static_cast<size_t>(1048576) == description->getOffers()[0].getSize());
+
+ JingleS5BTransportPayload::ref transport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(call.payload);
+ CPPUNIT_ASSERT(transport);
+ }
+
+ void test_IBBStartsAfterSendingSessionAccept() {
+ boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+ transfer->start();
+
+ FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0);
+ // FIXME: we initiate with SOCSK5 now and not IBB, needs to be fixed.
+ /*
+ fakeJingleSession->onSessionAcceptReceived(call.id, call.description, call.payload);
+ IQ::ref iqOpenStanza = stanzaChannel->getStanzaAtIndex<IQ>(0);
+ CPPUNIT_ASSERT(iqOpenStanza);
+ */
+ }
+
+ void test_ReceiveSessionTerminateAfterSessionInitiate() {
+ boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+ transfer->start();
+
+ getCall<FakeJingleSession::InitiateCall>(0);
+
+ FTStatusHelper helper;
+ helper.finishedCalled = false;
+ transfer->onFinished.connect(bind(&FTStatusHelper::handleFileTransferFinished, &helper, _1));
+ fakeJingleSession->onSessionTerminateReceived(JinglePayload::Reason(JinglePayload::Reason::Busy));
+ CPPUNIT_ASSERT_EQUAL(true, helper.finishedCalled);
+ CPPUNIT_ASSERT(FileTransferError::PeerError == helper.error);
+ }
+
+
+//TODO: some more testcases
+
+private:
+ void addFileTransferDescription() {
+ boost::shared_ptr<JingleFileTransferDescription> desc = boost::make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ }
+
+ boost::shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() {
+ JingleS5BTransportPayload::ref payLoad = boost::make_shared<JingleS5BTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ boost::shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() {
+ JingleIBBTransportPayload::ref payLoad = boost::make_shared<JingleIBBTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ JingleContentID getContentID() const {
+ return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator());
+ }
+
+ template <typename T> T getCall(int i) const {
+ size_t index = static_cast<size_t>(i);
+ CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size());
+ T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]);
+ CPPUNIT_ASSERT(cmd);
+ return *cmd;
+ }
+
+private:
+ std::vector<unsigned char> data;
+ boost::shared_ptr<ByteArrayReadBytestream> stream;
+ FakeJingleSession* fakeJingleSession;
+ boost::shared_ptr<JingleContentPayload> jingleContentPayload;
+ boost::shared_ptr<OFakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF;
+ boost::shared_ptr<OFakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF;
+ DummyStanzaChannel* stanzaChannel;
+ IQRouter* iqRouter;
+ IDGenerator* idGen;
+ EventLoop *eventLoop;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ DummyTimerFactory* timerFactory;
+ DummyConnectionFactory* connectionFactory;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(OutgoingJingleFileTransferTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp
new file mode 100644
index 0000000..35580bf
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Base/ByteArray.h>
+#include <QA/Checker/IO.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/bind.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/StartStopper.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/StringCodecs/Hexify.h>
+
+using namespace Swift;
+
+boost::mt19937 randomGen;
+
+class SOCKS5BytestreamClientSessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SOCKS5BytestreamClientSessionTest);
+ CPPUNIT_TEST(testForSessionReady);
+ CPPUNIT_TEST(testErrorHandlingHello);
+ CPPUNIT_TEST(testErrorHandlingRequest);
+ CPPUNIT_TEST(testWriteBytestream);
+ CPPUNIT_TEST(testReadBytestream);
+ CPPUNIT_TEST_SUITE_END();
+
+ const HostAddressPort destinationAddressPort;
+ const std::string destination;
+
+public:
+ SOCKS5BytestreamClientSessionTest() : destinationAddressPort(HostAddressPort(HostAddress("127.0.0.1"), 8888)),
+ destination(SOCKS5BytestreamRegistry::getHostname("foo", JID("requester@example.com/test"), JID("target@example.com/test"))), eventLoop(NULL), timerFactory(NULL) { }
+
+ void setUp() {
+ randomGen.seed(time(NULL));
+ eventLoop = new DummyEventLoop();
+ timerFactory = new DummyTimerFactory();
+ connection = boost::make_shared<MockeryConnection>(failingPorts, true, eventLoop);
+ //connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
+ //stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
+// connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSessionTest::handleDataRead, this, _1));
+ }
+
+ void tearDown() {
+ //connection.reset();
+ delete timerFactory;
+ delete eventLoop;
+ }
+
+ void testForSessionReady() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT(createByteArray("\x05\x01\x00", 3) == helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4]));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size()));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1));
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+ }
+
+ void testErrorHandlingHello() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloAuthFail();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError);
+ CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled);
+ }
+
+ void testErrorHandlingRequest() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4]));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size()));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1));
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestFail();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError);
+ CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled);
+ }
+
+ void testWriteBytestream() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+
+ boost::shared_ptr<ByteArrayWriteBytestream> output = boost::make_shared<ByteArrayWriteBytestream>();
+ clientSession->startReceiving(output);
+
+ ByteArray transferData = generateRandomByteArray(1024);
+ connection->onDataRead(createSafeByteArray(transferData.data(), transferData.size()));
+ CPPUNIT_ASSERT_EQUAL(transferData, output->getData());
+ }
+
+ void testReadBytestream() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+
+ helper.unprocessedInput.clear();
+ ByteArray transferData = generateRandomByteArray(1024 * 1024);
+ boost::shared_ptr<ByteArrayReadBytestream> input = boost::make_shared<ByteArrayReadBytestream>(transferData);
+ clientSession->startSending(input);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray(transferData.data(), transferData.size()), helper.unprocessedInput);
+ }
+
+
+
+private:
+ static ByteArray generateRandomByteArray(size_t len) {
+ boost::uniform_int<> dist(0, 255);
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomByte(randomGen, dist);
+ ByteArray result(len);
+ for (size_t i=0; i < len; ++i ) {
+ result[i] = randomByte();
+ }
+ return result;
+ }
+
+ // Server responses
+ void serverRespondHelloOK() {
+ connection->onDataRead(createSafeByteArray("\x05\00", 2));
+ }
+
+ void serverRespondHelloAuthFail() {
+ connection->onDataRead(createSafeByteArray("\x05\xFF", 2));
+ }
+
+ void serverRespondRequestOK() {
+ SafeByteArray dataToSend = createSafeByteArray("\x05\x00\x00\x03", 4);
+ append(dataToSend, createSafeByteArray(destination.size()));
+ append(dataToSend, createSafeByteArray(destination));
+ append(dataToSend, createSafeByteArray("\x00", 1));
+ connection->onDataRead(dataToSend);
+ }
+
+ void serverRespondRequestFail() {
+ SafeByteArray correctData = createSafeByteArray("\x05\x00\x00\x03", 4);
+ append(correctData, createSafeByteArray(destination.size()));
+ append(correctData, createSafeByteArray(destination));
+ append(correctData, createSafeByteArray("\x00", 1));
+
+ SafeByteArray dataToSend;
+ //ByteArray failingData = Hexify::unhexify("8417947d1d305c72c11520ea7d2c6e787396705e72c312c6ccc3f66613d7cae1b91b7ab48e8b59a17d559c15fb51");
+ //append(dataToSend, failingData);
+ //SWIFT_LOG(debug) << "hexed: " << Hexify::hexify(failingData) << std::endl;
+ do {
+ ByteArray rndArray = generateRandomByteArray(correctData.size());
+ dataToSend = createSafeByteArray(rndArray.data(), rndArray.size());
+ } while (dataToSend == correctData);
+ connection->onDataRead(dataToSend);
+ }
+
+private:
+ struct TestHelper {
+ TestHelper() : sessionReadyCalled(false), sessionReadyError(false) {}
+ ByteArray unprocessedInput;
+ bool sessionReadyCalled;
+ bool sessionReadyError;
+
+ void handleConnectionDataWritten(const SafeByteArray& data) {
+ append(unprocessedInput, data);
+ //SWIFT_LOG(debug) << "unprocessedInput (" << unprocessedInput.size() << "): " << Hexify::hexify(unprocessedInput) << std::endl;
+ }
+
+ void handleSessionReady(bool error) {
+ sessionReadyCalled = true;
+ sessionReadyError = error;
+ }
+ };
+
+
+private:
+ struct MockeryConnection : public Connection, public EventOwner, public boost::enable_shared_from_this<MockeryConnection> {
+ public:
+ MockeryConnection(const std::vector<HostAddressPort>& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive), disconnectCalled(false) {}
+
+ void listen() { assert(false); }
+ void connect(const HostAddressPort& address) {
+ hostAddressPort = address;
+ if (isResponsive) {
+ bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end();
+ eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail));
+ }
+ }
+
+ HostAddressPort getLocalAddress() const { return HostAddressPort(); }
+ void disconnect() {
+ disconnectCalled = true;
+ }
+
+ void write(const SafeByteArray& data) {
+ eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this());
+ onDataSent(data);
+ }
+
+ boost::signal<void (const SafeByteArray&)> onDataSent;
+
+ EventLoop* eventLoop;
+ boost::optional<HostAddressPort> hostAddressPort;
+ std::vector<HostAddressPort> failingPorts;
+ bool isResponsive;
+ bool disconnectCalled;
+ };
+
+private:
+ DummyEventLoop* eventLoop;
+ DummyTimerFactory* timerFactory;
+ boost::shared_ptr<MockeryConnection> connection;
+ const std::vector<HostAddressPort> failingPorts;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamClientSessionTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
index 06bc98f..cd480f0 100644
--- a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
@@ -34,6 +34,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void setUp() {
receivedDataChunks = 0;
eventLoop = new DummyEventLoop();
+ bytestreams = new SOCKS5BytestreamRegistry();
connection = boost::shared_ptr<DummyConnection>(new DummyConnection(eventLoop));
connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
@@ -41,6 +42,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void tearDown() {
connection.reset();
+ delete bytestreams;
delete eventLoop;
}
@@ -67,7 +69,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void testRequest() {
boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
ByteArray hostname(createByteArray("abcdef"));
@@ -88,10 +90,11 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void testReceiveData() {
boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
request("abcdef");
eventLoop->processEvents();
+ testling->startTransfer();
skipHeader("abcdef");
CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
@@ -102,11 +105,12 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
testling->setChunkSize(3);
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
request("abcdef");
eventLoop->processEvents();
-
+ testling->startTransfer();
+ eventLoop->processEvents();
skipHeader("abcdef");
CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks);
@@ -141,13 +145,13 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
private:
SOCKS5BytestreamServerSession* createSession() {
- SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, &bytestreams);
+ SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, bytestreams);
return session;
}
private:
DummyEventLoop* eventLoop;
- SOCKS5BytestreamRegistry bytestreams;
+ SOCKS5BytestreamRegistry* bytestreams;
boost::shared_ptr<DummyConnection> connection;
std::vector<unsigned char> receivedData;
int receivedDataChunks;
diff --git a/Swiften/FileTransfer/WriteBytestream.h b/Swiften/FileTransfer/WriteBytestream.h
index c27aeff..fb6f2f1 100644
--- a/Swiften/FileTransfer/WriteBytestream.h
+++ b/Swiften/FileTransfer/WriteBytestream.h
@@ -9,6 +9,8 @@
#include <boost/shared_ptr.hpp>
#include <vector>
+#include <Swiften/Base/boost_bsignals.h>
+
namespace Swift {
class WriteBytestream {
public:
@@ -17,5 +19,7 @@ namespace Swift {
virtual ~WriteBytestream();
virtual void write(const std::vector<unsigned char>&) = 0;
+
+ boost::signal<void (const std::vector<unsigned char>&)> onWrite;
};
}
diff --git a/Swiften/Jingle/FakeJingleSession.cpp b/Swiften/Jingle/FakeJingleSession.cpp
new file mode 100644
index 0000000..82ec631
--- /dev/null
+++ b/Swiften/Jingle/FakeJingleSession.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Jingle/FakeJingleSession.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+
+FakeJingleSession::FakeJingleSession(const JID& initiator, const std::string& id) : JingleSession(initiator, id) {
+
+}
+
+FakeJingleSession::~FakeJingleSession() {
+}
+
+void FakeJingleSession::sendInitiate(const JingleContentID& id, JingleDescription::ref desc, JingleTransportPayload::ref payload) {
+ calledCommands.push_back(InitiateCall(id, desc, payload));
+}
+
+void FakeJingleSession::sendTerminate(JinglePayload::Reason::Type reason) {
+ calledCommands.push_back(TerminateCall(reason));
+}
+
+void FakeJingleSession::sendInfo(boost::shared_ptr<Payload> payload) {
+ calledCommands.push_back(InfoCall(payload));
+}
+
+void FakeJingleSession::sendAccept(const JingleContentID& id, JingleDescription::ref desc, JingleTransportPayload::ref payload) {
+ calledCommands.push_back(AcceptCall(id, desc, payload));
+}
+
+void FakeJingleSession::sendTransportInfo(const JingleContentID& id, JingleTransportPayload::ref payload) {
+ calledCommands.push_back(InfoTransportCall(id, payload));
+}
+
+void FakeJingleSession::sendTransportAccept(const JingleContentID& id, JingleTransportPayload::ref payload) {
+ calledCommands.push_back(AcceptTransportCall(id, payload));
+}
+
+void FakeJingleSession::sendTransportReject(const JingleContentID& id, JingleTransportPayload::ref payload) {
+ calledCommands.push_back(RejectTransportCall(id, payload));
+}
+
+void FakeJingleSession::sendTransportReplace(const JingleContentID& id, JingleTransportPayload::ref payload) {
+ calledCommands.push_back(ReplaceTransportCall(id, payload));
+}
+
+}
diff --git a/Swiften/Jingle/FakeJingleSession.h b/Swiften/Jingle/FakeJingleSession.h
new file mode 100644
index 0000000..fd3d7b7
--- /dev/null
+++ b/Swiften/Jingle/FakeJingleSession.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <vector>
+#include <boost/variant.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Jingle/JingleSession.h>
+#include <Swiften/Jingle/JingleContentID.h>
+
+namespace Swift {
+ class JingleContentID;
+
+ class FakeJingleSession : public JingleSession {
+ public:
+ struct InitiateCall {
+ InitiateCall(JingleContentID contentId, JingleDescription::ref desc, JingleTransportPayload::ref payL) : id(contentId), description(desc), payload(payL) {}
+ JingleContentID id;
+ JingleDescription::ref description;
+ JingleTransportPayload::ref payload;
+ };
+
+ struct TerminateCall {
+ TerminateCall(JinglePayload::Reason::Type r) : reason(r) {}
+ JinglePayload::Reason::Type reason;
+ };
+
+ struct InfoCall {
+ InfoCall(boost::shared_ptr<Payload> info) : payload(info) {}
+ boost::shared_ptr<Payload> payload;
+ };
+
+ struct AcceptCall {
+ AcceptCall(JingleContentID contentId, JingleDescription::ref desc, JingleTransportPayload::ref payL) : id(contentId), description(desc), payload(payL) {}
+ JingleContentID id;
+ JingleDescription::ref description;
+ JingleTransportPayload::ref payload;
+ };
+
+ struct InfoTransportCall {
+ InfoTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+ JingleContentID id;
+ JingleTransportPayload::ref payload;
+ };
+
+ struct AcceptTransportCall {
+ AcceptTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+ JingleContentID id;
+ JingleTransportPayload::ref payload;
+ };
+
+ struct RejectTransportCall {
+ RejectTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+ JingleContentID id;
+ JingleTransportPayload::ref payload;
+ };
+
+ struct ReplaceTransportCall {
+ ReplaceTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+ JingleContentID id;
+ JingleTransportPayload::ref payload;
+ };
+
+ typedef boost::variant<InitiateCall, TerminateCall, AcceptCall, InfoCall, InfoTransportCall, AcceptTransportCall, RejectTransportCall, ReplaceTransportCall> Command;
+
+ public:
+ typedef boost::shared_ptr<FakeJingleSession> ref;
+
+ FakeJingleSession(const JID& initiator, const std::string& id);
+ virtual ~FakeJingleSession();
+
+ virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+ virtual void sendTerminate(JinglePayload::Reason::Type reason);
+ virtual void sendInfo(boost::shared_ptr<Payload>);
+ virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref = JingleTransportPayload::ref());
+ virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref);
+ virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref);
+ virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref);
+ virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref);
+
+ public:
+ std::vector<Command> calledCommands;
+ };
+}
diff --git a/Swiften/Jingle/JingleContentID.h b/Swiften/Jingle/JingleContentID.h
index 8d75581..fc0cc8f 100644
--- a/Swiften/Jingle/JingleContentID.h
+++ b/Swiften/Jingle/JingleContentID.h
@@ -15,6 +15,14 @@ namespace Swift {
public:
JingleContentID(const std::string& name, JingleContentPayload::Creator creator) : name(name), creator(creator) {
}
+
+ const std::string getName() const {
+ return this->name;
+ }
+
+ JingleContentPayload::Creator getCreator() const {
+ return this->creator;
+ }
private:
std::string name;
diff --git a/Swiften/Jingle/JingleResponder.cpp b/Swiften/Jingle/JingleResponder.cpp
index 198f9a2..63f108e 100644
--- a/Swiften/Jingle/JingleResponder.cpp
+++ b/Swiften/Jingle/JingleResponder.cpp
@@ -11,9 +11,14 @@
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swiften/Jingle/JingleSessionImpl.h>
+#include <Swiften/Base/Log.h>
+
namespace Swift {
-JingleResponder::JingleResponder(JingleSessionManager* sessionManager, IQRouter* router) : SetResponder<JinglePayload>(router), sessionManager(sessionManager) {
+JingleResponder::JingleResponder(JingleSessionManager* sessionManager, IQRouter* router) : SetResponder<JinglePayload>(router), sessionManager(sessionManager), router(router) {
+}
+
+JingleResponder::~JingleResponder() {
}
bool JingleResponder::handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<JinglePayload> payload) {
@@ -24,17 +29,29 @@ bool JingleResponder::handleSetRequest(const JID& from, const JID&, const std::s
}
else {
sendResponse(from, id, boost::shared_ptr<JinglePayload>());
- JingleSessionImpl::ref session = boost::make_shared<JingleSessionImpl>(payload->getInitiator(), payload->getSessionID());
- sessionManager->handleIncomingSession(from, session, payload->getContents());
+ if (!payload->getInitiator().isBare()) {
+ JingleSessionImpl::ref session = boost::make_shared<JingleSessionImpl>(payload->getInitiator(), from, payload->getSessionID(), router);
+ sessionManager->handleIncomingSession(from, session, payload->getContents());
+ } else {
+ SWIFT_LOG(debug) << "Unable to create Jingle session due to initiator not being a full JID." << std::endl;
+ }
}
}
else {
- JingleSessionImpl::ref session = sessionManager->getSession(from, payload->getSessionID());
+ JingleSessionImpl::ref session;
+ if (payload->getInitiator().isValid()) {
+ SWIFT_LOG(debug) << "Lookup session by initiator." << std::endl;
+ session = sessionManager->getSession(payload->getInitiator(), payload->getSessionID());
+ } else {
+ SWIFT_LOG(debug) << "Lookup session by from attribute." << std::endl;
+ session = sessionManager->getSession(from, payload->getSessionID());
+ }
if (session) {
session->handleIncomingAction(payload);
sendResponse(from, id, boost::shared_ptr<JinglePayload>());
}
else {
+ std::cerr << "WARN: Didn't find jingle session!" << std::endl;
// TODO: Add jingle-specific error
sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
}
diff --git a/Swiften/Jingle/JingleResponder.h b/Swiften/Jingle/JingleResponder.h
index 6e1965c..6f4d688 100644
--- a/Swiften/Jingle/JingleResponder.h
+++ b/Swiften/Jingle/JingleResponder.h
@@ -16,11 +16,12 @@ namespace Swift {
class JingleResponder : public SetResponder<JinglePayload> {
public:
JingleResponder(JingleSessionManager* sessionManager, IQRouter* router);
-
+ virtual ~JingleResponder();
private:
virtual bool handleSetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr<JinglePayload> payload);
private:
JingleSessionManager* sessionManager;
+ IQRouter* router;
};
}
diff --git a/Swiften/Jingle/JingleSession.cpp b/Swiften/Jingle/JingleSession.cpp
index 1366191..eb649f3 100644
--- a/Swiften/Jingle/JingleSession.cpp
+++ b/Swiften/Jingle/JingleSession.cpp
@@ -11,7 +11,9 @@
namespace Swift {
JingleSession::JingleSession(const JID& initiator, const std::string& id) : initiator(initiator), id(id) {
-
+ // initiator must always be a full JID; session lookup based on it wouldn't work otherwise
+ // this is checked on an upper level so that the assert never fails
+ assert(!initiator.isBare());
}
JingleSession::~JingleSession() {
diff --git a/Swiften/Jingle/JingleSession.h b/Swiften/Jingle/JingleSession.h
index b8117bb..150ad79 100644
--- a/Swiften/Jingle/JingleSession.h
+++ b/Swiften/Jingle/JingleSession.h
@@ -7,6 +7,8 @@
#pragma once
#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
#include <string>
#include <Swiften/Base/boost_bsignals.h>
@@ -30,16 +32,22 @@ namespace Swift {
const std::string& getID() const {
return id;
}
-
- virtual void terminate(JinglePayload::Reason::Type reason) = 0;
- virtual void accept(JingleTransportPayload::ref = JingleTransportPayload::ref()) = 0;
+ virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref) = 0;
+ virtual void sendTerminate(JinglePayload::Reason::Type reason) = 0;
+ virtual void sendInfo(boost::shared_ptr<Payload>) = 0;
+ virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref = JingleTransportPayload::ref()) = 0;
virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) = 0;
- virtual void acceptTransport(const JingleContentID&, JingleTransportPayload::ref) = 0;
- virtual void rejectTransport(const JingleContentID&, JingleTransportPayload::ref) = 0;
+ virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref) = 0;
+ virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref) = 0;
+ virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref) = 0;
public:
- boost::signal<void ()> onSessionTerminateReceived;
+ boost::signal<void (const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref)> onSessionAcceptReceived;
+ boost::signal<void (JinglePayload::ref)> onSessionInfoReceived;
+ boost::signal<void (boost::optional<JinglePayload::Reason>)> onSessionTerminateReceived;
+ boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportAcceptReceived;
boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportInfoReceived;
+ boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportRejectReceived;
boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportReplaceReceived;
private:
diff --git a/Swiften/Jingle/JingleSessionImpl.cpp b/Swiften/Jingle/JingleSessionImpl.cpp
index cbb2b42..98c5196 100644
--- a/Swiften/Jingle/JingleSessionImpl.cpp
+++ b/Swiften/Jingle/JingleSessionImpl.cpp
@@ -8,33 +8,176 @@
#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Parser/PayloadParsers/JingleParser.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Queries/Request.h>
+#include <Swiften/Queries/GenericRequest.h>
+
+#include <Swiften/Base/Log.h>
+
+#include "Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h"
+#include "Swiften/FileTransfer/JingleTransport.h"
+
namespace Swift {
-JingleSessionImpl::JingleSessionImpl(const JID& initiator, const std::string& id) : JingleSession(initiator, id) {
+JingleSessionImpl::JingleSessionImpl(const JID& initiator, const JID& peerJID, const std::string& id, IQRouter* router) : JingleSession(initiator, id), iqRouter(router), peerJID(peerJID) {
+ SWIFT_LOG(debug) << "initiator: " << initiator << ", peerJID: " << peerJID << std::endl;
+}
+
+void JingleSessionImpl::handleIncomingAction(JinglePayload::ref action) {
+ if (action->getAction() == JinglePayload::SessionTerminate) {
+ onSessionTerminateReceived(action->getReason());
+ return;
+ }
+ if (action->getAction() == JinglePayload::SessionInfo) {
+ onSessionInfoReceived(action);
+ return;
+ }
+
+ JingleContentPayload::ref content = action->getPayload<JingleContentPayload>();
+ if (!content) {
+ SWIFT_LOG(debug) << "no content payload!" << std::endl;
+ return;
+ }
+ JingleContentID contentID(content->getName(), content->getCreator());
+ JingleDescription::ref description = content->getDescriptions().empty() ? JingleDescription::ref() : content->getDescriptions()[0];
+ JingleTransportPayload::ref transport = content->getTransports().empty() ? JingleTransportPayload::ref() : content->getTransports()[0];
+ switch(action->getAction()) {
+ case JinglePayload::SessionAccept:
+ onSessionAcceptReceived(contentID, description, transport);
+ return;
+ case JinglePayload::TransportAccept:
+ onTransportAcceptReceived(contentID, transport);
+ return;
+ case JinglePayload::TransportInfo:
+ onTransportInfoReceived(contentID, transport);
+ return;
+ case JinglePayload::TransportReject:
+ onTransportRejectReceived(contentID, transport);
+ return;
+ case JinglePayload::TransportReplace:
+ onTransportReplaceReceived(contentID, transport);
+ return;
+ // following unused Jingle actions
+ case JinglePayload::ContentAccept:
+ case JinglePayload::ContentAdd:
+ case JinglePayload::ContentModify:
+ case JinglePayload::ContentReject:
+ case JinglePayload::ContentRemove:
+ case JinglePayload::DescriptionInfo:
+ case JinglePayload::SecurityInfo:
+
+ // handled elsewhere
+ case JinglePayload::SessionInitiate:
+ case JinglePayload::SessionInfo:
+ case JinglePayload::SessionTerminate:
+
+ case JinglePayload::UnknownAction:
+ return;
+ }
+ std::cerr << "Unhandled Jingle action!!! ACTION: " << action->getAction() << std::endl;
}
-void JingleSessionImpl::handleIncomingAction(JinglePayload::ref) {
+void JingleSessionImpl::sendInitiate(const JingleContentID& id, JingleDescription::ref description, JingleTransportPayload::ref transport) {
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionInitiate, getID());
+ payload->setInitiator(getInitiator());
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(id.getCreator());
+ content->setName(id.getName());
+ content->addDescription(description);
+ content->addTransport(transport);
+ payload->addPayload(content);
+
+ sendSetRequest(payload);
}
-void JingleSessionImpl::terminate(JinglePayload::Reason::Type reason) {
+void JingleSessionImpl::sendTerminate(JinglePayload::Reason::Type reason) {
JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionTerminate, getID());
payload->setReason(JinglePayload::Reason(reason));
- //onAction(payload)
+ payload->setInitiator(getInitiator());
+ sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendInfo(boost::shared_ptr<Payload> info) {
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionInfo, getID());
+ payload->addPayload(info);
+
+ sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendAccept(const JingleContentID& id, JingleDescription::ref description, JingleTransportPayload::ref transPayload) {
+ JinglePayload::ref payload = createPayload();
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(id.getCreator());
+ content->setName(id.getName());
+ content->addTransport(transPayload);
+ content->addDescription(description);
+ payload->setAction(JinglePayload::SessionAccept);
+ payload->addPayload(content);
+
+ // put into IQ:set and send it away
+ sendSetRequest(payload);
}
-void JingleSessionImpl::acceptTransport(const JingleContentID&, JingleTransportPayload::ref) {
+void JingleSessionImpl::sendTransportAccept(const JingleContentID& id, JingleTransportPayload::ref transPayload) {
+ JinglePayload::ref payload = createPayload();
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(id.getCreator());
+ content->setName(id.getName());
+ content->addTransport(transPayload);
+ payload->setAction(JinglePayload::TransportAccept);
+ payload->addPayload(content);
+
+ // put into IQ:set and send it away
+ sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendTransportInfo(const JingleContentID& id, JingleTransportPayload::ref transPayload) {
+ JinglePayload::ref payload = createPayload();
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(id.getCreator());
+ content->setName(id.getName());
+ content->addTransport(transPayload);
+ payload->setAction(JinglePayload::TransportInfo);
+ payload->addPayload(content);
+
+ sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendTransportReject(const JingleContentID& /* id */, JingleTransportPayload::ref /* transPayload */) {
+ SWIFT_LOG(debug) << "NOT IMPLEMENTED YET!!!!" << std::endl;
}
-void JingleSessionImpl::rejectTransport(const JingleContentID&, JingleTransportPayload::ref) {
+void JingleSessionImpl::sendTransportReplace(const JingleContentID& id, JingleTransportPayload::ref transPayload) {
+ JinglePayload::ref payload = createPayload();
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(id.getCreator());
+ content->setName(id.getName());
+ content->addTransport(transPayload);
+ payload->setAction(JinglePayload::TransportReplace);
+ payload->addContent(content);
+
+ sendSetRequest(payload);
}
-void JingleSessionImpl::accept(JingleTransportPayload::ref) {
+
+void JingleSessionImpl::sendSetRequest(JinglePayload::ref payload) {
+ boost::shared_ptr<GenericRequest<JinglePayload> > request = boost::make_shared<GenericRequest<JinglePayload> >(IQ::Set, peerJID, payload, iqRouter);
+ request->send();
}
-void JingleSessionImpl::sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) {
+JinglePayload::ref JingleSessionImpl::createPayload() const {
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setSessionID(getID());
+ payload->setInitiator(getInitiator());
+ return payload;
}
diff --git a/Swiften/Jingle/JingleSessionImpl.h b/Swiften/Jingle/JingleSessionImpl.h
index a254ead..3c1559a 100644
--- a/Swiften/Jingle/JingleSessionImpl.h
+++ b/Swiften/Jingle/JingleSessionImpl.h
@@ -11,20 +11,32 @@
#include <Swiften/Jingle/JingleSession.h>
namespace Swift {
+ class IQRouter;
+
class JingleSessionImpl : public JingleSession {
friend class JingleResponder;
public:
typedef boost::shared_ptr<JingleSessionImpl> ref;
- JingleSessionImpl(const JID& initiator, const std::string& id);
+ JingleSessionImpl(const JID& initiator, const JID&, const std::string& id, IQRouter* router);
- virtual void terminate(JinglePayload::Reason::Type reason);
- virtual void accept(JingleTransportPayload::ref);
+ virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+ virtual void sendTerminate(JinglePayload::Reason::Type reason);
+ virtual void sendInfo(boost::shared_ptr<Payload>);
+ virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref);
- virtual void acceptTransport(const JingleContentID&, JingleTransportPayload::ref);
- virtual void rejectTransport(const JingleContentID&, JingleTransportPayload::ref);
+ virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref);
+ virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref);
+ virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref);
private:
void handleIncomingAction(JinglePayload::ref);
+
+ void sendSetRequest(JinglePayload::ref payload);
+ JinglePayload::ref createPayload() const;
+
+ private:
+ IQRouter *iqRouter;
+ JID peerJID;
};
}
diff --git a/Swiften/Jingle/JingleSessionManager.cpp b/Swiften/Jingle/JingleSessionManager.cpp
index f9a94f3..4299a1e 100644
--- a/Swiften/Jingle/JingleSessionManager.cpp
+++ b/Swiften/Jingle/JingleSessionManager.cpp
@@ -14,15 +14,17 @@ namespace Swift {
JingleSessionManager::JingleSessionManager(IQRouter* router) : router(router) {
responder = new JingleResponder(this, router);
+ responder->start();
}
JingleSessionManager::~JingleSessionManager() {
+ responder->stop();
delete responder;
}
JingleSessionImpl::ref JingleSessionManager::getSession(const JID& jid, const std::string& id) const {
- SessionMap::const_iterator i = incomingSessions.find(JIDSession(jid, id));
- return i != incomingSessions.end() ? i->second : JingleSessionImpl::ref();
+ SessionMap::const_iterator i = sessions.find(JIDSession(jid, id));
+ return i != sessions.end() ? i->second : JingleSessionImpl::ref();
}
void JingleSessionManager::addIncomingSessionHandler(IncomingJingleSessionHandler* handler) {
@@ -33,8 +35,13 @@ void JingleSessionManager::removeIncomingSessionHandler(IncomingJingleSessionHan
erase(incomingSessionHandlers, handler);
}
-void JingleSessionManager::handleIncomingSession(const JID& from, JingleSessionImpl::ref session, const std::vector<JingleContentPayload::ref>& contents) {
- incomingSessions.insert(std::make_pair(JIDSession(from, session->getID()), session));
+void JingleSessionManager::registerOutgoingSession(const JID& initiator, JingleSessionImpl::ref session) {
+ sessions.insert(std::make_pair(JIDSession(initiator, session->getID()), session));
+ SWIFT_LOG(debug) << "Added session " << session->getID() << " for initiator " << initiator.toString() << std::endl;
+}
+
+void JingleSessionManager::handleIncomingSession(const JID& initiator, JingleSessionImpl::ref session, const std::vector<JingleContentPayload::ref>& contents) {
+ sessions.insert(std::make_pair(JIDSession(initiator, session->getID()), session));
foreach (IncomingJingleSessionHandler* handler, incomingSessionHandlers) {
if (handler->handleIncomingJingleSession(session, contents)) {
return;
diff --git a/Swiften/Jingle/JingleSessionManager.h b/Swiften/Jingle/JingleSessionManager.h
index 3b23fb0..50c429b 100644
--- a/Swiften/Jingle/JingleSessionManager.h
+++ b/Swiften/Jingle/JingleSessionManager.h
@@ -28,22 +28,23 @@ namespace Swift {
void addIncomingSessionHandler(IncomingJingleSessionHandler* handler);
void removeIncomingSessionHandler(IncomingJingleSessionHandler* handler);
+ void registerOutgoingSession(const JID& initiator, JingleSessionImpl::ref);
protected:
- void handleIncomingSession(const JID& from, JingleSessionImpl::ref, const std::vector<JingleContentPayload::ref>& contents);
+ void handleIncomingSession(const JID& initiator, JingleSessionImpl::ref, const std::vector<JingleContentPayload::ref>& contents);
private:
IQRouter* router;
JingleResponder* responder;
std::vector<IncomingJingleSessionHandler*> incomingSessionHandlers;
struct JIDSession {
- JIDSession(const JID& jid, const std::string& session) : jid(jid), session(session) {}
+ JIDSession(const JID& initiator, const std::string& session) : initiator(initiator), session(session) {}
bool operator<(const JIDSession& o) const {
- return jid == o.jid ? session < o.session : jid < o.jid;
+ return initiator == o.initiator ? session < o.session : initiator < o.initiator;
}
- JID jid;
+ JID initiator;
std::string session;
};
typedef std::map<JIDSession, JingleSessionImpl::ref> SessionMap;
- SessionMap incomingSessions;
+ SessionMap sessions;
};
}
diff --git a/Swiften/Jingle/SConscript b/Swiften/Jingle/SConscript
index 6b3cfd3..5dcf293 100644
--- a/Swiften/Jingle/SConscript
+++ b/Swiften/Jingle/SConscript
@@ -6,6 +6,7 @@ sources = [
"IncomingJingleSessionHandler.cpp",
"JingleSessionManager.cpp",
"JingleResponder.cpp",
+ "FakeJingleSession.cpp",
]
swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
diff --git a/Swiften/Network/BoostNetworkFactories.cpp b/Swiften/Network/BoostNetworkFactories.cpp
index cc80197..c13270f 100644
--- a/Swiften/Network/BoostNetworkFactories.cpp
+++ b/Swiften/Network/BoostNetworkFactories.cpp
@@ -9,6 +9,7 @@
#include <Swiften/Network/BoostConnectionFactory.h>
#include <Swiften/Network/PlatformDomainNameResolver.h>
#include <Swiften/Network/BoostConnectionServerFactory.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
namespace Swift {
@@ -17,9 +18,11 @@ BoostNetworkFactories::BoostNetworkFactories(EventLoop* eventLoop) {
connectionFactory = new BoostConnectionFactory(ioServiceThread.getIOService(), eventLoop);
domainNameResolver = new PlatformDomainNameResolver(eventLoop);
connectionServerFactory = new BoostConnectionServerFactory(ioServiceThread.getIOService(), eventLoop);
+ platformNATTraversalWorker = new PlatformNATTraversalWorker(eventLoop);
}
BoostNetworkFactories::~BoostNetworkFactories() {
+ delete platformNATTraversalWorker;
delete connectionServerFactory;
delete domainNameResolver;
delete connectionFactory;
diff --git a/Swiften/Network/BoostNetworkFactories.h b/Swiften/Network/BoostNetworkFactories.h
index 96bcc6c..a1cf9ae 100644
--- a/Swiften/Network/BoostNetworkFactories.h
+++ b/Swiften/Network/BoostNetworkFactories.h
@@ -37,11 +37,16 @@ namespace Swift {
return connectionServerFactory;
}
+ PlatformNATTraversalWorker* getPlatformNATTraversalWorker() const {
+ return platformNATTraversalWorker;
+ }
+
private:
BoostIOServiceThread ioServiceThread;
TimerFactory* timerFactory;
ConnectionFactory* connectionFactory;
DomainNameResolver* domainNameResolver;
ConnectionServerFactory* connectionServerFactory;
+ PlatformNATTraversalWorker* platformNATTraversalWorker;
};
}
diff --git a/Swiften/Network/DummyConnectionFactory.h b/Swiften/Network/DummyConnectionFactory.h
new file mode 100644
index 0000000..e8a294e
--- /dev/null
+++ b/Swiften/Network/DummyConnectionFactory.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/DummyConnection.h>
+
+namespace Swift {
+
+class EventLoop;
+
+class DummyConnectionFactory : public ConnectionFactory {
+public:
+ DummyConnectionFactory(EventLoop *eventLoop) : eventLoop(eventLoop) {}
+ virtual ~DummyConnectionFactory() {}
+ virtual boost::shared_ptr<Connection> createConnection() {
+ return boost::make_shared<DummyConnection>(eventLoop);
+ }
+private:
+ EventLoop* eventLoop;
+};
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp
new file mode 100644
index 0000000..69b325c
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "NATPMPNATTraversalForwardPortRequest.h"
+
+#include <natpmp.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+NATPMPNATTraversalForwardPortRequest::NATPMPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalForwardPortRequest(worker), mapping(mapping) {
+
+}
+
+NATPMPNATTraversalForwardPortRequest::~NATPMPNATTraversalForwardPortRequest() {
+
+}
+
+void NATPMPNATTraversalForwardPortRequest::runBlocking() {
+ boost::optional<PortMapping> result;
+
+ natpmp_t natpmp;
+ natpmpresp_t response;
+ initnatpmp(&natpmp, 0, 0);
+
+ do {
+ if (sendnewportmappingrequest(&natpmp, mapping.protocol == PortMapping::TCP ? NATPMP_PROTOCOL_TCP : NATPMP_PROTOCOL_UDP, mapping.leaseInSeconds, mapping.publicPort, mapping.localPort) != 2) {
+ SWIFT_LOG(debug) << "Failed to send NAT-PMP port forwarding request!" << std::endl;
+ break;
+ }
+ int r = 0;
+
+ do {
+ fd_set fds;
+ struct timeval timeout;
+ FD_ZERO(&fds);
+ FD_SET(natpmp.s, &fds);
+ getnatpmprequesttimeout(&natpmp, &timeout);
+ select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+ r = readnatpmpresponseorretry(&natpmp, &response);
+ } while(r == NATPMP_TRYAGAIN);
+
+ if (r == 0) {
+ if (response.pnu.newportmapping.privateport == mapping.localPort &&
+ response.pnu.newportmapping.mappedpublicport == mapping.publicPort) {
+ mapping.leaseInSeconds = response.pnu.newportmapping.lifetime;
+ result = boost::optional<PortMapping>(mapping);
+ }
+ } else {
+ SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl;
+ }
+ } while(false);
+ closenatpmp(&natpmp);
+
+ onResult(result);
+}
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalForwardPortRequest.h b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.h
new file mode 100644
index 0000000..71d8621
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class NATPMPNATTraversalForwardPortRequest : public PlatformNATTraversalForwardPortRequest {
+public:
+ NATPMPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping, PlatformNATTraversalWorker*);
+ virtual ~NATPMPNATTraversalForwardPortRequest();
+
+ virtual void runBlocking();
+
+private:
+ PlatformNATTraversalForwardPortRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp
new file mode 100644
index 0000000..06a21a3
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "NATPMPNATTraversalGetPublicIPRequest.h"
+
+#include <natpmp.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+NATPMPNATTraversalGetPublicIPRequest::NATPMPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalGetPublicIPRequest(worker) {
+
+}
+
+NATPMPNATTraversalGetPublicIPRequest::~NATPMPNATTraversalGetPublicIPRequest() {
+
+}
+
+/*
+TODO: a non-blocking solution should be possible too here
+void NATPMPNATTraversalGetPublicIPRequest::run() {
+ // we can run directly since libnatpmp's API is asynchronous
+ runBlocking();
+}*/
+
+void NATPMPNATTraversalGetPublicIPRequest::runBlocking() {
+ boost::optional<HostAddress> result;
+
+ natpmp_t natpmp;
+ natpmpresp_t response;
+ initnatpmp(&natpmp, 0, 0);
+
+ do {
+ if (sendpublicaddressrequest(&natpmp) != 2) {
+ SWIFT_LOG(debug) << "Failed to send NAT-PMP public address request!" << std::endl;
+ break;
+ }
+ int r = 0;
+
+ do {
+ fd_set fds;
+ struct timeval timeout;
+ FD_ZERO(&fds);
+ FD_SET(natpmp.s, &fds);
+ getnatpmprequesttimeout(&natpmp, &timeout);
+ select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+ r = readnatpmpresponseorretry(&natpmp, &response);
+ } while(r == NATPMP_TRYAGAIN);
+
+ if (r == 0) {
+ result = boost::optional<HostAddress>(HostAddress(reinterpret_cast<const unsigned char*>(&(response.pnu.publicaddress.addr)), 4));
+ } else {
+ SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl;
+ }
+ } while(false);
+ closenatpmp(&natpmp);
+ onResult(result);
+}
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h
new file mode 100644
index 0000000..6112091
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+
+namespace Swift {
+
+class NATPMPNATTraversalGetPublicIPRequest : public PlatformNATTraversalGetPublicIPRequest {
+public:
+ NATPMPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker*);
+ virtual ~NATPMPNATTraversalGetPublicIPRequest();
+
+ //virtual void run();
+ virtual void runBlocking();
+};
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp
new file mode 100644
index 0000000..c99ac92
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "NATPMPNATTraversalRemovePortForwardingRequest.h"
+
+#include <boost/format.hpp>
+
+#include <natpmp.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+NATPMPNATTraversalRemovePortForwardingRequest::NATPMPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalRemovePortForwardingRequest(worker), mapping(mapping) {
+
+}
+
+NATPMPNATTraversalRemovePortForwardingRequest::~NATPMPNATTraversalRemovePortForwardingRequest() {
+
+}
+
+void NATPMPNATTraversalRemovePortForwardingRequest::runBlocking() {
+ boost::optional<bool> result;
+
+ natpmp_t natpmp;
+ natpmpresp_t response;
+ initnatpmp(&natpmp, 0, 0);
+
+ do {
+ if (sendnewportmappingrequest(&natpmp, mapping.protocol == PortMapping::TCP ? NATPMP_PROTOCOL_TCP : NATPMP_PROTOCOL_UDP, 0, 0, mapping.localPort) != 2) {
+ SWIFT_LOG(debug) << "Failed to send NAT-PMP remove forwarding request!" << std::endl;
+ break;
+ }
+ int r = 0;
+
+ do {
+ fd_set fds;
+ struct timeval timeout;
+ FD_ZERO(&fds);
+ FD_SET(natpmp.s, &fds);
+ getnatpmprequesttimeout(&natpmp, &timeout);
+ select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+ r = readnatpmpresponseorretry(&natpmp, &response);
+ } while(r == NATPMP_TRYAGAIN);
+
+ if (r == 0) {
+ if (response.pnu.newportmapping.privateport == mapping.localPort &&
+ response.pnu.newportmapping.mappedpublicport == mapping.publicPort) {
+ mapping.leaseInSeconds = response.pnu.newportmapping.lifetime;
+ result = boost::optional<bool>(true);
+ }
+ } else {
+ result = boost::optional<bool>(false);
+ SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl;
+ }
+ } while(false);
+ closenatpmp(&natpmp);
+
+ onResult(result);
+}
+
+HostAddress NATPMPNATTraversalRemovePortForwardingRequest::getLocalClient() {
+ PlatformNetworkEnvironment env;
+
+ foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+ if (!iface->isLoopback()) {
+ foreach (HostAddress address, iface->getAddresses()) {
+ if (address.getRawAddress().is_v4()) {
+ return address;
+ }
+ }
+ }
+ }
+ return HostAddress();
+}
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h
new file mode 100644
index 0000000..c4ffcf3
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+
+namespace Swift {
+
+class NATPMPNATTraversalRemovePortForwardingRequest : public PlatformNATTraversalRemovePortForwardingRequest {
+public:
+ NATPMPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping, PlatformNATTraversalWorker*);
+ virtual ~NATPMPNATTraversalRemovePortForwardingRequest();
+
+ virtual void runBlocking();
+
+private:
+ HostAddress getLocalClient();
+
+private:
+ PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/NetworkEnvironment.h b/Swiften/Network/NetworkEnvironment.h
new file mode 100644
index 0000000..348bdb9
--- /dev/null
+++ b/Swiften/Network/NetworkEnvironment.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+namespace Swift {
+
+class NetworkEnvironment {
+public:
+ virtual ~NetworkEnvironment() {};
+ virtual std::vector<NetworkInterface::ref> getNetworkInterfaces() = 0;
+
+ boost::signal <void (NetworkInterface::ref)> onNetworkInterfaceChange;
+};
+
+}
diff --git a/Swiften/Network/NetworkFactories.h b/Swiften/Network/NetworkFactories.h
index d0d2299..e7d2ff0 100644
--- a/Swiften/Network/NetworkFactories.h
+++ b/Swiften/Network/NetworkFactories.h
@@ -11,6 +11,7 @@ namespace Swift {
class ConnectionFactory;
class DomainNameResolver;
class ConnectionServerFactory;
+ class PlatformNATTraversalWorker;
/**
* An interface collecting network factories.
@@ -23,5 +24,6 @@ namespace Swift {
virtual ConnectionFactory* getConnectionFactory() const = 0;
virtual DomainNameResolver* getDomainNameResolver() const = 0;
virtual ConnectionServerFactory* getConnectionServerFactory() const = 0;
+ virtual PlatformNATTraversalWorker* getPlatformNATTraversalWorker() const = 0;
};
}
diff --git a/Swiften/Network/NetworkInterface.h b/Swiften/Network/NetworkInterface.h
new file mode 100644
index 0000000..062e1f9
--- /dev/null
+++ b/Swiften/Network/NetworkInterface.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Network/HostAddress.h>
+
+namespace Swift {
+
+class NetworkInterface {
+public:
+ typedef boost::shared_ptr<NetworkInterface> ref;
+
+public:
+ enum InterfaceType {
+ WLAN,
+ Ethernet,
+ Mobile,
+ VPN,
+ };
+
+public:
+ virtual ~NetworkInterface() {};
+ virtual std::vector<HostAddress> getAddresses() = 0;
+ virtual std::string getName() = 0;
+ virtual bool isLoopback() = 0;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp b/Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp
new file mode 100644
index 0000000..b28024a
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalForwardPortRequest.h"
+
+namespace Swift {
+
+PlatformNATTraversalForwardPortRequest::PlatformNATTraversalForwardPortRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) {
+}
+
+PlatformNATTraversalForwardPortRequest::~PlatformNATTraversalForwardPortRequest() {
+
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalForwardPortRequest.h b/Swiften/Network/PlatformNATTraversalForwardPortRequest.h
new file mode 100644
index 0000000..cb1750c
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalForwardPortRequest.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalForwardPortRequest : public PlatformNATTraversalRequest {
+public:
+ struct PortMapping {
+ enum Protocol {
+ TCP,
+ UDP,
+ };
+
+ unsigned int publicPort;
+ unsigned int localPort;
+ Protocol protocol;
+ unsigned long leaseInSeconds;
+ };
+
+public:
+ PlatformNATTraversalForwardPortRequest(PlatformNATTraversalWorker* worker);
+ virtual ~PlatformNATTraversalForwardPortRequest();
+
+ boost::signal<void (boost::optional<PortMapping>)> onResult;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp
new file mode 100644
index 0000000..7a57e30
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalGetPublicIPRequest.h"
+
+namespace Swift {
+
+PlatformNATTraversalGetPublicIPRequest::PlatformNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) {
+}
+
+PlatformNATTraversalGetPublicIPRequest::~PlatformNATTraversalGetPublicIPRequest() {
+
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h
new file mode 100644
index 0000000..1cb37fe
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalGetPublicIPRequest : public PlatformNATTraversalRequest {
+public:
+ PlatformNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker);
+ virtual ~PlatformNATTraversalGetPublicIPRequest();
+
+ boost::signal<void (boost::optional<HostAddress>)> onResult;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp
new file mode 100644
index 0000000..514988e
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalRemovePortForwardingRequest.h"
+
+namespace Swift {
+
+PlatformNATTraversalRemovePortForwardingRequest::PlatformNATTraversalRemovePortForwardingRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) {
+}
+
+PlatformNATTraversalRemovePortForwardingRequest::~PlatformNATTraversalRemovePortForwardingRequest() {
+
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h
new file mode 100644
index 0000000..03427ad
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalRemovePortForwardingRequest : public PlatformNATTraversalRequest {
+public:
+ struct PortMapping {
+ enum Protocol {
+ TCP,
+ UDP,
+ };
+
+ unsigned int publicPort;
+ unsigned int localPort;
+ Protocol protocol;
+ unsigned long leaseInSeconds;
+ };
+
+public:
+ PlatformNATTraversalRemovePortForwardingRequest(PlatformNATTraversalWorker* worker);
+ virtual ~PlatformNATTraversalRemovePortForwardingRequest();
+
+ boost::signal<void (boost::optional<bool> /* failure */)> onResult;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRequest.cpp b/Swiften/Network/PlatformNATTraversalRequest.cpp
new file mode 100644
index 0000000..25e8a32
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRequest.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalRequest.h"
+
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+
+namespace Swift {
+
+PlatformNATTraversalRequest::PlatformNATTraversalRequest(PlatformNATTraversalWorker* worker) : worker(worker) {
+
+}
+
+PlatformNATTraversalRequest::~PlatformNATTraversalRequest() {
+
+}
+
+void PlatformNATTraversalRequest::run() {
+ worker->addRequestToQueue(shared_from_this());
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRequest.h b/Swiften/Network/PlatformNATTraversalRequest.h
new file mode 100644
index 0000000..4b760ad
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRequest.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalRequest : public boost::enable_shared_from_this<PlatformNATTraversalRequest> {
+public:
+ typedef boost::shared_ptr<PlatformNATTraversalRequest> ref;
+
+public:
+ PlatformNATTraversalRequest(PlatformNATTraversalWorker* worker);
+ virtual ~PlatformNATTraversalRequest();
+
+ virtual void run();
+ virtual void runBlocking() = 0;
+
+private:
+ PlatformNATTraversalWorker* worker;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalWorker.cpp b/Swiften/Network/PlatformNATTraversalWorker.cpp
new file mode 100644
index 0000000..a4efedd
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalWorker.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalWorker.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/UPnPNATTraversalForwardPortRequest.h>
+#include <Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/NATPMPNATTraversalForwardPortRequest.h>
+#include <Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+
+namespace Swift {
+
+PlatformNATTraversalWorker::PlatformNATTraversalWorker(EventLoop* eventLoop) : backendType(NotYetDecided), eventLoop(eventLoop), stopRequested(false) {
+ checkAvailableNATTraversalProtocols();
+ thread = new boost::thread(boost::bind(&PlatformNATTraversalWorker::run, this));
+}
+
+PlatformNATTraversalWorker::~PlatformNATTraversalWorker() {
+ stopRequested = true;
+ addRequestToQueue(boost::shared_ptr<PlatformNATTraversalRequest>());
+ thread->join();
+ delete thread;
+}
+
+boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> PlatformNATTraversalWorker::createGetPublicIPRequest() {
+ switch(backendType) {
+ case UPnP:
+ return boost::make_shared<UPnPNATTraversalGetPublicIPRequest>(this);
+ case NATPMP:
+ return boost::make_shared<NATPMPNATTraversalGetPublicIPRequest>(this);
+ case NotYetDecided:
+ case None:
+ break;
+ }
+ return boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest>();
+}
+
+boost::shared_ptr<PlatformNATTraversalForwardPortRequest> PlatformNATTraversalWorker::createForwardPortRequest(unsigned int localPort, unsigned int publicPort) {
+ PlatformNATTraversalForwardPortRequest::PortMapping mapping;
+ mapping.protocol = PlatformNATTraversalForwardPortRequest::PortMapping::TCP;
+ mapping.leaseInSeconds = 60 * 60 * 24;
+ mapping.localPort = localPort;
+ mapping.publicPort = publicPort;
+
+ switch(backendType) {
+ case UPnP:
+ return boost::make_shared<UPnPNATTraversalForwardPortRequest>(mapping, this);
+ case NATPMP:
+ return boost::make_shared<NATPMPNATTraversalForwardPortRequest>(mapping, this);
+ case NotYetDecided:
+ case None:
+ break;
+ }
+ return boost::shared_ptr<PlatformNATTraversalForwardPortRequest>();
+}
+
+boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> PlatformNATTraversalWorker::createRemovePortForwardingRequest(unsigned int localPort, unsigned int publicPort) {
+ PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping;
+ mapping.protocol = PlatformNATTraversalRemovePortForwardingRequest::PortMapping::TCP;
+ mapping.leaseInSeconds = 60 * 60 * 24;
+ mapping.localPort = localPort;
+ mapping.publicPort = publicPort;
+
+ switch(backendType) {
+ case UPnP:
+ return boost::make_shared<UPnPNATTraversalRemovePortForwardingRequest>(mapping, this);
+ case NATPMP:
+ return boost::make_shared<NATPMPNATTraversalRemovePortForwardingRequest>(mapping, this);
+ case NotYetDecided:
+ case None:
+ break;
+ }
+ return boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest>();
+}
+
+void PlatformNATTraversalWorker::run() {
+ while (!stopRequested) {
+ PlatformNATTraversalRequest::ref request;
+ {
+ boost::unique_lock<boost::mutex> lock(queueMutex);
+ while (queue.empty()) {
+ queueNonEmpty.wait(lock);
+ }
+ request = queue.front();
+ queue.pop_front();
+ }
+ // Check whether we don't have a non-null request (used to stop the
+ // worker)
+ if (request) {
+ request->runBlocking();
+ }
+ }
+}
+
+void PlatformNATTraversalWorker::addRequestToQueue(PlatformNATTraversalRequest::ref request) {
+ {
+ boost::lock_guard<boost::mutex> lock(queueMutex);
+ queue.push_back(request);
+ }
+ queueNonEmpty.notify_one();
+}
+
+void PlatformNATTraversalWorker::checkAvailableNATTraversalProtocols() {
+ boost::shared_ptr<UPnPNATTraversalGetPublicIPRequest> upnpRequest = boost::make_shared<UPnPNATTraversalGetPublicIPRequest>(this);
+ upnpRequest->onResult.connect(boost::bind(&PlatformNATTraversalWorker::handleUPnPGetPublicIPResult, this, _1));
+
+ boost::shared_ptr<NATPMPNATTraversalGetPublicIPRequest> natpmpRequest = boost::make_shared<NATPMPNATTraversalGetPublicIPRequest>(this);
+ natpmpRequest->onResult.connect(boost::bind(&PlatformNATTraversalWorker::handleNATPMPGetPublicIPResult, this, _1));
+
+ upnpRequest->run();
+ natpmpRequest->run();
+}
+
+void PlatformNATTraversalWorker::handleUPnPGetPublicIPResult(boost::optional<HostAddress> address) {
+ if (backendType == NotYetDecided || backendType == None) {
+ if (address) {
+ SWIFT_LOG(debug) << "Found UPnP IGD in the local network." << std::endl;
+ backendType = UPnP;
+ }
+ }
+}
+
+void PlatformNATTraversalWorker::handleNATPMPGetPublicIPResult(boost::optional<HostAddress> address) {
+ if (backendType == NotYetDecided || backendType == None) {
+ if (address) {
+ SWIFT_LOG(debug) << "Found NAT-PMP device in the local network." << std::endl;
+ backendType = NATPMP;
+ }
+ }
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalWorker.h b/Swiften/Network/PlatformNATTraversalWorker.h
new file mode 100644
index 0000000..7c249cc
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalWorker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <deque>
+#include <boost/optional.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class EventLoop;
+class PlatformNATTraversalGetPublicIPRequest;
+class PlatformNATTraversalForwardPortRequest;
+class PlatformNATTraversalRemovePortForwardingRequest;
+
+class PlatformNATTraversalWorker {
+private:
+ enum BackendType {
+ NotYetDecided,
+ UPnP,
+ NATPMP,
+ None,
+ };
+
+public:
+ PlatformNATTraversalWorker(EventLoop* eventLoop);
+ ~PlatformNATTraversalWorker();
+
+ boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> createGetPublicIPRequest();
+ boost::shared_ptr<PlatformNATTraversalForwardPortRequest> createForwardPortRequest(unsigned int localPort, unsigned int publicPort);
+ boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> createRemovePortForwardingRequest(unsigned int localPort, unsigned int publicPort);
+
+ void run();
+ void addRequestToQueue(PlatformNATTraversalRequest::ref);
+
+private:
+ void checkAvailableNATTraversalProtocols();
+ void handleUPnPGetPublicIPResult(boost::optional<HostAddress> address);
+ void handleNATPMPGetPublicIPResult(boost::optional<HostAddress> address);
+
+private:
+ BackendType backendType;
+ EventLoop* eventLoop;
+ bool stopRequested;
+ boost::thread* thread;
+ std::deque<PlatformNATTraversalRequest::ref> queue;
+ boost::mutex queueMutex;
+ boost::condition_variable queueNonEmpty;
+};
+
+}
diff --git a/Swiften/Network/PlatformNetworkEnvironment.h b/Swiften/Network/PlatformNetworkEnvironment.h
new file mode 100644
index 0000000..c6b945e
--- /dev/null
+++ b/Swiften/Network/PlatformNetworkEnvironment.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/Platform.h>
+
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+#include <Swiften/Network/UnixNetworkEnvironment.h>
+namespace Swift {
+ typedef UnixNetworkEnvironment PlatformNetworkEnvironment;
+}
+#elif defined(SWIFTEN_PLATFORM_WIN32)
+#include <Swiften/Network/WindowsNetworkEnvironment.h>
+namespace Swift {
+ typedef WindowsNetworkEnvironment PlatformNetworkEnvironment;
+}
+#else
+#include <Swiften/Network/UnixNetworkEnvironment.h>
+namespace Swift {
+ typedef UnixNetworkEnvironment PlatformNetworkEnvironment;
+}
+#endif
diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript
index 965361b..e9853dd 100644
--- a/Swiften/Network/SConscript
+++ b/Swiften/Network/SConscript
@@ -40,7 +40,18 @@ sourceList = [
"Timer.cpp",
"BoostTimer.cpp",
"ProxyProvider.cpp",
- "NullProxyProvider.cpp"
+ "NullProxyProvider.cpp",
+ "PlatformNATTraversalWorker.cpp",
+ "PlatformNATTraversalGetPublicIPRequest.cpp",
+ "PlatformNATTraversalForwardPortRequest.cpp",
+ "PlatformNATTraversalRemovePortForwardingRequest.cpp",
+ "PlatformNATTraversalRequest.cpp",
+ "UPnPNATTraversalGetPublicIPRequest.cpp",
+ "UPnPNATTraversalForwardPortRequest.cpp",
+ "UPnPNATTraversalRemovePortForwardingRequest.cpp",
+ "NATPMPNATTraversalGetPublicIPRequest.cpp",
+ "NATPMPNATTraversalForwardPortRequest.cpp",
+ "NATPMPNATTraversalRemovePortForwardingRequest.cpp",
]
if myenv.get("HAVE_CARES", False) :
@@ -49,9 +60,12 @@ if myenv.get("HAVE_CARES", False) :
if myenv["PLATFORM"] == "darwin" :
myenv.Append(FRAMEWORKS = ["CoreServices", "SystemConfiguration"])
sourceList += [ "MacOSXProxyProvider.cpp" ]
+ sourceList += [ "UnixNetworkEnvironment.cpp" ]
elif myenv["PLATFORM"] == "win32" :
sourceList += [ "WindowsProxyProvider.cpp" ]
+ sourceList += [ "WindowsNetworkEnvironment.cpp" ]
else :
+ sourceList += [ "UnixNetworkEnvironment.cpp" ]
sourceList += [ "UnixProxyProvider.cpp" ]
sourceList += [ "EnvironmentProxyProvider.cpp" ]
if myenv.get("HAVE_GCONF", 0) :
diff --git a/Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp b/Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp
new file mode 100644
index 0000000..c95066e
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UPnPNATTraversalForwardPortRequest.h"
+
+#include <boost/format.hpp>
+
+#include <miniupnpc.h>
+#include <upnpcommands.h>
+#include <upnperrors.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+UPnPNATTraversalForwardPortRequest::UPnPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalForwardPortRequest(worker), mapping(mapping) {
+
+}
+
+UPnPNATTraversalForwardPortRequest::~UPnPNATTraversalForwardPortRequest() {
+
+}
+
+void UPnPNATTraversalForwardPortRequest::runBlocking() {
+ boost::optional<PortMapping> result;
+
+ UPNPDev* deviceList = 0;
+ int error = 0;
+ char lanAddrress[64];
+
+ std::string publicPort = str(boost::format("%d") % mapping.publicPort);
+ std::string localPort = str(boost::format("%d") % mapping.localPort);
+ std::string internalClient = getLocalClient().toString();
+ std::string leaseSeconds = str(boost::format("%d") % mapping.leaseInSeconds);
+ UPNPUrls urls;
+ IGDdatas data;
+
+ do {
+ // find valid IGD
+ deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error);
+ if (!deviceList) {
+ break;
+ }
+
+ if (!UPNP_GetValidIGD(deviceList, &urls, &data, lanAddrress, sizeof(lanAddrress))) {
+ break;
+ }
+
+ /*
+ int ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+ if (ret != UPNPCOMMAND_SUCCESS) {
+ break;
+ }*/
+
+ int ret = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, publicPort.c_str(), localPort.c_str(), internalClient.c_str(), 0, mapping.protocol == PlatformNATTraversalForwardPortRequest::PortMapping::TCP ? "TCP" : "UDP", 0, leaseSeconds.c_str());
+ if (ret == UPNPCOMMAND_SUCCESS) {
+ result = boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping>(mapping);
+ }
+ } while(false);
+
+ freeUPNPDevlist(deviceList); deviceList = 0;
+
+ onResult(result);
+}
+
+HostAddress UPnPNATTraversalForwardPortRequest::getLocalClient() {
+ PlatformNetworkEnvironment env;
+
+ foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+ if (!iface->isLoopback()) {
+ foreach (HostAddress address, iface->getAddresses()) {
+ if (address.getRawAddress().is_v4()) {
+ return address;
+ }
+ }
+ }
+ }
+ return HostAddress();
+}
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalForwardPortRequest.h b/Swiften/Network/UPnPNATTraversalForwardPortRequest.h
new file mode 100644
index 0000000..931efee
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalForwardPortRequest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class UPnPNATTraversalForwardPortRequest : public PlatformNATTraversalForwardPortRequest {
+public:
+ UPnPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping, PlatformNATTraversalWorker*);
+ virtual ~UPnPNATTraversalForwardPortRequest();
+
+ virtual void runBlocking();
+
+private:
+ HostAddress getLocalClient();
+
+private:
+ PlatformNATTraversalForwardPortRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp
new file mode 100644
index 0000000..4a7c247
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UPnPNATTraversalGetPublicIPRequest.h"
+
+#include <miniupnpc.h>
+#include <upnpcommands.h>
+#include <upnperrors.h>
+
+namespace Swift {
+
+UPnPNATTraversalGetPublicIPRequest::UPnPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalGetPublicIPRequest(worker) {
+
+}
+
+UPnPNATTraversalGetPublicIPRequest::~UPnPNATTraversalGetPublicIPRequest() {
+
+}
+
+void UPnPNATTraversalGetPublicIPRequest::runBlocking() {
+ boost::optional<HostAddress> result;
+
+ UPNPDev* deviceList = 0;
+ int error = 0;
+ char lanAddrress[64];
+ char externalIPAddress[40];
+ UPNPUrls urls;
+ IGDdatas data;
+
+ do {
+ // find valid IGD
+ deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error);
+ if (!deviceList) {
+ break;
+ }
+
+ if (!UPNP_GetValidIGD(deviceList, &urls, &data, lanAddrress, sizeof(lanAddrress))) {
+ break;
+ }
+
+ int ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+ if (ret != UPNPCOMMAND_SUCCESS) {
+ break;
+ } else {
+ result = HostAddress(std::string(externalIPAddress));
+ }
+ } while(false);
+
+ freeUPNPDevlist(deviceList); deviceList = 0;
+
+ onResult(result);
+}
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h
new file mode 100644
index 0000000..9d50001
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+
+namespace Swift {
+
+class UPnPNATTraversalGetPublicIPRequest : public PlatformNATTraversalGetPublicIPRequest {
+public:
+ UPnPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker*);
+ virtual ~UPnPNATTraversalGetPublicIPRequest();
+
+ virtual void runBlocking();
+};
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp
new file mode 100644
index 0000000..2026880
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UPnPNATTraversalRemovePortForwardingRequest.h"
+
+#include <boost/format.hpp>
+
+#include <miniupnpc.h>
+#include <upnpcommands.h>
+#include <upnperrors.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+UPnPNATTraversalRemovePortForwardingRequest::UPnPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalRemovePortForwardingRequest(worker), mapping(mapping) {
+
+}
+
+UPnPNATTraversalRemovePortForwardingRequest::~UPnPNATTraversalRemovePortForwardingRequest() {
+
+}
+
+void UPnPNATTraversalRemovePortForwardingRequest::runBlocking() {
+ boost::optional<bool> result;
+
+ UPNPDev* deviceList = 0;
+ int error = 0;
+ char lanAddrress[64];
+
+ std::string publicPort = str(boost::format("%d") % mapping.publicPort);
+ std::string localPort = str(boost::format("%d") % mapping.localPort);
+ std::string internalClient = getLocalClient().toString();
+ std::string leaseSeconds = str(boost::format("%d") % mapping.leaseInSeconds);
+ UPNPUrls urls;
+ IGDdatas data;
+
+ do {
+ // find valid IGD
+ deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error);
+ if (!deviceList) {
+ break;
+ }
+
+ if (!UPNP_GetValidIGD(deviceList, &urls, &data, lanAddrress, sizeof(lanAddrress))) {
+ break;
+ }
+
+ /*
+ int ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+ if (ret != UPNPCOMMAND_SUCCESS) {
+ break;
+ }*/
+ SWIFT_LOG(debug) << "Start removing port forwarding..." << std::endl;
+ int ret = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, publicPort.c_str(), mapping.protocol == PlatformNATTraversalRemovePortForwardingRequest::PortMapping::TCP ? "TCP" : "UDP", 0);
+
+ if (ret == UPNPCOMMAND_SUCCESS) {
+ SWIFT_LOG(debug) << "Removing port " << publicPort << " successfull." << std::endl;
+ result = true;
+ } else {
+ SWIFT_LOG(debug) << "Removing port " << publicPort << " failed." << std::endl;
+ result = false;
+ }
+ } while(false);
+
+ freeUPNPDevlist(deviceList); deviceList = 0;
+
+ onResult(result);
+}
+
+HostAddress UPnPNATTraversalRemovePortForwardingRequest::getLocalClient() {
+ PlatformNetworkEnvironment env;
+
+ foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+ if (!iface->isLoopback()) {
+ foreach (HostAddress address, iface->getAddresses()) {
+ if (address.getRawAddress().is_v4()) {
+ return address;
+ }
+ }
+ }
+ }
+ return HostAddress();
+}
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h
new file mode 100644
index 0000000..ad1e019
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+
+namespace Swift {
+
+class UPnPNATTraversalRemovePortForwardingRequest : public PlatformNATTraversalRemovePortForwardingRequest {
+public:
+ UPnPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping, PlatformNATTraversalWorker*);
+ virtual ~UPnPNATTraversalRemovePortForwardingRequest();
+
+ virtual void runBlocking();
+
+private:
+ HostAddress getLocalClient();
+
+private:
+ PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/UnixNetworkEnvironment.cpp b/Swiften/Network/UnixNetworkEnvironment.cpp
new file mode 100644
index 0000000..649855d
--- /dev/null
+++ b/Swiften/Network/UnixNetworkEnvironment.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UnixNetworkEnvironment.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+namespace Swift {
+
+std::vector<NetworkInterface::ref> UnixNetworkEnvironment::getNetworkInterfaces() {
+ std::map<std::string, UnixNetworkInterface::ref> interfaces;
+ std::vector<NetworkInterface::ref> result;
+
+ ifaddrs *addrs = 0;
+ int ret = getifaddrs(&addrs);
+ if (ret != 0) {
+ return result;
+ }
+
+ for (ifaddrs *a = addrs; a != 0; a = a->ifa_next) {
+ std::string name(a->ifa_name);
+ std::string ip;
+ if (a->ifa_addr->sa_family == PF_INET) {
+ sockaddr_in* sa = reinterpret_cast<sockaddr_in*>(a->ifa_addr);
+ char str[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &(sa->sin_addr), str, INET_ADDRSTRLEN);
+ ip.assign(str);
+ }
+ else if (a->ifa_addr->sa_family == PF_INET6) {
+ sockaddr_in6* sa = reinterpret_cast<sockaddr_in6*>(a->ifa_addr);
+ char str[INET6_ADDRSTRLEN];
+ /*if (IN6_IS_ADDR_LINKLOCAL(sa)) {
+ continue;
+ }*/
+ inet_ntop(AF_INET6, &(sa->sin6_addr), str, INET6_ADDRSTRLEN);
+ ip.assign(str);
+ }
+ if (!ip.empty()) {
+ if (interfaces.find(name) == interfaces.end()) {
+ interfaces[name] = boost::make_shared<UnixNetworkInterface>(name);
+ if (a->ifa_flags & IFF_LOOPBACK) {
+ interfaces[name]->setLoopback(true);
+ }
+ }
+ interfaces[name]->addHostAddress(HostAddress(ip));
+ }
+ }
+
+ freeifaddrs(addrs);
+
+ for(std::map<std::string, UnixNetworkInterface::ref>::iterator i = interfaces.begin(); i != interfaces.end(); ++i) {
+ result.push_back(i->second);
+ }
+ return result;
+}
+
+}
diff --git a/Swiften/Network/UnixNetworkEnvironment.h b/Swiften/Network/UnixNetworkEnvironment.h
new file mode 100644
index 0000000..e4b2f37
--- /dev/null
+++ b/Swiften/Network/UnixNetworkEnvironment.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/Network/NetworkEnvironment.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+namespace Swift {
+
+class UnixNetworkEnvironment : public NetworkEnvironment {
+ class UnixNetworkInterface : public NetworkInterface {
+ public:
+ typedef boost::shared_ptr<UnixNetworkInterface> ref;
+
+ public:
+ UnixNetworkInterface(std::string name) : name(name), loopback(false) { }
+
+ std::vector<HostAddress> getAddresses() {
+ return addresses;
+ }
+
+ std::string getName() {
+ return name;
+ }
+
+ bool isLoopback() {
+ return loopback;
+ }
+
+ private:
+ void addHostAddress(HostAddress address) {
+ addresses.push_back(address);
+ }
+
+ void setLoopback(bool loopback) {
+ this->loopback = loopback;
+ }
+
+ private:
+ friend class UnixNetworkEnvironment;
+ std::vector<HostAddress> addresses;
+ std::string name;
+ InterfaceType type;
+ bool loopback;
+ };
+
+public:
+ std::vector<NetworkInterface::ref> getNetworkInterfaces();
+};
+
+}
diff --git a/Swiften/Network/WindowsNetworkEnvironment.cpp b/Swiften/Network/WindowsNetworkEnvironment.cpp
new file mode 100644
index 0000000..5163f43
--- /dev/null
+++ b/Swiften/Network/WindowsNetworkEnvironment.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "WindowsNetworkEnvironment.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+#include <winsock2.h>
+#include <iphlpapi.h>
+
+namespace Swift {
+
+std::string winSocketAddressToStdString(const SOCKET_ADDRESS& socketAddress) {
+ char text[46];
+ ULONG bufferSize = sizeof(text);
+ std::string result;
+
+ int ret = WSAAddressToString(socketAddress.lpSockaddr, socketAddress.iSockaddrLength, NULL, text, &bufferSize);
+ if (ret == 0) {
+ result.assign(text, sizeof(text));
+ }
+ return result;
+}
+
+std::vector<NetworkInterface::ref> WindowsNetworkEnvironment::getNetworkInterfaces() {
+ std::map<std::string, WindowsNetworkInterface::ref> interfaces;
+ std::vector<NetworkInterface::ref> result;
+
+ IP_ADAPTER_ADDRESSES preBuffer[5];
+ PIP_ADAPTER_ADDRESSES adapterStart = preBuffer;
+
+ ULONG bufferSize = sizeof(preBuffer);
+
+ ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES | GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
+
+ ULONG ret = GetAdaptersAddresses( AF_UNSPEC, flags, NULL, adapterStart, &bufferSize);
+ if (ret == ERROR_BUFFER_OVERFLOW) {
+ adapterStart = new IP_ADAPTER_ADDRESSES[bufferSize / sizeof(IP_ADAPTER_ADDRESSES)];
+ if (!adapterStart) {
+ return result;
+ }
+ ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterStart, &bufferSize);
+ }
+ if (ret != ERROR_SUCCESS) {
+ if (adapterStart != preBuffer) {
+ delete adapterStart;
+ }
+ return result;
+ }
+
+ for (PIP_ADAPTER_ADDRESSES adapter = adapterStart; adapter; adapter = adapter->Next) {
+ std::string name(adapter->AdapterName);
+
+ if (adapter->OperStatus != IfOperStatusUp) {
+ continue;
+ }
+
+ // iterate over addresses
+ for (PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress; address; address = address->Next) {
+ std::string ip;
+
+ if (address->Address.lpSockaddr->sa_family == PF_INET ||
+ address->Address.lpSockaddr->sa_family == PF_INET6) {
+ ip = winSocketAddressToStdString(address->Address);
+ if (!ip.empty()) {
+ if (interfaces.find(name) == interfaces.end()) {
+ interfaces[name] = boost::make_shared<WindowsNetworkInterface>();
+ interfaces[name]->setName(name);
+ }
+ interfaces[name]->addHostAddress(HostAddress(ip));
+ }
+ }
+ }
+ }
+
+ if (adapterStart != preBuffer) {
+ //delete adapterStart;
+ }
+
+ for(std::map<std::string, WindowsNetworkInterface::ref>::iterator i = interfaces.begin(); i != interfaces.end(); ++i) {
+ result.push_back(i->second);
+ }
+ return result;
+}
+
+}
diff --git a/Swiften/Network/WindowsNetworkEnvironment.h b/Swiften/Network/WindowsNetworkEnvironment.h
new file mode 100644
index 0000000..2b79504
--- /dev/null
+++ b/Swiften/Network/WindowsNetworkEnvironment.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/Network/NetworkEnvironment.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+namespace Swift {
+
+class WindowsNetworkEnvironment : public NetworkEnvironment {
+ class WindowsNetworkInterface : public NetworkInterface {
+ public:
+ typedef boost::shared_ptr<WindowsNetworkInterface> ref;
+
+ public:
+ virtual ~WindowsNetworkInterface() { }
+ virtual std::vector<HostAddress> getAddresses() {
+ return addresses;
+ }
+
+ virtual std::string getName() {
+ return name;
+ }
+
+ virtual bool isLoopback() {
+ return false;
+ }
+
+ public:
+ void addHostAddress(HostAddress address) {
+ addresses.push_back(address);
+ }
+
+ void setName(const std::string& name) {
+ this->name = name;
+ }
+
+ private:
+ std::vector<HostAddress> addresses;
+ InterfaceType type;
+ std::string name;
+ };
+
+public:
+ std::vector<NetworkInterface::ref> getNetworkInterfaces();
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 1b59f6f..9eafcba 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -48,6 +48,19 @@
#include <Swiften/Parser/PayloadParsers/NicknameParserFactory.h>
#include <Swiften/Parser/PayloadParsers/ReplaceParser.h>
#include <Swiften/Parser/PayloadParsers/LastParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleReasonParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h>
+#include <Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h>
+
+#include "JingleIBBTransportMethodPayloadParser.h"
+#include "JingleFileTransferDescriptionParser.h"
using namespace boost;
@@ -91,6 +104,16 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
factories_.push_back(shared_ptr<PayloadParserFactory>(new MUCUserPayloadParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<MUCAdminPayloadParser>("query", "http://jabber.org/protocol/muc#admin")));
factories_.push_back(shared_ptr<PayloadParserFactory>(new NicknameParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleParserFactory(this)));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleReasonParser>("reason", "urn:xmpp:jingle:1")));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleContentPayloadParserFactory(this)));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleIBBTransportMethodPayloadParser>("transport", "urn:xmpp:jingle:transports:ibb:1")));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleS5BTransportMethodPayloadParser>("transport", "urn:xmpp:jingle:transports:s5b:1")));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleFileTransferDescriptionParserFactory(this)));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StreamInitiationFileInfoParser>("file", "http://jabber.org/protocol/si/profile/file-transfer")));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleFileTransferReceivedParser>("received", "urn:xmpp:jingle:apps:file-transfer:3")));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleFileTransferHashParser>("checksum")));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<S5BProxyRequestParser>("query", "http://jabber.org/protocol/bytestreams")));
foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
addFactory(factory.get());
}
diff --git a/Swiften/Parser/PayloadParsers/IBBParser.cpp b/Swiften/Parser/PayloadParsers/IBBParser.cpp
index 2705c75..20a1ce9 100644
--- a/Swiften/Parser/PayloadParsers/IBBParser.cpp
+++ b/Swiften/Parser/PayloadParsers/IBBParser.cpp
@@ -60,7 +60,7 @@ void IBBParser::handleEndElement(const std::string& element, const std::string&)
std::vector<char> data;
for (size_t i = 0; i < currentText.size(); ++i) {
char c = currentText[i];
- if (c >= 48 && c <= 122) {
+ if ((c >= 48 && c <= 122) || c == 47 || c == 43) {
data.push_back(c);
}
}
diff --git a/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp
new file mode 100644
index 0000000..1431b9e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleContentPayloadParser.h"
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Elements/JinglePayload.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+ JingleContentPayloadParser::JingleContentPayloadParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0) {
+
+ }
+
+ void JingleContentPayloadParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+ if (level == 0) {
+ std::string creator = attributes.getAttributeValue("creator").get_value_or("");
+ if (creator == "initiator") {
+ getPayloadInternal()->setCreator(JingleContentPayload::InitiatorCreator);
+ } else if (creator == "responder") {
+ getPayloadInternal()->setCreator(JingleContentPayload::ResponderCreator);
+ } else {
+ getPayloadInternal()->setCreator(JingleContentPayload::UnknownCreator);
+ }
+
+ getPayloadInternal()->setName(attributes.getAttributeValue("name").get_value_or(""));
+ }
+
+ if (level == 1) {
+ PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+ if (payloadParserFactory) {
+ currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+ }
+ }
+
+ if (level >= 1 && currentPayloadParser) {
+ currentPayloadParser->handleStartElement(element, ns, attributes);
+ }
+
+ ++level;
+ }
+
+ void JingleContentPayloadParser::handleEndElement(const std::string& element, const std::string& ns) {
+ --level;
+
+ if (currentPayloadParser) {
+ if (level >= 1) {
+ currentPayloadParser->handleEndElement(element, ns);
+ }
+
+ if (level == 1) {
+ boost::shared_ptr<JingleTransportPayload> transport = boost::dynamic_pointer_cast<JingleTransportPayload>(currentPayloadParser->getPayload());
+ if (transport) {
+ getPayloadInternal()->addTransport(transport);
+ }
+
+ boost::shared_ptr<JingleDescription> description = boost::dynamic_pointer_cast<JingleDescription>(currentPayloadParser->getPayload());
+ if (description) {
+ getPayloadInternal()->addDescription(description);
+ }
+ }
+ }
+ }
+
+ void JingleContentPayloadParser::handleCharacterData(const std::string& data) {
+ if (level > 1 && currentPayloadParser) {
+ currentPayloadParser->handleCharacterData(data);
+ }
+ }
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h
new file mode 100644
index 0000000..a871cc4
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class PayloadParserFactoryCollection;
+
+class JingleContentPayloadParser : public GenericPayloadParser<JingleContentPayload> {
+ public:
+ JingleContentPayloadParser(PayloadParserFactoryCollection* factories);
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ PayloadParserFactoryCollection* factories;
+ int level;
+ boost::shared_ptr<PayloadParser> currentPayloadParser;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h b/Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h
new file mode 100644
index 0000000..6d66e74
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h>
+
+namespace Swift {
+
+ class PayloadParserFactoryCollection;
+
+ class JingleContentPayloadParserFactory : public PayloadParserFactory {
+ public:
+ JingleContentPayloadParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+ }
+
+ virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+ return element == "content" && ns == "urn:xmpp:jingle:1";
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new JingleContentPayloadParser(factories);
+ }
+
+ private:
+ PayloadParserFactoryCollection* factories;
+
+ };
+}
+
+
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp
new file mode 100644
index 0000000..b394115
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleFileTransferDescriptionParser.h"
+
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+JingleFileTransferDescriptionParser::JingleFileTransferDescriptionParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0),
+ currentElement(UnknownElement) {
+
+}
+
+void JingleFileTransferDescriptionParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+ if (level == 0) {
+
+ }
+
+ if (level == 1) {
+ if (element == "offer") {
+ currentElement = OfferElement;
+ } else if (element == "request") {
+ currentElement = RequestElement;
+ } else {
+ currentElement = UnknownElement;
+ }
+ }
+
+ if (level == 2) {
+ PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+ if (payloadParserFactory) {
+ currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+ }
+ }
+
+ if (level >= 2 && currentPayloadParser) {
+ currentPayloadParser->handleStartElement(element, ns, attributes);
+ }
+
+ ++level;
+}
+
+void JingleFileTransferDescriptionParser::handleEndElement(const std::string& element, const std::string& ns) {
+ --level;
+ if (currentPayloadParser) {
+ if (level >= 2) {
+ currentPayloadParser->handleEndElement(element, ns);
+ }
+
+ if (level == 2) {
+ boost::shared_ptr<StreamInitiationFileInfo> info = boost::dynamic_pointer_cast<StreamInitiationFileInfo>(currentPayloadParser->getPayload());
+ if (info) {
+ if (currentElement == OfferElement) {
+ getPayloadInternal()->addOffer(*info);
+ } else if (currentElement == RequestElement) {
+ getPayloadInternal()->addRequest(*info);
+ }
+ }
+ }
+ }
+}
+
+void JingleFileTransferDescriptionParser::handleCharacterData(const std::string& data) {
+ if (level >= 2 && currentPayloadParser) {
+ currentPayloadParser->handleCharacterData(data);
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h
new file mode 100644
index 0000000..93560c6
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+#include <Swiften/Parser/PayloadParser.h>
+
+namespace Swift {
+
+class PayloadParserFactoryCollection;
+
+class JingleFileTransferDescriptionParser : public GenericPayloadParser<JingleFileTransferDescription> {
+ public:
+ JingleFileTransferDescriptionParser(PayloadParserFactoryCollection* factories);
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ enum CurrentParseElement {
+ UnknownElement,
+ RequestElement,
+ OfferElement,
+ };
+
+ PayloadParserFactoryCollection* factories;
+ int level;
+ CurrentParseElement currentElement;
+ boost::shared_ptr<PayloadParser> currentPayloadParser;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h
new file mode 100644
index 0000000..b997c1d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h>
+
+namespace Swift {
+
+ class PayloadParserFactoryCollection;
+
+ class JingleFileTransferDescriptionParserFactory : public PayloadParserFactory {
+ public:
+ JingleFileTransferDescriptionParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+ }
+
+ virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+ return element == "description" && ns == "urn:xmpp:jingle:apps:file-transfer:3";
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new JingleFileTransferDescriptionParser(factories);
+ }
+
+ private:
+ PayloadParserFactoryCollection* factories;
+
+ };
+}
+
+
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp
new file mode 100644
index 0000000..87f8317
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleFileTransferHashParser.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+
+namespace Swift {
+
+JingleFileTransferHashParser::JingleFileTransferHashParser() {
+}
+
+void JingleFileTransferHashParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+ if (element == "hash") {
+ algo = attributes.getAttribute("algo");
+ }
+}
+
+void JingleFileTransferHashParser::handleEndElement(const std::string& element, const std::string& ) {
+ if (element == "hash" && !algo.empty() && !hash.empty()) {
+ getPayloadInternal()->setHash(algo, hash);
+ algo.clear();
+ hash.clear();
+ }
+}
+
+void JingleFileTransferHashParser::handleCharacterData(const std::string& data) {
+ if (!algo.empty()) {
+ std::string new_data(data);
+ boost::trim(new_data);
+ hash += new_data;
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h
new file mode 100644
index 0000000..35e4a05
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleFileTransferHashParser : public GenericPayloadParser<JingleFileTransferHash> {
+public:
+ JingleFileTransferHashParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+private:
+ std::string algo;
+ std::string hash;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp
new file mode 100644
index 0000000..20ad73e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleFileTransferReceivedParser.h"
+#include "StreamInitiationFileInfoParser.h"
+
+#include <boost/shared_ptr.hpp>
+#include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+
+namespace Swift {
+
+JingleFileTransferReceivedParser::JingleFileTransferReceivedParser() : level(0) {
+}
+
+void JingleFileTransferReceivedParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+ if (level == 1 && element == "file") {
+ PayloadParserFactory* payloadParserFactory = new GenericPayloadParserFactory<StreamInitiationFileInfoParser>("file", "http://jabber.org/protocol/si/profile/file-transfer");
+ if (payloadParserFactory) {
+ currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+ }
+ }
+
+ if (currentPayloadParser && level >= 1) {
+ currentPayloadParser->handleStartElement(element, ns, attributes);
+ }
+
+ ++level;
+}
+
+void JingleFileTransferReceivedParser::handleEndElement(const std::string& element, const std::string& ) {
+ --level;
+ if (element == "file") {
+ boost::shared_ptr<StreamInitiationFileInfo> fileInfo = boost::dynamic_pointer_cast<StreamInitiationFileInfo>(currentPayloadParser->getPayload());
+ if (fileInfo) {
+ getPayloadInternal()->setFileInfo(*fileInfo);
+ }
+ }
+}
+
+void JingleFileTransferReceivedParser::handleCharacterData(const std::string& ) {
+
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h
new file mode 100644
index 0000000..824b06d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleFileTransferReceivedParser : public GenericPayloadParser<JingleFileTransferReceived> {
+public:
+ JingleFileTransferReceivedParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+private:
+ boost::shared_ptr<PayloadParser> currentPayloadParser;
+ int level;
+};
+
+} \ No newline at end of file
diff --git a/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp
new file mode 100644
index 0000000..a3dfd12
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
+
+#include "JingleIBBTransportMethodPayloadParser.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+ JingleIBBTransportMethodPayloadParser::JingleIBBTransportMethodPayloadParser() : level(0) {
+
+ }
+
+ void JingleIBBTransportMethodPayloadParser::handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) {
+ try {
+ getPayloadInternal()->setBlockSize(boost::lexical_cast<int>(attributes.getAttributeValue("block-size").get_value_or("0")));
+ } catch (boost::bad_lexical_cast &) {
+ getPayloadInternal()->setBlockSize(0);
+ }
+ getPayloadInternal()->setSessionID(attributes.getAttributeValue("sid").get_value_or(""));
+ ++level;
+ }
+
+ void JingleIBBTransportMethodPayloadParser::handleEndElement(const std::string&, const std::string&) {
+ --level;
+
+
+ }
+
+ void JingleIBBTransportMethodPayloadParser::handleCharacterData(const std::string&) {
+
+ }
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h
new file mode 100644
index 0000000..311cc5b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleIBBTransportMethodPayloadParser : public GenericPayloadParser<JingleIBBTransportPayload> {
+ public:
+ JingleIBBTransportMethodPayloadParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ int level;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleParser.cpp b/Swiften/Parser/PayloadParsers/JingleParser.cpp
new file mode 100644
index 0000000..dd34458
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleParser.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/JingleParser.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Base/Log.h>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace Swift {
+
+ JingleParser::JingleParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0) {
+
+ }
+
+ void JingleParser::handleStartElement(const std::string& element, const std::string &ns, const AttributeMap& attributes) {
+ if (level == 0) {
+ // <jingle > tag
+ JinglePayload::ref payload = getPayloadInternal();
+ payload->setAction(stringToAction(attributes.getAttributeValue("action").get_value_or("")));
+ payload->setInitiator(JID(attributes.getAttributeValue("initiator").get_value_or("")));
+ payload->setResponder(JID(attributes.getAttributeValue("responder").get_value_or("")));
+ payload->setSessionID(attributes.getAttributeValue("sid").get_value_or(""));
+ }
+
+ if (level == 1) {
+ PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+ if (payloadParserFactory) {
+ currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+ }
+ }
+
+ if (level >= 1 && currentPayloadParser) {
+ currentPayloadParser->handleStartElement(element, ns, attributes);
+ }
+
+ ++level;
+ }
+
+ void JingleParser::handleEndElement(const std::string& element, const std::string &ns) {
+ --level;
+ if (currentPayloadParser) {
+ if (level >= 1) {
+ currentPayloadParser->handleEndElement(element, ns);
+ }
+
+ if (level == 1) {
+ boost::shared_ptr<JinglePayload::Reason> reason = boost::dynamic_pointer_cast<JinglePayload::Reason>(currentPayloadParser->getPayload());
+ if (reason) {
+ getPayloadInternal()->setReason(*reason);
+ }
+
+ boost::shared_ptr<JingleContentPayload> payload = boost::dynamic_pointer_cast<JingleContentPayload>(currentPayloadParser->getPayload());
+ if (payload) {
+ getPayloadInternal()->addContent(payload);
+ }
+
+ boost::shared_ptr<JingleFileTransferReceived> received = boost::dynamic_pointer_cast<JingleFileTransferReceived>(currentPayloadParser->getPayload());
+ if (received) {
+ getPayloadInternal()->addPayload(received);
+ }
+
+ boost::shared_ptr<JingleFileTransferHash> hash = boost::dynamic_pointer_cast<JingleFileTransferHash>(currentPayloadParser->getPayload());
+ if (hash) {
+ getPayloadInternal()->addPayload(hash);
+ }
+ }
+ }
+ }
+
+ void JingleParser::handleCharacterData(const std::string& data) {
+ if (level > 1 && currentPayloadParser) {
+ currentPayloadParser->handleCharacterData(data);
+ }
+ }
+
+ JinglePayload::Action JingleParser::stringToAction(const std::string &str) const {
+ if (str == "content-accept") {
+ return JinglePayload::ContentAccept;
+ } else if (str == "content-add") {
+ return JinglePayload::ContentAdd;
+ } else if (str == "content-modify") {
+ return JinglePayload::ContentModify;
+ } else if (str == "content-reject") {
+ return JinglePayload::ContentReject;
+ } else if (str == "content-remove") {
+ return JinglePayload::ContentRemove;
+ } else if (str == "description-info") {
+ return JinglePayload::DescriptionInfo;
+ } else if (str == "security-info") {
+ return JinglePayload::SecurityInfo;
+ } else if (str == "session-accept") {
+ return JinglePayload::SessionAccept;
+ } else if (str == "session-info") {
+ return JinglePayload::SessionInfo;
+ } else if (str == "session-initiate") {
+ return JinglePayload::SessionInitiate;
+ } else if (str == "session-terminate") {
+ return JinglePayload::SessionTerminate;
+ } else if (str == "transport-accept") {
+ return JinglePayload::TransportAccept;
+ } else if (str == "transport-info") {
+ return JinglePayload::TransportInfo;
+ } else if (str == "transport-reject") {
+ return JinglePayload::TransportReject;
+ } else if (str == "transport-replace") {
+ return JinglePayload::TransportReplace;
+ } else {
+ return JinglePayload::UnknownAction;
+ }
+
+ }
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleParser.h b/Swiften/Parser/PayloadParsers/JingleParser.h
new file mode 100644
index 0000000..ecaca3c
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleParser.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+
+namespace Swift {
+
+class JingleParser : public GenericPayloadParser<JinglePayload> {
+ public:
+ JingleParser(PayloadParserFactoryCollection* factories);
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ JinglePayload::Action stringToAction(const std::string &str) const;
+
+ private:
+ PayloadParserFactoryCollection* factories;
+ int level;
+ boost::shared_ptr<PayloadParser> currentPayloadParser;
+};
+
+}; \ No newline at end of file
diff --git a/Swiften/Parser/PayloadParsers/JingleParserFactory.h b/Swiften/Parser/PayloadParsers/JingleParserFactory.h
new file mode 100644
index 0000000..fa25aeb
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleParserFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleParser.h>
+
+namespace Swift {
+
+ class PayloadParserFactoryCollection;
+
+ class JingleParserFactory : public PayloadParserFactory {
+ public:
+ JingleParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+ }
+
+ virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+ return element == "jingle" && ns == "urn:xmpp:jingle:1";
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new JingleParser(factories);
+ }
+
+ private:
+ PayloadParserFactoryCollection* factories;
+
+ };
+}
+
+
diff --git a/Swiften/Parser/PayloadParsers/JingleReasonParser.cpp b/Swiften/Parser/PayloadParsers/JingleReasonParser.cpp
new file mode 100644
index 0000000..3df82ae
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleReasonParser.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleReasonParser.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+ JingleReasonParser::JingleReasonParser() : level(0), parseText(false) {
+
+ }
+
+ void JingleReasonParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap&) {
+ if (level == 1) {
+ if (element == "text") {
+ parseText = true;
+ } else {
+ // reason type
+ getPayloadInternal()->type = stringToReasonType(element);
+ }
+ }
+ ++level;
+ }
+
+ void JingleReasonParser::handleEndElement(const std::string& element, const std::string&) {
+ --level;
+ if (element == "text") {
+ parseText = false;
+ getPayloadInternal()->text = text;
+ }
+ }
+
+ void JingleReasonParser::handleCharacterData(const std::string& data) {
+ if (parseText) {
+ text += data;
+ }
+ }
+
+ JinglePayload::Reason::Type JingleReasonParser::stringToReasonType(const std::string& type) const {
+ if (type == "alternative-session") {
+ return JinglePayload::Reason::AlternativeSession;
+ } else if (type == "busy") {
+ return JinglePayload::Reason::Busy;
+ } else if (type == "cancel") {
+ return JinglePayload::Reason::Cancel;
+ } else if (type == "connectivity-error") {
+ return JinglePayload::Reason::ConnectivityError;
+ } else if (type == "decline") {
+ return JinglePayload::Reason::Decline;
+ } else if (type == "expired") {
+ return JinglePayload::Reason::Expired;
+ } else if (type == "failed-application") {
+ return JinglePayload::Reason::FailedApplication;
+ } else if (type == "failed-transport") {
+ return JinglePayload::Reason::FailedTransport;
+ } else if (type == "general-error") {
+ return JinglePayload::Reason::GeneralError;
+ } else if (type == "gone") {
+ return JinglePayload::Reason::Gone;
+ } else if (type == "incompatible-parameters") {
+ return JinglePayload::Reason::IncompatibleParameters;
+ } else if (type == "media-error") {
+ return JinglePayload::Reason::MediaError;
+ } else if (type == "security-error") {
+ return JinglePayload::Reason::SecurityError;
+ } else if (type == "success") {
+ return JinglePayload::Reason::Success;
+ } else if (type == "timeout") {
+ return JinglePayload::Reason::Timeout;
+ } else if (type == "unsupported-applications") {
+ return JinglePayload::Reason::UnsupportedApplications;
+ } else if (type == "unsupported-transports") {
+ return JinglePayload::Reason::UnsupportedTransports;
+ } else {
+ return JinglePayload::Reason::UnknownType;
+ }
+ }
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleReasonParser.h b/Swiften/Parser/PayloadParsers/JingleReasonParser.h
new file mode 100644
index 0000000..08af31a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleReasonParser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleReasonParser : public GenericPayloadParser<JinglePayload::Reason> {
+ public:
+ JingleReasonParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+ private:
+ JinglePayload::Reason::Type stringToReasonType(const std::string& type) const;
+
+ private:
+ int level;
+ bool parseText;
+ std::string text;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp
new file mode 100644
index 0000000..14a80e6
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
+
+#include "JingleS5BTransportMethodPayloadParser.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+ JingleS5BTransportMethodPayloadParser::JingleS5BTransportMethodPayloadParser() : level(0) {
+
+ }
+
+ void JingleS5BTransportMethodPayloadParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+ if (level == 0) {
+ getPayloadInternal()->setSessionID(attributes.getAttributeValue("sid").get_value_or(""));
+ std::string mode = attributes.getAttributeValue("mode").get_value_or("tcp");
+ if (mode == "tcp") {
+ getPayloadInternal()->setMode(JingleS5BTransportPayload::TCPMode);
+ } else if(mode == "udp") {
+ getPayloadInternal()->setMode(JingleS5BTransportPayload::UDPMode);
+ } else {
+ std::cerr << "Unknown S5B mode; falling back to defaul!" << std::endl;
+ getPayloadInternal()->setMode(JingleS5BTransportPayload::TCPMode);
+ }
+ } else if (level == 1) {
+ if (element == "candidate") {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.cid = attributes.getAttributeValue("cid").get_value_or("");
+
+ int port = -1;
+ try {
+ port = boost::lexical_cast<int>(attributes.getAttributeValue("port").get_value_or("-1"));
+ } catch(boost::bad_lexical_cast &) { }
+ candidate.hostPort = HostAddressPort(HostAddress(attributes.getAttributeValue("host").get_value_or("")), port);
+ candidate.jid = JID(attributes.getAttributeValue("jid").get_value_or(""));
+ int priority = -1;
+ try {
+ priority = boost::lexical_cast<int>(attributes.getAttributeValue("priority").get_value_or("-1"));
+ } catch(boost::bad_lexical_cast &) { }
+ candidate.priority = priority;
+ candidate.type = stringToType(attributes.getAttributeValue("type").get_value_or("direct"));
+
+ getPayloadInternal()->addCandidate(candidate);
+ } else if (element == "candidate-used") {
+ getPayloadInternal()->setCandidateUsed(attributes.getAttributeValue("cid").get_value_or(""));
+ } else if (element == "candidate-error") {
+ getPayloadInternal()->setCandidateError(true);
+ } else if (element == "activated") {
+ getPayloadInternal()->setActivated(attributes.getAttributeValue("cid").get_value_or(""));
+ } else if (element == "proxy-error") {
+ getPayloadInternal()->setProxyError(true);
+ }
+ }
+
+ ++level;
+ }
+
+ void JingleS5BTransportMethodPayloadParser::handleEndElement(const std::string&, const std::string&) {
+ --level;
+
+
+ }
+
+ void JingleS5BTransportMethodPayloadParser::handleCharacterData(const std::string&) {
+
+ }
+
+ JingleS5BTransportPayload::Candidate::Type JingleS5BTransportMethodPayloadParser::stringToType(const std::string &str) const {
+ if (str == "direct") {
+ return JingleS5BTransportPayload::Candidate::DirectType;
+ } else if (str == "assisted") {
+ return JingleS5BTransportPayload::Candidate::AssistedType;
+ } else if (str == "tunnel") {
+ return JingleS5BTransportPayload::Candidate::TunnelType;
+ } else if (str == "proxy") {
+ return JingleS5BTransportPayload::Candidate::ProxyType;
+ } else {
+ std::cerr << "Unknown candidate type; falling back to default!" << std::endl;
+ return JingleS5BTransportPayload::Candidate::DirectType;
+ }
+ }
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h
new file mode 100644
index 0000000..1987d3f
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleS5BTransportMethodPayloadParser : public GenericPayloadParser<JingleS5BTransportPayload> {
+ public:
+ JingleS5BTransportMethodPayloadParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ JingleS5BTransportPayload::Candidate::Type stringToType(const std::string &str) const;
+
+ private:
+ int level;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp
new file mode 100644
index 0000000..6e33f16
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "S5BProxyRequestParser.h"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
+
+namespace Swift {
+
+S5BProxyRequestParser::S5BProxyRequestParser() : parseActivate(false) {
+}
+
+S5BProxyRequestParser::~S5BProxyRequestParser() {
+}
+
+void S5BProxyRequestParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+ if (element == "streamhost") {
+ if (attributes.getAttributeValue("host") && attributes.getAttributeValue("jid") && attributes.getAttributeValue("port")) {
+ HostAddress address = attributes.getAttributeValue("host").get_value_or("");
+ int port = -1;
+ JID jid = attributes.getAttributeValue("jid").get_value_or("");
+
+ try {
+ port = boost::lexical_cast<int>(attributes.getAttributeValue("port").get());
+ } catch (boost::bad_lexical_cast &) {
+ port = -1;
+ }
+ if (address.isValid() && port != -1 && jid.isValid()) {
+ S5BProxyRequest::StreamHost streamHost;
+ streamHost.addressPort = HostAddressPort(address, port);
+ streamHost.jid = jid;
+ getPayloadInternal()->setStreamHost(streamHost);
+ }
+ }
+ } else if (element == "activate") {
+ parseActivate = true;
+ } else if (element == "query") {
+ if (attributes.getAttributeValue("sid")) {
+ getPayloadInternal()->setSID(attributes.getAttributeValue("sid").get());
+ }
+ }
+}
+
+void S5BProxyRequestParser::handleEndElement(const std::string& element, const std::string&) {
+ if (element == "activate") {
+ JID activate = JID(activateJID);
+ if (activate.isValid()) {
+ getPayloadInternal()->setActivate(activate);
+ }
+ parseActivate = false;
+ }
+}
+
+void S5BProxyRequestParser::handleCharacterData(const std::string& data) {
+ if (parseActivate) {
+ activateJID = activateJID + data;
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h
new file mode 100644
index 0000000..0bf1a26
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class S5BProxyRequestParser : public GenericPayloadParser<S5BProxyRequest> {
+public:
+ S5BProxyRequestParser();
+ virtual ~S5BProxyRequestParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+private:
+ bool parseActivate;
+ std::string activateJID;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp
new file mode 100644
index 0000000..0a13844
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "StreamInitiationFileInfoParser.h"
+
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+StreamInitiationFileInfoParser::StreamInitiationFileInfoParser() : level(0), parseDescription(false) {
+
+}
+
+void StreamInitiationFileInfoParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+ if (level == 0) {
+ getPayloadInternal()->setName(attributes.getAttributeValue("name").get_value_or(""));
+ getPayloadInternal()->setHash(attributes.getAttributeValue("hash").get_value_or(""));
+ getPayloadInternal()->setAlgo(attributes.getAttributeValue("algo").get_value_or("md5"));
+ try {
+ getPayloadInternal()->setSize(boost::lexical_cast<boost::uintmax_t>(attributes.getAttributeValue("size").get_value_or("0")));
+ } catch (boost::bad_lexical_cast &) {
+ getPayloadInternal()->setSize(0);
+ }
+ getPayloadInternal()->setDate(stringToDateTime(attributes.getAttributeValue("date").get_value_or("")));
+ } else if (level == 1) {
+ if (element == "desc") {
+ parseDescription = true;
+ } else {
+ parseDescription = false;
+ if (element == "range") {
+ int offset = 0;
+ try {
+ offset = boost::lexical_cast<boost::uintmax_t>(attributes.getAttributeValue("offset").get_value_or("0"));
+ } catch (boost::bad_lexical_cast &) {
+ offset = 0;
+ }
+ if (offset == 0) {
+ getPayloadInternal()->setSupportsRangeRequests(true);
+ } else {
+ getPayloadInternal()->setRangeOffset(offset);
+ }
+ }
+ }
+ }
+ ++level;
+}
+
+void StreamInitiationFileInfoParser::handleEndElement(const std::string& element, const std::string&) {
+ --level;
+ if (parseDescription && element == "desc") {
+ parseDescription = false;
+ getPayloadInternal()->setDescription(desc);
+ }
+}
+
+void StreamInitiationFileInfoParser::handleCharacterData(const std::string& data) {
+ if (parseDescription) {
+ desc += data;
+ }
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h
new file mode 100644
index 0000000..6d3591d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class StreamInitiationFileInfoParser : public GenericPayloadParser<StreamInitiationFileInfo> {
+ public:
+ StreamInitiationFileInfoParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ int level;
+ bool parseDescription;
+ std::string desc;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
index 9ea8089..fd3d019 100644
--- a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
@@ -38,9 +38,9 @@ void StreamInitiationParser::handleStartElement(const std::string& element, cons
if (element == "file") {
inFile = true;
currentFile = StreamInitiationFileInfo();
- currentFile.name = attributes.getAttribute("name");
+ currentFile.setName(attributes.getAttribute("name"));
try {
- currentFile.size = boost::lexical_cast<int>(attributes.getAttribute("size"));
+ currentFile.setSize(boost::lexical_cast<int>(attributes.getAttribute("size")));
}
catch (boost::bad_lexical_cast&) {
}
@@ -83,7 +83,7 @@ void StreamInitiationParser::handleEndElement(const std::string& element, const
}
else if (level == FileOrFeatureLevel) {
if (inFile && element == "desc") {
- currentFile.description = currentText;
+ currentFile.setDescription(currentText);
}
else if (formParser) {
Form::ref form = formParser->getPayloadInternal();
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp
new file mode 100644
index 0000000..d03ba8b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Base/DateTime.h>
+
+#include <Swiften/Base/Log.h>
+
+using namespace Swift;
+
+class JingleParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(JingleParserTest);
+ CPPUNIT_TEST(testParse_Xep0166_Example3);
+ CPPUNIT_TEST(testParse_Xep0166_Example8);
+
+ CPPUNIT_TEST(testParse_Xep0261_Example1);
+ CPPUNIT_TEST(testParse_Xep0261_Example3);
+ CPPUNIT_TEST(testParse_Xep0261_Example9);
+ CPPUNIT_TEST(testParse_Xep0261_Example13);
+
+ CPPUNIT_TEST(testParse_Xep0234_Example1);
+ CPPUNIT_TEST(testParse_Xep0234_Example3);
+ CPPUNIT_TEST(testParse_Xep0234_Example5);
+ CPPUNIT_TEST(testParse_Xep0234_Example8);
+ CPPUNIT_TEST(testParse_Xep0234_Example10);
+ CPPUNIT_TEST(testParse_Xep0234_Example11);
+ CPPUNIT_TEST(testParse_Xep0234_Example12);
+
+ CPPUNIT_TEST(testParse_Xep0260_Example1);
+ CPPUNIT_TEST(testParse_Xep0260_Example3);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ //http://xmpp.org/extensions/xep-0166.html#example-3
+ void testParse_Xep0166_Example3() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-terminate'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <reason>\n"
+ " <success/>\n"
+ " </reason>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionTerminate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::Reason::Success,
+ jingle->getReason().get_value_or(JinglePayload::Reason(JinglePayload::Reason::UnknownType, "")).type);
+ }
+
+ //http://xmpp.org/extensions/xep-0166.html#example-8
+ void testParse_Xep0166_Example8() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-terminate'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <reason>\n"
+ " <success/>\n"
+ " <text>Sorry, gotta go!</text>\n"
+ " </reason>\n"
+ "</jingle>\n"
+ ));
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionTerminate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::Reason::Success,
+ jingle->getReason().get_value_or(JinglePayload::Reason(JinglePayload::Reason::UnknownType, "")).type);
+ CPPUNIT_ASSERT_EQUAL(std::string("Sorry, gotta go!"),
+ jingle->getReason().get_value_or(JinglePayload::Reason(JinglePayload::Reason::UnknownType, "")).text);
+ }
+
+ // IBB Transport Method Examples
+
+ // http://xmpp.org/extensions/xep-0261.html#example-1
+ void testParse_Xep0261_Example1() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-initiate'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <content creator='initiator' name='ex'>\n"
+ " <description xmlns='urn:xmpp:example'/>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:ibb:1'\n"
+ " block-size='4096'\n"
+ " sid='ch3d9s71'/>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ std::vector<JingleContentPayload::ref> payloads = jingle->getContents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payloads.size());
+ JingleContentPayload::ref payload = payloads[0];
+ CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, payload->getCreator());
+ CPPUNIT_ASSERT_EQUAL(std::string("ex"), payload->getName());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payload->getTransports().size());
+
+ JingleIBBTransportPayload::ref transportPaylod = payload->getTransport<JingleIBBTransportPayload>();
+ CPPUNIT_ASSERT(transportPaylod);
+ CPPUNIT_ASSERT_EQUAL(4096, transportPaylod->getBlockSize());
+ CPPUNIT_ASSERT_EQUAL(std::string("ch3d9s71"), transportPaylod->getSessionID());
+ }
+
+ // http://xmpp.org/extensions/xep-0261.html#example-1
+ void testParse_Xep0261_Example3() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-accept'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " responder='juliet@capulet.lit/balcony'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <content creator='initiator' name='ex'>\n"
+ " <description xmlns='urn:xmpp:example'/>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:ibb:1'\n"
+ " block-size='2048'\n"
+ " sid='ch3d9s71'/>\n"
+ " </content>\n"
+ " </jingle>\n"
+ ));
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionAccept, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), jingle->getResponder());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ std::vector<JingleContentPayload::ref> payloads = jingle->getContents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payloads.size());
+ JingleContentPayload::ref payload = payloads[0];
+ CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, payload->getCreator());
+ CPPUNIT_ASSERT_EQUAL(std::string("ex"), payload->getName());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payload->getTransports().size());
+
+ JingleIBBTransportPayload::ref transportPaylod = payload->getTransport<JingleIBBTransportPayload>();
+ CPPUNIT_ASSERT(transportPaylod);
+ CPPUNIT_ASSERT_EQUAL(2048, transportPaylod->getBlockSize());
+ CPPUNIT_ASSERT_EQUAL(std::string("ch3d9s71"), transportPaylod->getSessionID());
+ }
+
+ // http://xmpp.org/extensions/xep-0261.html#example-9
+ void testParse_Xep0261_Example9() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='transport-info'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <content creator='initiator' name='ex'>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:ibb:1'\n"
+ " block-size='2048'\n"
+ " sid='bt8a71h6'/>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::TransportInfo, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ std::vector<JingleContentPayload::ref> payloads = jingle->getContents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payloads.size());
+ JingleContentPayload::ref payload = payloads[0];
+ CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, payload->getCreator());
+ CPPUNIT_ASSERT_EQUAL(std::string("ex"), payload->getName());
+
+ JingleIBBTransportPayload::ref transportPaylod = payload->getTransport<JingleIBBTransportPayload>();
+ CPPUNIT_ASSERT(transportPaylod);
+ CPPUNIT_ASSERT_EQUAL(2048, transportPaylod->getBlockSize());
+ CPPUNIT_ASSERT_EQUAL(std::string("bt8a71h6"), transportPaylod->getSessionID());
+ }
+
+ // http://xmpp.org/extensions/xep-0261.html#example-13
+ void testParse_Xep0261_Example13() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-terminate'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <reason><success/></reason>\n"
+ " </jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionTerminate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::Reason::Success, jingle->getReason().get_value_or(JinglePayload::Reason()).type);
+
+ }
+
+ // Jingle File Transfer Examples
+
+ // http://xmpp.org/extensions/xep-0234.html#example-1
+ void testParse_Xep0234_Example1() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-initiate'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='851ba2'>\n"
+ " <content creator='initiator' name='a-file-offer'>\n"
+ " <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+ " <offer>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " date='1969-07-21T02:56:15Z'\n"
+ " hash='552da749930852c69ae5d2141d3766b1'\n"
+ " name='test.txt'\n"
+ " size='1022'>\n"
+ " <desc>This is a test. If this were a real file...</desc>\n"
+ " <range/>\n"
+ " </file>\n"
+ " </offer>\n"
+ " </description>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " mode='tcp'\n"
+ " sid='vj3hs98y'>\n"
+ " <candidate cid='hft54dqy'\n"
+ " host='192.168.4.1'\n"
+ " jid='romeo@montague.lit/orchard'\n"
+ " port='5086'\n"
+ " priority='8257636'\n"
+ " type='direct'/>\n"
+ " <candidate cid='hutr46fe'\n"
+ " host='24.24.24.1'\n"
+ " jid='romeo@montague.lit/orchard'\n"
+ " port='5087'\n"
+ " priority='8258636'\n"
+ " type='direct'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ " </jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("851ba2"), jingle->getSessionID());
+
+ std::vector<JingleContentPayload::ref> contents = jingle->getContents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), contents.size());
+
+ JingleFileTransferDescription::ref description = contents[0]->getDescription<JingleFileTransferDescription>();
+
+
+ std::vector<StreamInitiationFileInfo> offers = description->getOffers();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), offers.size());
+ CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), offers[0].getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), offers[0].getHash());
+ CPPUNIT_ASSERT(1022 == offers[0].getSize());
+ CPPUNIT_ASSERT_EQUAL(std::string("This is a test. If this were a real file..."), offers[0].getDescription());
+ CPPUNIT_ASSERT_EQUAL(true, offers[0].getSupportsRangeRequests());
+ CPPUNIT_ASSERT(stringToDateTime("1969-07-21T02:56:15Z") == offers[0].getDate());
+ CPPUNIT_ASSERT_EQUAL(std::string("md5"), offers[0].getAlgo());
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-3
+ void testParse_Xep0234_Example3() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-accept'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='851ba2'>\n"
+ " <content creator='initiator' name='a-file-offer'>\n"
+ " <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+ " <offer>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " name='test.txt'\n"
+ " size='1022'\n"
+ " hash='552da749930852c69ae5d2141d3766b1'\n"
+ " date='1969-07-21T02:56:15Z'>\n"
+ " <desc>This is a test. If this were a real file...</desc>\n"
+ " <range/>\n"
+ " </file>\n"
+ " </offer>\n"
+ " </description>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " mode='tcp'\n"
+ " sid='vj3hs98y'>\n"
+ " <candidate cid='ht567dq'\n"
+ " host='192.169.1.10'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='6539'\n"
+ " priority='8257636'\n"
+ " type='direct'/>\n"
+ " <candidate cid='hr65dqyd'\n"
+ " host='134.102.201.180'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='16453'\n"
+ " priority='7929856'\n"
+ " type='assisted'/>\n"
+ " <candidate cid='grt654q2'\n"
+ " host='2001:638:708:30c9:219:d1ff:fea4:a17d'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='6539'\n"
+ " priority='8257606'\n"
+ " type='direct'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionAccept, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("851ba2"), jingle->getSessionID());
+
+ std::vector<JingleContentPayload::ref> contents = jingle->getContents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), contents.size());
+
+ JingleFileTransferDescription::ref description = contents[0]->getDescription<JingleFileTransferDescription>();
+
+
+ std::vector<StreamInitiationFileInfo> offers = description->getOffers();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), offers.size());
+ CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), offers[0].getName());
+ CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), offers[0].getHash());
+ CPPUNIT_ASSERT(1022 == offers[0].getSize());
+ CPPUNIT_ASSERT_EQUAL(std::string("This is a test. If this were a real file..."), offers[0].getDescription());
+ CPPUNIT_ASSERT_EQUAL(true, offers[0].getSupportsRangeRequests());
+ CPPUNIT_ASSERT(stringToDateTime("1969-07-21T02:56:15Z") == offers[0].getDate());
+ CPPUNIT_ASSERT_EQUAL(std::string("md5"), offers[0].getAlgo());
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-5
+ void testParse_Xep0234_Example5() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='transport-info'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <content creator='initiator' name='ex'>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " sid='vj3hs98y'>\n"
+ " <candidate-used cid='hr65dqyd'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::TransportInfo, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ std::vector<JingleContentPayload::ref> contents = jingle->getContents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), contents.size());
+
+ JingleS5BTransportPayload::ref transport = contents[0]->getTransport<JingleS5BTransportPayload>();
+ CPPUNIT_ASSERT(transport);
+
+ CPPUNIT_ASSERT_EQUAL(std::string("vj3hs98y"), transport->getSessionID());
+ CPPUNIT_ASSERT_EQUAL(std::string("hr65dqyd"), transport->getCandidateUsed());
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-8
+ void testParse_Xep0234_Example8() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-info'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <checksum xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+ " <file>\n"
+ " <hashes xmlns='urn:xmpp:hashes:0'>\n"
+ " <hash algo='sha-1'>552da749930852c69ae5d2141d3766b1</hash>\n"
+ " </hashes>\n"
+ " </file>\n"
+ " </checksum>\n"
+ "</jingle>\n"
+ ));
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInfo, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ JingleFileTransferHash::ref hash = jingle->getPayload<JingleFileTransferHash>();
+ CPPUNIT_ASSERT(hash);
+ CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), hash->getHashes().at("sha-1"));
+
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-10
+ void testParse_Xep0234_Example10() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-initiate'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='uj3b2'>\n"
+ " <content creator='initiator' name='a-file-request'>\n"
+ " <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+ " <request>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " hash='552da749930852c69ae5d2141d3766b1'>\n"
+ " <range offset='270336'/>\n"
+ " </file>\n"
+ " </request>\n"
+ " </description>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " mode='tcp'\n"
+ " sid='xig361fj'>\n"
+ " <candidate cid='ht567dq'\n"
+ " host='192.169.1.10'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='6539'\n"
+ " priority='8257636'\n"
+ " type='direct'/>\n"
+ " <candidate cid='hr65dqyd'\n"
+ " host='134.102.201.180'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='16453'\n"
+ " priority='7929856'\n"
+ " type='assisted'/>\n"
+ " <candidate cid='grt654q2'\n"
+ " host='2001:638:708:30c9:219:d1ff:fea4:a17d'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='6539'\n"
+ " priority='8257606'\n"
+ " type='direct'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("uj3b2"), jingle->getSessionID());
+
+ JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+ CPPUNIT_ASSERT(content);
+
+ StreamInitiationFileInfo file = content->getDescription<JingleFileTransferDescription>()->getRequests()[0];
+ CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), file.getHash());
+ CPPUNIT_ASSERT_EQUAL(270336, file.getRangeOffset());
+ CPPUNIT_ASSERT_EQUAL(true, file.getSupportsRangeRequests());
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-11
+ void testParse_Xep0234_Example11() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-initiate'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='h2va419i'>\n"
+ " <content creator='initiator' name='a-file-offer'>\n"
+ " <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+ " <offer>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " date='2011-06-01T15:58:15Z'\n"
+ " hash='a749930852c69ae5d2141d3766b1552d'\n"
+ " name='somefile.txt'\n"
+ " size='1234'/>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " date='2011-06-01T15:58:15Z'\n"
+ " hash='930852c69ae5d2141d3766b1552da749'\n"
+ " name='anotherfile.txt'\n"
+ " size='2345'/>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " date='2011-06-01T15:58:15Z'\n"
+ " hash='52c69ae5d2141d3766b1552da7499308'\n"
+ " name='yetanotherfile.txt'\n"
+ " size='3456'/>\n"
+ " </offer>\n"
+ " </description>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " mode='tcp'\n"
+ " sid='vj3hs98y'>\n"
+ " <candidate cid='hft54dqy'\n"
+ " host='192.168.4.1'\n"
+ " jid='romeo@montague.lit/orchard'\n"
+ " port='5086'\n"
+ " priority='8257636'\n"
+ " type='direct'/>\n"
+ " <candidate cid='hutr46fe'\n"
+ " host='24.24.24.1'\n"
+ " jid='romeo@montague.lit/orchard'\n"
+ " port='5087'\n"
+ " priority='8258636'\n"
+ " type='direct'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("h2va419i"), jingle->getSessionID());
+
+ JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+ CPPUNIT_ASSERT(content);
+ CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, content->getCreator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a-file-offer"), content->getName());
+
+ std::vector<StreamInitiationFileInfo> offers = content->getDescription<JingleFileTransferDescription>()->getOffers();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), offers.size());
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-12
+ void testParse_Xep0234_Example12() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-info'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <received xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+ " <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+ " hash='a749930852c69ae5d2141d3766b1552d'/>\n"
+ " </received>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInfo, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ boost::shared_ptr<JingleFileTransferReceived> received = jingle->getPayload<JingleFileTransferReceived>();
+ CPPUNIT_ASSERT(received);
+ CPPUNIT_ASSERT_EQUAL(std::string("a749930852c69ae5d2141d3766b1552d"), received->getFileInfo().getHash());
+ }
+
+ // http://xmpp.org/extensions/xep-0260.html#example-1
+ void testParse_Xep0260_Example1() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-initiate'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <content creator='initiator' name='ex'>\n"
+ " <description xmlns='urn:xmpp:example'/>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " mode='tcp'\n"
+ " sid='vj3hs98y'>\n"
+ " <candidate cid='hft54dqy'\n"
+ " host='192.168.4.1'\n"
+ " jid='romeo@montague.lit/orchard'\n"
+ " port='5086'\n"
+ " priority='8257636'\n"
+ " type='direct'/>\n"
+ " <candidate cid='hutr46fe'\n"
+ " host='24.24.24.1'\n"
+ " jid='romeo@montague.lit/orchard'\n"
+ " port='5087'\n"
+ " priority='8258636'\n"
+ " type='direct'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+ CPPUNIT_ASSERT(content);
+
+ JingleS5BTransportPayload::ref s5bPayload = content->getTransport<JingleS5BTransportPayload>();
+ CPPUNIT_ASSERT(s5bPayload);
+
+ CPPUNIT_ASSERT_EQUAL(std::string("vj3hs98y"), s5bPayload->getSessionID());
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::TCPMode, s5bPayload->getMode());
+ CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasCandidateError());
+ CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasProxyError());
+ CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getActivated());
+ CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getCandidateUsed());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), s5bPayload->getCandidates().size());
+
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate = s5bPayload->getCandidates()[0];
+ CPPUNIT_ASSERT_EQUAL(std::string("hft54dqy"), candidate.cid);
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), candidate.jid);
+ CPPUNIT_ASSERT(HostAddressPort(HostAddress("192.168.4.1"), 5086) == candidate.hostPort);
+ CPPUNIT_ASSERT_EQUAL(8257636, candidate.priority);
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+
+ candidate = s5bPayload->getCandidates()[1];
+ CPPUNIT_ASSERT_EQUAL(std::string("hutr46fe"), candidate.cid);
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), candidate.jid);
+ CPPUNIT_ASSERT(HostAddressPort(HostAddress("24.24.24.1"), 5087) == candidate.hostPort);
+ CPPUNIT_ASSERT_EQUAL(8258636, candidate.priority);
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+ }
+
+ // http://xmpp.org/extensions/xep-0260.html#example-3
+ void testParse_Xep0260_Example3() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<jingle xmlns='urn:xmpp:jingle:1'\n"
+ " action='session-accept'\n"
+ " initiator='romeo@montague.lit/orchard'\n"
+ " sid='a73sjjvkla37jfea'>\n"
+ " <content creator='initiator' name='ex'>\n"
+ " <description xmlns='urn:xmpp:example'/>\n"
+ " <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+ " mode='tcp'\n"
+ " sid='vj3hs98y'>\n"
+ " <candidate cid='ht567dq'\n"
+ " host='192.169.1.10'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='6539'\n"
+ " priority='8257636'\n"
+ " type='direct'/>\n"
+ " <candidate cid='hr65dqyd'\n"
+ " host='134.102.201.180'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='16453'\n"
+ " priority='7929856'\n"
+ " type='assisted'/>\n"
+ " <candidate cid='grt654q2'\n"
+ " host='2001:638:708:30c9:219:d1ff:fea4:a17d'\n"
+ " jid='juliet@capulet.lit/balcony'\n"
+ " port='6539'\n"
+ " priority='8257606'\n"
+ " type='direct'/>\n"
+ " </transport>\n"
+ " </content>\n"
+ "</jingle>\n"
+ ));
+
+ JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+ CPPUNIT_ASSERT(jingle);
+ CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionAccept, jingle->getAction());
+ CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+ CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+
+ JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+ CPPUNIT_ASSERT(content);
+
+ JingleS5BTransportPayload::ref s5bPayload = content->getTransport<JingleS5BTransportPayload>();
+ CPPUNIT_ASSERT(s5bPayload);
+
+ CPPUNIT_ASSERT_EQUAL(std::string("vj3hs98y"), s5bPayload->getSessionID());
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::TCPMode, s5bPayload->getMode());
+ CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasCandidateError());
+ CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasProxyError());
+ CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getActivated());
+ CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getCandidateUsed());
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), s5bPayload->getCandidates().size());
+
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate = s5bPayload->getCandidates()[0];
+ CPPUNIT_ASSERT_EQUAL(std::string("ht567dq"), candidate.cid);
+ CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), candidate.jid);
+ CPPUNIT_ASSERT(HostAddressPort(HostAddress("192.169.1.10"), 6539) == candidate.hostPort);
+ CPPUNIT_ASSERT_EQUAL(8257636, candidate.priority);
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+
+ candidate = s5bPayload->getCandidates()[1];
+ CPPUNIT_ASSERT_EQUAL(std::string("hr65dqyd"), candidate.cid);
+ CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), candidate.jid);
+ CPPUNIT_ASSERT(HostAddressPort(HostAddress("134.102.201.180"), 16453) == candidate.hostPort);
+ CPPUNIT_ASSERT_EQUAL(7929856, candidate.priority);
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::AssistedType, candidate.type);
+
+ candidate = s5bPayload->getCandidates()[2];
+ CPPUNIT_ASSERT_EQUAL(std::string("grt654q2"), candidate.cid);
+ CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), candidate.jid);
+ CPPUNIT_ASSERT(HostAddressPort(HostAddress("2001:638:708:30c9:219:d1ff:fea4:a17d"), 6539) == candidate.hostPort);
+ CPPUNIT_ASSERT_EQUAL(8257606, candidate.priority);
+ CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(JingleParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
index 47b2816..63a6c9b 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
@@ -42,9 +42,9 @@ class StreamInitiationParserTest : public CppUnit::TestFixture {
StreamInitiation::ref si = parser.getPayload<StreamInitiation>();
CPPUNIT_ASSERT(si->getIsFileTransfer());
CPPUNIT_ASSERT(si->getFileInfo());
- CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), si->getFileInfo()->name);
- CPPUNIT_ASSERT_EQUAL(1022, si->getFileInfo()->size);
- CPPUNIT_ASSERT_EQUAL(std::string("This is info about the file."), si->getFileInfo()->description);
+ CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), si->getFileInfo()->getName());
+ CPPUNIT_ASSERT(1022 == si->getFileInfo()->getSize());
+ CPPUNIT_ASSERT_EQUAL(std::string("This is info about the file."), si->getFileInfo()->getDescription());
CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(si->getProvidedMethods().size()));
CPPUNIT_ASSERT_EQUAL(std::string("http://jabber.org/protocol/bytestreams"), si->getProvidedMethods()[0]);
CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:oob"), si->getProvidedMethods()[1]);
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index e6c16d1..3dbfbee 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -29,6 +29,15 @@ sources = [
"PayloadParsers/ErrorParser.cpp",
"PayloadParsers/FormParser.cpp",
"PayloadParsers/IBBParser.cpp",
+ "PayloadParsers/JingleParser.cpp",
+ "PayloadParsers/JingleReasonParser.cpp",
+ "PayloadParsers/JingleContentPayloadParser.cpp",
+ "PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp",
+ "PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp",
+ "PayloadParsers/JingleFileTransferDescriptionParser.cpp",
+ "PayloadParsers/JingleFileTransferReceivedParser.cpp",
+ "PayloadParsers/JingleFileTransferHashParser.cpp",
+ "PayloadParsers/StreamInitiationFileInfoParser.cpp",
"PayloadParsers/CommandParser.cpp",
"PayloadParsers/InBandRegistrationPayloadParser.cpp",
"PayloadParsers/SearchPayloadParser.cpp",
@@ -56,6 +65,7 @@ sources = [
"PayloadParsers/NicknameParser.cpp",
"PayloadParsers/ReplaceParser.cpp",
"PayloadParsers/LastParser.cpp",
+ "PayloadParsers/S5BProxyRequestParser.cpp",
"PlatformXMLParserFactory.cpp",
"PresenceParser.cpp",
"SerializingParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index e20e5e6..ca8dee5 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -6,7 +6,7 @@ Import("env")
# Flags
################################################################################
-swiften_dep_modules = ["BOOST", "GCONF", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI"]
+swiften_dep_modules = ["BOOST", "GCONF", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI", "LIBMINIUPNPC", "LIBNATPMP"]
if env["SCONS_STAGE"] == "flags" :
env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
@@ -21,6 +21,10 @@ if env["SCONS_STAGE"] == "flags" :
env["SWIFTEN_LIBRARY"] = "Swiften"
env["SWIFTEN_LIBRARY_FILE"] = "Swiften"
env["SWIFTEN_LIBRARY_ALIASES"] = []
+
+ if env["PLATFORM"] == "win32" :
+ env.Append(CCFLAGS = ["-DSTATICLIB"])
+
if ARGUMENTS.get("swiften_dll", False) :
if env["PLATFORM"] == "win32" :
pass
@@ -87,6 +91,7 @@ if env["SCONS_STAGE"] == "build" :
"Client/NickManager.cpp",
"Client/NickManagerImpl.cpp",
"Client/Storages.cpp",
+ "Client/XMLBeautifier.cpp",
"Compress/ZLibCodecompressor.cpp",
"Compress/ZLibDecompressor.cpp",
"Compress/ZLibCompressor.cpp",
@@ -166,6 +171,14 @@ if env["SCONS_STAGE"] == "build" :
"Serializer/PayloadSerializers/SearchPayloadSerializer.cpp",
"Serializer/PayloadSerializers/FormSerializer.cpp",
"Serializer/PayloadSerializers/NicknameSerializer.cpp",
+ "Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp",
+ "Serializer/PayloadSerializers/JinglePayloadSerializer.cpp",
+ "Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp",
+ "Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp",
+ "Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp",
+ "Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp",
+ "Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp",
+ "Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp",
"Serializer/PresenceSerializer.cpp",
"Serializer/StanzaSerializer.cpp",
"Serializer/StreamErrorSerializer.cpp",
@@ -281,6 +294,7 @@ if env["SCONS_STAGE"] == "build" :
File("Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/IBBParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/JingleParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"),
@@ -340,6 +354,7 @@ if env["SCONS_STAGE"] == "build" :
File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp"),
File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),
File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"),
File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 0ddd445..55c39c7 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -49,6 +49,16 @@
#include <Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h>
#include <Swiften/Serializer/PayloadSerializers/LastSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h>
+
namespace Swift {
FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
@@ -92,6 +102,17 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
serializers_.push_back(new SearchPayloadSerializer());
serializers_.push_back(new ReplaceSerializer());
serializers_.push_back(new LastSerializer());
+
+ serializers_.push_back(new StreamInitiationFileInfoSerializer());
+ serializers_.push_back(new JingleContentPayloadSerializer());
+ serializers_.push_back(new JingleFileTransferDescriptionSerializer());
+ serializers_.push_back(new JingleFileTransferHashSerializer());
+ serializers_.push_back(new JingleFileTransferReceivedSerializer());
+ serializers_.push_back(new JingleIBBTransportPayloadSerializer());
+ serializers_.push_back(new JingleS5BTransportPayloadSerializer());
+ serializers_.push_back(new JinglePayloadSerializer(this));
+ serializers_.push_back(new S5BProxyRequestSerializer());
+
foreach(PayloadSerializer* serializer, serializers_) {
addSerializer(serializer);
}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp
new file mode 100644
index 0000000..90bd940
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+
+#include <Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h>
+
+#include "Swiften/FileTransfer/JingleTransport.h"
+
+namespace Swift {
+
+JingleContentPayloadSerializer::JingleContentPayloadSerializer() {
+}
+
+std::string JingleContentPayloadSerializer::serializePayload(boost::shared_ptr<JingleContentPayload> payload) const {
+ XMLElement payloadXML("content");
+ payloadXML.setAttribute("creator", creatorToString(payload->getCreator()));
+ payloadXML.setAttribute("name", payload->getName());
+
+ if (!payload->getDescriptions().empty()) {
+ // JingleFileTransferDescription
+ JingleFileTransferDescriptionSerializer ftSerializer;
+ JingleFileTransferDescription::ref filetransfer;
+
+ foreach(JingleDescription::ref desc, payload->getDescriptions()) {
+ if ((filetransfer = boost::dynamic_pointer_cast<JingleFileTransferDescription>(desc))) {
+ payloadXML.addNode(boost::make_shared<XMLRawTextNode>(ftSerializer.serializePayload(filetransfer)));
+ }
+ }
+ }
+
+ if (!payload->getTransports().empty()) {
+ // JingleIBBTransportPayload
+ JingleIBBTransportPayloadSerializer ibbSerializer;
+ JingleIBBTransportPayload::ref ibb;
+
+ // JingleS5BTransportPayload
+ JingleS5BTransportPayloadSerializer s5bSerializer;
+ JingleS5BTransportPayload::ref s5b;
+
+ foreach(JingleTransportPayload::ref transport, payload->getTransports()) {
+ if ((ibb = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport))) {
+ payloadXML.addNode(boost::make_shared<XMLRawTextNode>(ibbSerializer.serializePayload(ibb)));
+ } else if ((s5b = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport))) {
+ payloadXML.addNode(boost::make_shared<XMLRawTextNode>(s5bSerializer.serializePayload(s5b)));
+ }
+ }
+ }
+ return payloadXML.serialize();
+}
+
+std::string JingleContentPayloadSerializer::creatorToString(JingleContentPayload::Creator creator) const {
+ switch(creator) {
+ case JingleContentPayload::InitiatorCreator:
+ return "initiator";
+ case JingleContentPayload::ResponderCreator:
+ return "responder";
+ case JingleContentPayload::UnknownCreator:
+ std::cerr << "Serializing unknown creator value." << std::endl;
+ return "ERROR ERROR ERROR";
+ }
+ assert(false);
+}
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h
new file mode 100644
index 0000000..2de0064
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class JingleContentPayloadSerializer : public GenericPayloadSerializer<JingleContentPayload> {
+ public:
+ JingleContentPayloadSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<JingleContentPayload>) const;
+
+ private:
+ std::string creatorToString(JingleContentPayload::Creator creator) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp
new file mode 100644
index 0000000..16337ff
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+
+namespace Swift {
+
+JingleFileTransferDescriptionSerializer::JingleFileTransferDescriptionSerializer() {
+}
+
+std::string JingleFileTransferDescriptionSerializer::serializePayload(boost::shared_ptr<JingleFileTransferDescription> payload) const {
+ XMLElement description("description", "urn:xmpp:jingle:apps:file-transfer:3");
+ StreamInitiationFileInfoSerializer fileInfoSerializer;
+ if (!payload->getOffers().empty()) {
+ boost::shared_ptr<XMLElement> offers = boost::make_shared<XMLElement>("offer");
+ foreach(const StreamInitiationFileInfo &fileInfo, payload->getOffers()) {
+ boost::shared_ptr<XMLRawTextNode> fileInfoXML = boost::make_shared<XMLRawTextNode>(fileInfoSerializer.serialize(boost::make_shared<StreamInitiationFileInfo>(fileInfo)));
+ offers->addNode(fileInfoXML);
+ }
+ description.addNode(offers);
+ }
+ if (!payload->getRequests().empty()) {
+ boost::shared_ptr<XMLElement> requests = boost::make_shared<XMLElement>("request");
+ foreach(const StreamInitiationFileInfo &fileInfo, payload->getRequests()) {
+ boost::shared_ptr<XMLRawTextNode> fileInfoXML = boost::make_shared<XMLRawTextNode>(fileInfoSerializer.serialize(boost::make_shared<StreamInitiationFileInfo>(fileInfo)));
+ requests->addNode(fileInfoXML);
+ }
+ description.addNode(requests);
+ }
+ return description.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h
new file mode 100644
index 0000000..5131435
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+
+
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class JingleFileTransferDescriptionSerializer : public GenericPayloadSerializer<JingleFileTransferDescription> {
+ public:
+ JingleFileTransferDescriptionSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<JingleFileTransferDescription>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp
new file mode 100644
index 0000000..2bd3afa
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h>
+
+#include <string>
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+
+namespace Swift {
+
+JingleFileTransferHashSerializer::JingleFileTransferHashSerializer() {
+}
+
+std::string JingleFileTransferHashSerializer::serializePayload(boost::shared_ptr<JingleFileTransferHash> payload) const {
+ // code for version urn:xmpp:jingle:apps:file-transfer:2
+ //XMLElement hash("hash", "urn:xmpp:jingle:apps:file-transfer:info:2", payload->getHash());
+
+ // code for version urn:xmpp:jingle:apps:file-transfer:3
+ XMLElement checksum("checksum", "urn:xmpp:jingle:apps:file-transfer:3");
+ boost::shared_ptr<XMLElement> file = boost::make_shared<XMLElement>("file");
+ checksum.addNode(file);
+ boost::shared_ptr<XMLElement> hashes = boost::make_shared<XMLElement>("hashes", "urn:xmpp:hashes:0");
+ file->addNode(hashes);
+ foreach(const JingleFileTransferHash::HashesMap::value_type& pair, payload->getHashes()) {
+ boost::shared_ptr<XMLElement> hash = boost::make_shared<XMLElement>("hash", "", pair.second);
+ hash->setAttribute("algo", pair.first);
+ hashes->addNode(hash);
+ }
+
+ return checksum.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h
new file mode 100644
index 0000000..7fa6ac5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class JingleFileTransferHashSerializer : public GenericPayloadSerializer<JingleFileTransferHash> {
+ public:
+ JingleFileTransferHashSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<JingleFileTransferHash>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp
new file mode 100644
index 0000000..40be70e
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+namespace Swift {
+
+JingleFileTransferReceivedSerializer::JingleFileTransferReceivedSerializer() {
+}
+
+std::string JingleFileTransferReceivedSerializer::serializePayload(boost::shared_ptr<JingleFileTransferReceived> payload) const {
+ XMLElement receivedElement("received", "urn:xmpp:jingle:apps:file-transfer:3");
+ XMLElement::ref fileElement = boost::make_shared<XMLElement>("file", "http://jabber.org/protocol/si/profile/file-transfer");
+ fileElement->setAttribute("hash", payload->getFileInfo().getHash());
+ if (payload->getFileInfo().getAlgo() != "md5") {
+ fileElement->setAttribute("algo", payload->getFileInfo().getAlgo());
+ }
+ receivedElement.addNode(fileElement);
+ return receivedElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h
new file mode 100644
index 0000000..4151dd0
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class JingleFileTransferReceivedSerializer : public GenericPayloadSerializer<JingleFileTransferReceived> {
+ public:
+ JingleFileTransferReceivedSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<JingleFileTransferReceived>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp
new file mode 100644
index 0000000..029a5b4
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+namespace Swift {
+
+JingleIBBTransportPayloadSerializer::JingleIBBTransportPayloadSerializer() {
+}
+
+std::string JingleIBBTransportPayloadSerializer::serializePayload(boost::shared_ptr<JingleIBBTransportPayload> payload) const {
+ XMLElement payloadXML("transport", "urn:xmpp:jingle:transports:ibb:1");
+ payloadXML.setAttribute("block-size", boost::lexical_cast<std::string>(payload->getBlockSize()));
+ payloadXML.setAttribute("sid", payload->getSessionID());
+
+ return payloadXML.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h
new file mode 100644
index 0000000..ac9cba9
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+
+
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class JingleIBBTransportPayloadSerializer : public GenericPayloadSerializer<JingleIBBTransportPayload> {
+ public:
+ JingleIBBTransportPayloadSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<JingleIBBTransportPayload>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp
new file mode 100644
index 0000000..25d35ff
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h>
+
+#include <Swiften/Serializer/PayloadSerializerCollection.h>
+
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+
+namespace Swift {
+
+JinglePayloadSerializer::JinglePayloadSerializer(PayloadSerializerCollection* serializers) : serializers(serializers) {
+}
+
+std::string JinglePayloadSerializer::serializePayload(boost::shared_ptr<JinglePayload> payload) const {
+ XMLElement jinglePayload("jingle", "urn:xmpp:jingle:1");
+ jinglePayload.setAttribute("action", actionToString(payload->getAction()));
+ jinglePayload.setAttribute("initiator", payload->getInitiator());
+ jinglePayload.setAttribute("sid", payload->getSessionID());
+
+ if (!payload->getPayloads().empty()) {
+ foreach(boost::shared_ptr<Payload> subPayload, payload->getPayloads()) {
+ PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload);
+ if (serializer) {
+ jinglePayload.addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(serializer->serialize(subPayload))));
+ }
+ }
+ }
+
+ if (payload->getReason().is_initialized()) {
+ boost::shared_ptr<XMLElement> reason = boost::make_shared<XMLElement>("reason");
+ reason->addNode(boost::make_shared<XMLElement>(reasonTypeToString(payload->getReason()->type)));
+ if (!payload->getReason()->text.empty()) {
+ reason->addNode(boost::make_shared<XMLElement>("desc", "", payload->getReason()->text));
+ }
+ jinglePayload.addNode(reason);
+ }
+
+ return jinglePayload.serialize();
+}
+
+std::string JinglePayloadSerializer::actionToString(JinglePayload::Action action) const {
+ switch(action) {
+ case JinglePayload::ContentAccept:
+ return "content-accept";
+ case JinglePayload::ContentAdd:
+ return "content-add";
+ case JinglePayload::ContentModify:
+ return "content-modify";
+ case JinglePayload::ContentReject:
+ return "content-reject";
+ case JinglePayload::ContentRemove:
+ return "content-remove";
+ case JinglePayload::DescriptionInfo:
+ return "description-info";
+ case JinglePayload::SecurityInfo:
+ return "security-info";
+ case JinglePayload::SessionAccept:
+ return "session-accept";
+ case JinglePayload::SessionInfo:
+ return "session-info";
+ case JinglePayload::SessionInitiate:
+ return "session-initiate";
+ case JinglePayload::SessionTerminate:
+ return "session-terminate";
+ case JinglePayload::TransportAccept:
+ return "transport-accept";
+ case JinglePayload::TransportInfo:
+ return "transport-info";
+ case JinglePayload::TransportReject:
+ return "transport-reject";
+ case JinglePayload::TransportReplace:
+ return "transport-replace";
+ case JinglePayload::UnknownAction:
+ std::cerr << "Serializing unknown action value." << std::endl;
+ return "";
+ }
+ assert(false);
+}
+
+std::string JinglePayloadSerializer::reasonTypeToString(JinglePayload::Reason::Type type) const {
+ switch(type) {
+ case JinglePayload::Reason::UnknownType:
+ std::cerr << "Unknown jingle reason type!" << std::endl;
+ return "";
+ case JinglePayload::Reason::AlternativeSession:
+ return "alternative-session";
+ case JinglePayload::Reason::Busy:
+ return "busy";
+ case JinglePayload::Reason::Cancel:
+ return "cancel";
+ case JinglePayload::Reason::ConnectivityError:
+ return "connectivity-error";
+ case JinglePayload::Reason::Decline:
+ return "decline";
+ case JinglePayload::Reason::Expired:
+ return "expired";
+ case JinglePayload::Reason::FailedApplication:
+ return "failed-application";
+ case JinglePayload::Reason::FailedTransport:
+ return "failed-transport";
+ case JinglePayload::Reason::GeneralError:
+ return "general-error";
+ case JinglePayload::Reason::Gone:
+ return "gone";
+ case JinglePayload::Reason::IncompatibleParameters:
+ return "incompatible-parameters";
+ case JinglePayload::Reason::MediaError:
+ return "media-error";
+ case JinglePayload::Reason::SecurityError:
+ return "security-error";
+ case JinglePayload::Reason::Success:
+ return "success";
+ case JinglePayload::Reason::Timeout:
+ return "timeout";
+ case JinglePayload::Reason::UnsupportedApplications:
+ return "unsupported-applications";
+ case JinglePayload::Reason::UnsupportedTransports:
+ return "unsupported-transports";
+ }
+ assert(false);
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h
new file mode 100644
index 0000000..ccdb6d0
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JinglePayload.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class JinglePayloadSerializer : public GenericPayloadSerializer<JinglePayload> {
+ public:
+ JinglePayloadSerializer(PayloadSerializerCollection*);
+
+ virtual std::string serializePayload(boost::shared_ptr<JinglePayload>) const;
+
+ private:
+ std::string actionToString(JinglePayload::Action action) const;
+ std::string reasonTypeToString(JinglePayload::Reason::Type type) const;
+
+ private:
+ PayloadSerializerCollection* serializers;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp
new file mode 100644
index 0000000..c5b40d5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+JingleS5BTransportPayloadSerializer::JingleS5BTransportPayloadSerializer() {
+}
+
+std::string JingleS5BTransportPayloadSerializer::serializePayload(boost::shared_ptr<JingleS5BTransportPayload> payload) const {
+ XMLElement payloadXML("transport", "urn:xmpp:jingle:transports:s5b:1");
+ payloadXML.setAttribute("sid", payload->getSessionID());
+ payloadXML.setAttribute("mode", modeToString(payload->getMode()));
+
+ foreach(JingleS5BTransportPayload::Candidate candidate, payload->getCandidates()) {
+ boost::shared_ptr<XMLElement> candidateXML = boost::make_shared<XMLElement>("candidate");
+ candidateXML->setAttribute("cid", candidate.cid);
+ candidateXML->setAttribute("host", candidate.hostPort.getAddress().toString());
+ candidateXML->setAttribute("jid", candidate.jid.toString());
+ candidateXML->setAttribute("port", boost::lexical_cast<std::string>(candidate.hostPort.getPort()));
+ candidateXML->setAttribute("priority", boost::lexical_cast<std::string>(candidate.priority));
+ candidateXML->setAttribute("type", typeToString(candidate.type));
+ payloadXML.addNode(candidateXML);
+ }
+
+ if (payload->hasCandidateError()) {
+ payloadXML.addNode(boost::make_shared<XMLElement>("candidate-error"));
+ }
+ if (payload->hasProxyError()) {
+ payloadXML.addNode(boost::make_shared<XMLElement>("proxy-error"));
+ }
+
+ if (!payload->getActivated().empty()) {
+ boost::shared_ptr<XMLElement> activatedXML = boost::make_shared<XMLElement>("activated");
+ activatedXML->setAttribute("cid", payload->getActivated());
+ payloadXML.addNode(activatedXML);
+ }
+ if (!payload->getCandidateUsed().empty()) {
+ boost::shared_ptr<XMLElement> candusedXML = boost::make_shared<XMLElement>("candidate-used");
+ candusedXML->setAttribute("cid", payload->getCandidateUsed());
+ payloadXML.addNode(candusedXML);
+ }
+
+ return payloadXML.serialize();
+}
+
+std::string JingleS5BTransportPayloadSerializer::modeToString(JingleS5BTransportPayload::Mode mode) const {
+ switch(mode) {
+ case JingleS5BTransportPayload::TCPMode:
+ return "tcp";
+ case JingleS5BTransportPayload::UDPMode:
+ return "udp";
+ }
+ assert(false);
+}
+
+std::string JingleS5BTransportPayloadSerializer::typeToString(JingleS5BTransportPayload::Candidate::Type type) const {
+ switch(type) {
+ case JingleS5BTransportPayload::Candidate::AssistedType:
+ return "assisted";
+ case JingleS5BTransportPayload::Candidate::DirectType:
+ return "direct";
+ case JingleS5BTransportPayload::Candidate::ProxyType:
+ return "proxy";
+ case JingleS5BTransportPayload::Candidate::TunnelType:
+ return "tunnel";
+ }
+ assert(false);
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h
new file mode 100644
index 0000000..210688d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+ class XMLElement;
+
+ class JingleS5BTransportPayloadSerializer : public GenericPayloadSerializer<JingleS5BTransportPayload> {
+ public:
+ JingleS5BTransportPayloadSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<JingleS5BTransportPayload>) const;
+
+ private:
+ std::string modeToString(JingleS5BTransportPayload::Mode) const;
+ std::string typeToString(JingleS5BTransportPayload::Candidate::Type) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h b/Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h
new file mode 100644
index 0000000..b523588
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <boost/lexical_cast.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class S5BProxyRequestSerializer : public GenericPayloadSerializer<S5BProxyRequest> {
+ public:
+ virtual std::string serializePayload(boost::shared_ptr<S5BProxyRequest> s5bProxyRequest) const {
+ XMLElement queryElement("query", "http://jabber.org/protocol/bytestreams");
+ if (s5bProxyRequest && s5bProxyRequest->getStreamHost()) {
+ boost::shared_ptr<XMLElement> streamHost = boost::make_shared<XMLElement>("streamhost");
+ streamHost->setAttribute("host", s5bProxyRequest->getStreamHost().get().addressPort.getAddress().toString());
+ streamHost->setAttribute("port", boost::lexical_cast<std::string>(s5bProxyRequest->getStreamHost().get().addressPort.getPort()));
+ streamHost->setAttribute("jid", s5bProxyRequest->getStreamHost().get().jid.toString());
+ queryElement.addNode(streamHost);
+ } else if (s5bProxyRequest && s5bProxyRequest->getActivate()) {
+ queryElement.setAttribute("sid", s5bProxyRequest->getSID());
+ boost::shared_ptr<XMLElement> activate = boost::make_shared<XMLElement>("activate", "", s5bProxyRequest->getActivate().get().toString());
+ queryElement.addNode(activate);
+ }
+ return queryElement.serialize();
+ }
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp
new file mode 100644
index 0000000..7b0cad8
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+#include <Swiften/Serializer/XML/XMLTextNode.h>
+
+
+
+namespace Swift {
+
+StreamInitiationFileInfoSerializer::StreamInitiationFileInfoSerializer() {
+}
+
+std::string StreamInitiationFileInfoSerializer::serializePayload(boost::shared_ptr<StreamInitiationFileInfo> fileInfo) const {
+ XMLElement fileElement("file", "http://jabber.org/protocol/si/profile/file-transfer");
+
+ if (fileInfo->getDate() != stringToDateTime("")) {
+ fileElement.setAttribute("date", dateTimeToString(fileInfo->getDate()));
+ }
+ fileElement.setAttribute("hash", fileInfo->getHash());
+ if (fileInfo->getAlgo() != "md5") {
+ fileElement.setAttribute("algo", fileInfo->getAlgo());
+ }
+ if (!fileInfo->getName().empty()) {
+ fileElement.setAttribute("name", fileInfo->getName());
+ }
+ if (fileInfo->getSize() != 0) {
+ fileElement.setAttribute("size", boost::lexical_cast<std::string>(fileInfo->getSize()));
+ }
+ if (!fileInfo->getDescription().empty()) {
+ boost::shared_ptr<XMLElement> desc = boost::make_shared<XMLElement>("desc", "", fileInfo->getDescription());
+ fileElement.addNode(desc);
+ }
+ if (fileInfo->getSupportsRangeRequests()) {
+ boost::shared_ptr<XMLElement> range = boost::make_shared<XMLElement>("range");
+ if (fileInfo->getRangeOffset() != 0) {
+ range->setAttribute("offset", boost::lexical_cast<std::string>(fileInfo->getRangeOffset()));
+ }
+ fileElement.addNode(range);
+ }
+ return fileElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h
new file mode 100644
index 0000000..4ac0a0d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+ class PayloadSerializerCollection;
+
+ class StreamInitiationFileInfoSerializer : public GenericPayloadSerializer<StreamInitiationFileInfo> {
+ public:
+ StreamInitiationFileInfoSerializer();
+
+ virtual std::string serializePayload(boost::shared_ptr<StreamInitiationFileInfo>) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
index 3b71bfb..9ccfab2 100644
--- a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
@@ -36,13 +36,13 @@ std::string StreamInitiationSerializer::serializePayload(boost::shared_ptr<Strea
if (streamInitiation->getFileInfo()) {
StreamInitiationFileInfo file = *streamInitiation->getFileInfo();
boost::shared_ptr<XMLElement> fileElement(new XMLElement("file", "http://jabber.org/protocol/si/profile/file-transfer"));
- fileElement->setAttribute("name", file.name);
- if (file.size != -1) {
- fileElement->setAttribute("size", boost::lexical_cast<std::string>(file.size));
+ fileElement->setAttribute("name", file.getName());
+ if (file.getSize() != 0) {
+ fileElement->setAttribute("size", boost::lexical_cast<std::string>(file.getSize()));
}
- if (!file.description.empty()) {
+ if (!file.getDescription().empty()) {
boost::shared_ptr<XMLElement> descElement(new XMLElement("desc"));
- descElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(file.description)));
+ descElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(file.getDescription())));
fileElement->addNode(descElement);
}
siElement.addNode(fileElement);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp
new file mode 100644
index 0000000..e3ec8fc
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Base/DateTime.h>
+
+using namespace Swift;
+
+class JingleSerializersTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(JingleSerializersTest);
+ CPPUNIT_TEST(testSerialize_StreamInitiationFileInfo);
+ CPPUNIT_TEST(testSerialize_StreamInitiationFileInfoRange);
+
+ CPPUNIT_TEST(testSerialize_Xep0261_Example1);
+ CPPUNIT_TEST(testSerialize_Xep0261_Example9);
+ CPPUNIT_TEST(testSerialize_Xep0261_Example13);
+
+ CPPUNIT_TEST(testSerialize_Xep0234_Example1);
+ CPPUNIT_TEST(testSerialize_Xep0234_Example3);
+ CPPUNIT_TEST(testSerialize_Xep0234_Example5);
+ CPPUNIT_TEST(testSerialize_Xep0234_Example8);
+ CPPUNIT_TEST(testSerialize_Xep0234_Example10);
+ CPPUNIT_TEST(testSerialize_Xep0234_Example13);
+
+ CPPUNIT_TEST(testSerialize_Xep0260_Example1);
+
+ CPPUNIT_TEST_SUITE_END();
+
+ boost::shared_ptr<JinglePayloadSerializer> createTestling() {
+ return boost::make_shared<JinglePayloadSerializer>(&collection);
+ }
+
+
+ public:
+ void testSerialize_StreamInitiationFileInfo() {
+ std::string expected = "<file"
+ " date=\"1969-07-21T02:56:15Z\""
+ " hash=\"552da749930852c69ae5d2141d3766b1\""
+ " name=\"test.txt\""
+ " size=\"1022\""
+ " xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<desc>This is a test. If this were a real file...</desc>"
+ "<range/>"
+ "</file>";
+
+ StreamInitiationFileInfo::ref fileInfo = boost::make_shared<StreamInitiationFileInfo>();
+ fileInfo->setDate(stringToDateTime("1969-07-21T02:56:15Z"));
+ fileInfo->setHash("552da749930852c69ae5d2141d3766b1");
+ fileInfo->setSize(1022);
+ fileInfo->setName("test.txt");
+ fileInfo->setDescription("This is a test. If this were a real file...");
+ fileInfo->setSupportsRangeRequests(true);
+
+ boost::shared_ptr<StreamInitiationFileInfoSerializer> serializer = boost::make_shared<StreamInitiationFileInfoSerializer>();
+ CPPUNIT_ASSERT_EQUAL(expected, serializer->serializePayload(fileInfo));
+ }
+
+ void testSerialize_StreamInitiationFileInfoRange() {
+ std::string expected = "<file hash=\"552da749930852c69ae5d2141d3766b1\""
+ " xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<range offset=\"270336\"/>"
+ "</file>";
+
+ StreamInitiationFileInfo::ref fileInfo = boost::make_shared<StreamInitiationFileInfo>();
+ fileInfo->setHash("552da749930852c69ae5d2141d3766b1");
+ fileInfo->setSupportsRangeRequests(true);
+ fileInfo->setRangeOffset(270336);
+
+ boost::shared_ptr<StreamInitiationFileInfoSerializer> serializer = boost::make_shared<StreamInitiationFileInfoSerializer>();
+ CPPUNIT_ASSERT_EQUAL(expected, serializer->serializePayload(fileInfo));
+ }
+
+
+ // IBB Transport Method Examples
+
+ // http://xmpp.org/extensions/xep-0261.html#example-1
+ void testSerialize_Xep0261_Example1() {
+ std::string expected =
+ "<jingle action=\"session-initiate\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<content creator=\"initiator\" name=\"ex\">"
+ "<transport block-size=\"4096\""
+ " sid=\"ch3d9s71\""
+ " xmlns=\"urn:xmpp:jingle:transports:ibb:1\"/>"
+ "</content>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionInitiate);
+ payload->setSessionID("a73sjjvkla37jfea");
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+
+ JingleIBBTransportPayload::ref transport = boost::make_shared<JingleIBBTransportPayload>();
+ transport->setBlockSize(4096);
+ transport->setSessionID("ch3d9s71");
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(JingleContentPayload::InitiatorCreator);
+ content->setName("ex");
+ content->addTransport(transport);
+
+ payload->addPayload(content);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0261.html#example-9
+ void testSerialize_Xep0261_Example9() {
+ std::string expected =
+ "<jingle action=\"transport-info\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<content creator=\"initiator\" name=\"ex\">"
+ "<transport block-size=\"2048\""
+ " sid=\"bt8a71h6\""
+ " xmlns=\"urn:xmpp:jingle:transports:ibb:1\"/>"
+ "</content>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::TransportInfo);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("a73sjjvkla37jfea");
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(JingleContentPayload::InitiatorCreator);
+ content->setName("ex");
+
+ JingleIBBTransportPayload::ref transport = boost::make_shared<JingleIBBTransportPayload>();
+ transport->setBlockSize(2048);
+ transport->setSessionID("bt8a71h6");
+
+ content->addTransport(transport);
+ payload->addPayload(content);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0261.html#example-13
+ void testSerialize_Xep0261_Example13() {
+ std::string expected =
+ "<jingle action=\"session-terminate\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<reason><success/></reason>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionTerminate);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("a73sjjvkla37jfea");
+ payload->setReason(JinglePayload::Reason(JinglePayload::Reason::Success));
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-1
+ void testSerialize_Xep0234_Example1() {
+ std::string expected = "<description xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+ "<offer>"
+ "<file"
+ " date=\"1969-07-21T02:56:15Z\""
+ " hash=\"552da749930852c69ae5d2141d3766b1\""
+ " name=\"test.txt\""
+ " size=\"1022\""
+ " xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<desc>This is a test. If this were a real file...</desc>"
+ "<range/>"
+ "</file>"
+ "</offer>"
+ "</description>";
+ JingleFileTransferDescription::ref desc = boost::make_shared<JingleFileTransferDescription>();
+ StreamInitiationFileInfo fileInfo;
+
+ fileInfo.setDate(stringToDateTime("1969-07-21T02:56:15Z"));
+ fileInfo.setHash("552da749930852c69ae5d2141d3766b1");
+ fileInfo.setSize(1022);
+ fileInfo.setName("test.txt");
+ fileInfo.setDescription("This is a test. If this were a real file...");
+ fileInfo.setSupportsRangeRequests(true);
+
+ desc->addOffer(fileInfo);
+
+ CPPUNIT_ASSERT_EQUAL(expected, boost::make_shared<JingleFileTransferDescriptionSerializer>()->serialize(desc));
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-3
+ void testSerialize_Xep0234_Example3() {
+ std::string expected =
+ "<jingle action=\"session-accept\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"851ba2\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<content creator=\"initiator\" name=\"a-file-offer\">"
+ "<description xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+ "<offer>"
+ "<file"
+ " date=\"1969-07-21T02:56:15Z\""
+ " hash=\"552da749930852c69ae5d2141d3766b1\""
+ " name=\"test.txt\""
+ " size=\"1022\""
+ " xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<desc>This is a test. If this were a real file...</desc>"
+ "<range/>"
+ "</file>"
+ "</offer>"
+ "</description>"
+ /*"<transport xmlns=\"urn:xmpp:jingle:transports:s5b:1\""
+ " mode=\"tcp\""
+ " sid=\"vj3hs98y\">"
+ "<candidate cid=\"ht567dq\""
+ " host=\"192.169.1.10\""
+ " jid=\"juliet@capulet.lit/balcony\""
+ " port=\"6539\""
+ " priority=\"8257636\""
+ " type=\"direct\"/>"
+ "<candidate cid=\"hr65dqyd\""
+ " host=\"134.102.201.180\""
+ " jid=\"juliet@capulet.lit/balcony\""
+ " port=\"16453\""
+ " priority=\"7929856\""
+ " type=\"assisted\"/>"
+ "<candidate cid=\"grt654q2\""
+ " host=\"2001:638:708:30c9:219:d1ff:fea4:a17d\""
+ " jid=\"juliet@capulet.lit/balcony\""
+ " port=\"6539\""
+ " priority=\"8257606\""
+ " type=\"direct\"/>"
+ "</transport>"*/
+ "</content>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionAccept);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("851ba2");
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(JingleContentPayload::InitiatorCreator);
+ content->setName("a-file-offer");
+
+ JingleFileTransferDescription::ref description = boost::make_shared<JingleFileTransferDescription>();
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setName("test.txt");
+ fileInfo.setSize(1022);
+ fileInfo.setHash("552da749930852c69ae5d2141d3766b1");
+ fileInfo.setDate(stringToDateTime("1969-07-21T02:56:15Z"));
+ fileInfo.setDescription("This is a test. If this were a real file...");
+ fileInfo.setSupportsRangeRequests(true);
+
+ description->addOffer(fileInfo);
+ content->addDescription(description);
+ payload->addPayload(content);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-5
+ void testSerialize_Xep0234_Example5() {
+ std::string expected =
+ "<jingle"
+ " action=\"transport-info\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<content creator=\"initiator\" name=\"ex\"/>"
+ /*"<transport"
+ " sid=\"vj3hs98y\""
+ " xmlns=\"urn:xmpp:jingle:transports:s5b:1\">"
+ "<candidate-used cid=\"hr65dqyd\"/>"
+ "</transport>"*/
+ //"</content>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::TransportInfo);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("a73sjjvkla37jfea");
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(JingleContentPayload::InitiatorCreator);
+ content->setName("ex");
+ payload->addPayload(content);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-8
+ void testSerialize_Xep0234_Example8() {
+ std::string expected =
+ "<jingle"
+ " action=\"session-info\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<checksum xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+ "<file>"
+ "<hashes xmlns=\"urn:xmpp:hashes:0\">"
+ "<hash algo=\"sha-1\">552da749930852c69ae5d2141d3766b1</hash>"
+ "</hashes>"
+ "</file>"
+ "</checksum>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionInfo);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("a73sjjvkla37jfea");
+
+ JingleFileTransferHash::ref hash = boost::make_shared<JingleFileTransferHash>();
+ hash->setHash("sha-1", "552da749930852c69ae5d2141d3766b1");
+
+ payload->addPayload(hash);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-10
+ void testSerialize_Xep0234_Example10() {
+ std::string expected =
+ "<jingle"
+ " action=\"session-initiate\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"uj3b2\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<content creator=\"initiator\" name=\"a-file-request\">"
+ "<description"
+ " xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+ "<request>"
+ "<file"
+ " hash=\"552da749930852c69ae5d2141d3766b1\""
+ " xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<range offset=\"270336\"/>"
+ "</file>"
+ "</request>"
+ "</description>"
+ /*"<transport"
+ " mode=\"tcp\""
+ " sid=\"xig361fj\""
+ " xmlns=\"urn:xmpp:jingle:transports:s5b:1\">"
+ "<candidate"
+ " cid=\"ht567dq\""
+ " host=\"192.169.1.10\""
+ " jid=\"juliet@capulet.lit/balcony\""
+ " port=\"6539\""
+ " priority=\"8257636\""
+ " type=\"direct\"/>"
+ "<candidate"
+ " cid=\"hr65dqyd\""
+ " host=\"134.102.201.180\""
+ " jid=\"juliet@capulet.lit/balcony\""
+ " port=\"16453\""
+ " priority=\"7929856\""
+ " type=\"assisted\"/>"
+ "<candidate"
+ " cid=\"grt654q2\""
+ " host=\"2001:638:708:30c9:219:d1ff:fea4:a17d\""
+ " jid=\"juliet@capulet.lit/balcony\""
+ " port=\"6539\""
+ " priority=\"8257606\""
+ " type=\"direct\"/>"
+ "</transport>"*/
+ "</content>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionInitiate);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("uj3b2");
+
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setHash("552da749930852c69ae5d2141d3766b1");
+ fileInfo.setRangeOffset(270336);
+
+ JingleFileTransferDescription::ref desc = boost::make_shared<JingleFileTransferDescription>();
+ desc->addRequest(fileInfo);
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(JingleContentPayload::InitiatorCreator);
+ content->setName("a-file-request");
+ content->addDescription(desc);
+
+ payload->addPayload(content);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0234.html#example-10
+ void testSerialize_Xep0234_Example13() {
+ std::string expected =
+ "<jingle"
+ " action=\"session-info\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<received xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+ "<file"
+ " hash=\"a749930852c69ae5d2141d3766b1552d\""
+ " xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\"/>"
+ "</received>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionInfo);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("a73sjjvkla37jfea");
+
+ JingleFileTransferReceived::ref received = boost::make_shared<JingleFileTransferReceived>();
+
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setHash("a749930852c69ae5d2141d3766b1552d");
+
+ received->setFileInfo(fileInfo);
+ payload->addPayload(received);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ // http://xmpp.org/extensions/xep-0260.html#example-1
+ void testSerialize_Xep0260_Example1() {
+ std::string expected =
+ "<jingle"
+ " action=\"session-initiate\""
+ " initiator=\"romeo@montague.lit/orchard\""
+ " sid=\"a73sjjvkla37jfea\""
+ " xmlns=\"urn:xmpp:jingle:1\">"
+ "<content creator=\"initiator\" name=\"ex\">"
+ "<transport"
+ " mode=\"tcp\""
+ " sid=\"vj3hs98y\""
+ " xmlns=\"urn:xmpp:jingle:transports:s5b:1\">"
+ "<candidate cid=\"hft54dqy\""
+ " host=\"192.168.4.1\""
+ " jid=\"romeo@montague.lit/orchard\""
+ " port=\"5086\""
+ " priority=\"8257636\""
+ " type=\"direct\"/>"
+ "<candidate cid=\"hutr46fe\""
+ " host=\"24.24.24.1\""
+ " jid=\"romeo@montague.lit/orchard\""
+ " port=\"5087\""
+ " priority=\"8258636\""
+ " type=\"direct\"/>"
+ "</transport>"
+ "</content>"
+ "</jingle>";
+
+ JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+ payload->setAction(JinglePayload::SessionInitiate);
+ payload->setInitiator(JID("romeo@montague.lit/orchard"));
+ payload->setSessionID("a73sjjvkla37jfea");
+
+ JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+ content->setCreator(JingleContentPayload::InitiatorCreator);
+ content->setName("ex");
+
+ JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>();
+ transport->setMode(JingleS5BTransportPayload::TCPMode);
+ transport->setSessionID("vj3hs98y");
+
+ JingleS5BTransportPayload::Candidate candidate1;
+ candidate1.cid = "hft54dqy";
+ candidate1.hostPort = HostAddressPort(HostAddress("192.168.4.1"), 5086);
+ candidate1.jid = JID("romeo@montague.lit/orchard");
+ candidate1.priority = 8257636;
+ candidate1.type = JingleS5BTransportPayload::Candidate::DirectType;
+ transport->addCandidate(candidate1);
+
+ JingleS5BTransportPayload::Candidate candidate2;
+ candidate2.cid = "hutr46fe";
+ candidate2.hostPort = HostAddressPort(HostAddress("24.24.24.1"), 5087);
+ candidate2.jid = JID("romeo@montague.lit/orchard");
+ candidate2.priority = 8258636;
+ candidate2.type = JingleS5BTransportPayload::Candidate::DirectType;
+ transport->addCandidate(candidate2);
+
+ content->addTransport(transport);
+
+ payload->addPayload(content);
+
+ CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+ }
+
+ private:
+ FullPayloadSerializerCollection collection;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(JingleSerializersTest);
+
diff --git a/Swiften/StringCodecs/Hexify.cpp b/Swiften/StringCodecs/Hexify.cpp
index 367743c..668079b 100644
--- a/Swiften/StringCodecs/Hexify.cpp
+++ b/Swiften/StringCodecs/Hexify.cpp
@@ -31,4 +31,22 @@ std::string Hexify::hexify(const ByteArray& data) {
return std::string(result.str());
}
+ByteArray Hexify::unhexify(const std::string& hexstring) {
+ if (hexstring.size() % 2) {
+ return ByteArray();
+ }
+ ByteArray result = ByteArray(hexstring.size() / 2);
+ for (size_t pos = 0; pos < hexstring.size() - 1; pos += 2) {
+ char c;
+ c = hexstring[pos];
+ int a = (c>='0'&&c<='9') ? c-'0' : (c>='A'&&c<='Z') ? c-'A' + 10 : (c>='a'&&c<='z') ? c-'a' + 10 : -1;
+ c = hexstring[pos+1];
+ int b = (c>='0'&&c<='9') ? c-'0' : (c>='A'&&c<='Z') ? c-'A' + 10 : (c>='a'&&c<='z') ? c-'a' + 10 : -1;
+ if (a == -1 || b == -1) return ByteArray(); // fail
+ result[pos/2] = (a<<4) | b;
+
+ }
+ return result;
+}
+
}
diff --git a/Swiften/StringCodecs/Hexify.h b/Swiften/StringCodecs/Hexify.h
index 9815e21..c016448 100644
--- a/Swiften/StringCodecs/Hexify.h
+++ b/Swiften/StringCodecs/Hexify.h
@@ -13,5 +13,6 @@ namespace Swift {
public:
static std::string hexify(unsigned char byte);
static std::string hexify(const ByteArray& data);
+ static ByteArray unhexify(const std::string& hexstring);
};
}
diff --git a/Swiften/StringCodecs/MD5.cpp b/Swiften/StringCodecs/MD5.cpp
index 0d36254..6871f79 100644
--- a/Swiften/StringCodecs/MD5.cpp
+++ b/Swiften/StringCodecs/MD5.cpp
@@ -366,6 +366,27 @@ namespace {
}
}
+MD5::MD5() {
+ state = new md5_state_t;
+ md5_init(state);
+}
+
+MD5::~MD5() {
+ delete state;
+}
+
+MD5& MD5::update(const std::vector<unsigned char>& input) {
+ md5_append(state, reinterpret_cast<const md5_byte_t*>(vecptr(input)), input.size());
+ return *this;
+}
+
+std::vector<unsigned char> MD5::getHash() {
+ ByteArray digest;
+ digest.resize(16);
+ md5_finish(state, reinterpret_cast<md5_byte_t*>(vecptr(digest)));
+ return digest;
+}
+
ByteArray MD5::getHash(const ByteArray& data) {
return getMD5Hash(data);
}
diff --git a/Swiften/StringCodecs/MD5.h b/Swiften/StringCodecs/MD5.h
index b1d610c..09473c2 100644
--- a/Swiften/StringCodecs/MD5.h
+++ b/Swiften/StringCodecs/MD5.h
@@ -10,9 +10,20 @@
#include <Swiften/Base/SafeByteArray.h>
namespace Swift {
+ struct md5_state_s;
+
class MD5 {
public:
+ MD5();
+ ~MD5();
+
+ MD5& update(const std::vector<unsigned char>& data);
+ std::vector<unsigned char> getHash();
+
static ByteArray getHash(const ByteArray& data);
static ByteArray getHash(const SafeByteArray& data);
+
+ private:
+ md5_state_s* state;
};
}
diff --git a/Swiften/StringCodecs/UnitTest/HexifyTest.cpp b/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
index 9cbd0d4..38233f9 100644
--- a/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
+++ b/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
@@ -17,6 +17,7 @@ class HexifyTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(HexifyTest);
CPPUNIT_TEST(testHexify);
CPPUNIT_TEST(testHexify_Byte);
+ CPPUNIT_TEST(testUnhexify);
CPPUNIT_TEST_SUITE_END();
public:
@@ -27,6 +28,11 @@ class HexifyTest : public CppUnit::TestFixture {
void testHexify_Byte() {
CPPUNIT_ASSERT_EQUAL(std::string("b2"), Hexify::hexify(0xb2));
}
+
+ void testUnhexify() {
+ CPPUNIT_ASSERT_EQUAL(std::string("ffaf02"), Hexify::hexify(Hexify::unhexify("ffaf02")));
+ CPPUNIT_ASSERT(createByteArray("\x01\x23\xf2", 3) == Hexify::unhexify("0123f2"));
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(HexifyTest);
diff --git a/Swiften/StringCodecs/UnitTest/MD5Test.cpp b/Swiften/StringCodecs/UnitTest/MD5Test.cpp
index ce7e422..c62c46a 100644
--- a/Swiften/StringCodecs/UnitTest/MD5Test.cpp
+++ b/Swiften/StringCodecs/UnitTest/MD5Test.cpp
@@ -19,6 +19,7 @@ class MD5Test : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(MD5Test);
CPPUNIT_TEST(testGetHash_Empty);
CPPUNIT_TEST(testGetHash_Alphabet);
+ CPPUNIT_TEST(testIncrementalTest);
CPPUNIT_TEST_SUITE_END();
public:
@@ -33,6 +34,16 @@ class MD5Test : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(createByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
}
+
+ void testIncrementalTest() {
+ MD5 testling;
+ testling.update(createByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ testling.update(createByteArray("abcdefghijklmnopqrstuvwxyz0123456789"));
+
+ ByteArray result = testling.getHash();
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(MD5Test);