From ce469cbad1be7074adbcb346c4aa1acdbfd17a37 Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Wed, 9 Dec 2020 14:11:22 +0900 Subject: Renaming of files in preparation for code style tools. Signed-off-by: Michele Calgaro (cherry picked from commit a06e2c2f225d76b67b0058a9880222f75d5495c3) --- .../jabber/jingle/libjingle/libjingle.pro | 108 +- .../jingle/libjingle/talk/base/CMakeLists.txt | 8 +- .../jabber/jingle/libjingle/talk/base/Makefile.am | 32 +- .../libjingle/talk/base/asyncpacketsocket.cc | 83 -- .../libjingle/talk/base/asyncpacketsocket.cpp | 83 ++ .../jingle/libjingle/talk/base/asynctcpsocket.cc | 198 ---- .../jingle/libjingle/talk/base/asynctcpsocket.cpp | 198 ++++ .../jingle/libjingle/talk/base/asyncudpsocket.cc | 83 -- .../jingle/libjingle/talk/base/asyncudpsocket.cpp | 83 ++ .../jabber/jingle/libjingle/talk/base/base64.cc | 194 ---- .../jabber/jingle/libjingle/talk/base/base64.cpp | 194 ++++ .../jingle/libjingle/talk/base/bytebuffer.cc | 166 --- .../jingle/libjingle/talk/base/bytebuffer.cpp | 166 +++ .../jabber/jingle/libjingle/talk/base/common.h | 2 +- .../jabber/jingle/libjingle/talk/base/host.cc | 101 -- .../jabber/jingle/libjingle/talk/base/host.cpp | 101 ++ .../jabber/jingle/libjingle/talk/base/jtime.cc | 77 -- .../jabber/jingle/libjingle/talk/base/jtime.cpp | 77 ++ .../jingle/libjingle/talk/base/messagequeue.cc | 321 ------ .../jingle/libjingle/talk/base/messagequeue.cpp | 321 ++++++ .../jabber/jingle/libjingle/talk/base/network.cc | 382 ------- .../jabber/jingle/libjingle/talk/base/network.cpp | 382 +++++++ .../libjingle/talk/base/physicalsocketserver.cc | 1116 ------------------- .../libjingle/talk/base/physicalsocketserver.cpp | 1116 +++++++++++++++++++ .../jingle/libjingle/talk/base/socketadapters.cc | 1131 -------------------- .../jingle/libjingle/talk/base/socketadapters.cpp | 1131 ++++++++++++++++++++ .../jingle/libjingle/talk/base/socketaddress.cc | 267 ----- .../jingle/libjingle/talk/base/socketaddress.cpp | 267 +++++ .../libjingle/talk/base/socketaddresspair.cc | 58 - .../libjingle/talk/base/socketaddresspair.cpp | 58 + .../jabber/jingle/libjingle/talk/base/task.cc | 238 ---- .../jabber/jingle/libjingle/talk/base/task.cpp | 238 ++++ .../jingle/libjingle/talk/base/taskrunner.cc | 92 -- .../jingle/libjingle/talk/base/taskrunner.cpp | 92 ++ .../jabber/jingle/libjingle/talk/base/thread.cc | 274 ----- .../jabber/jingle/libjingle/talk/base/thread.cpp | 274 +++++ .../libjingle/talk/examples/call/Makefile.am | 4 +- .../jingle/libjingle/talk/examples/call/call.pro | 18 +- .../libjingle/talk/examples/call/call_main.cc | 62 -- .../libjingle/talk/examples/call/call_main.cpp | 62 ++ .../libjingle/talk/examples/call/callclient.cc | 390 ------- .../libjingle/talk/examples/call/callclient.cpp | 390 +++++++ .../jingle/libjingle/talk/examples/call/console.cc | 196 ---- .../libjingle/talk/examples/call/console.cpp | 196 ++++ .../talk/examples/call/presenceouttask.cc | 148 --- .../talk/examples/call/presenceouttask.cpp | 148 +++ .../talk/examples/call/presencepushtask.cc | 172 --- .../talk/examples/call/presencepushtask.cpp | 172 +++ .../libjingle/talk/examples/login/Makefile.am | 10 +- .../libjingle/talk/examples/login/login_main.cc | 63 -- .../libjingle/talk/examples/login/login_main.cpp | 63 ++ .../libjingle/talk/examples/login/xmppauth.cc | 93 -- .../libjingle/talk/examples/login/xmppauth.cpp | 93 ++ .../libjingle/talk/examples/login/xmpppump.cc | 73 -- .../libjingle/talk/examples/login/xmpppump.cpp | 73 ++ .../libjingle/talk/examples/login/xmppsocket.cc | 144 --- .../libjingle/talk/examples/login/xmppsocket.cpp | 144 +++ .../libjingle/talk/examples/login/xmppthread.cc | 80 -- .../libjingle/talk/examples/login/xmppthread.cpp | 80 ++ .../jingle/libjingle/talk/p2p/base/CMakeLists.txt | 10 +- .../jingle/libjingle/talk/p2p/base/Makefile.am | 28 +- .../jingle/libjingle/talk/p2p/base/helpers.cc | 129 --- .../jingle/libjingle/talk/p2p/base/helpers.cpp | 129 +++ .../jingle/libjingle/talk/p2p/base/p2psocket.cc | 910 ---------------- .../jingle/libjingle/talk/p2p/base/p2psocket.cpp | 910 ++++++++++++++++ .../jabber/jingle/libjingle/talk/p2p/base/port.cc | 869 --------------- .../jabber/jingle/libjingle/talk/p2p/base/port.cpp | 869 +++++++++++++++ .../jingle/libjingle/talk/p2p/base/relayport.cc | 640 ----------- .../jingle/libjingle/talk/p2p/base/relayport.cpp | 640 +++++++++++ .../jingle/libjingle/talk/p2p/base/relayserver.cc | 657 ------------ .../jingle/libjingle/talk/p2p/base/relayserver.cpp | 657 ++++++++++++ .../jingle/libjingle/talk/p2p/base/relayserver.pro | 8 +- .../libjingle/talk/p2p/base/relayserver_main.cc | 76 -- .../libjingle/talk/p2p/base/relayserver_main.cpp | 76 ++ .../jingle/libjingle/talk/p2p/base/session.cc | 421 -------- .../jingle/libjingle/talk/p2p/base/session.cpp | 421 ++++++++ .../libjingle/talk/p2p/base/sessionmanager.cc | 173 --- .../libjingle/talk/p2p/base/sessionmanager.cpp | 173 +++ .../libjingle/talk/p2p/base/socketmanager.cc | 273 ----- .../libjingle/talk/p2p/base/socketmanager.cpp | 273 +++++ .../jabber/jingle/libjingle/talk/p2p/base/stun.cc | 577 ---------- .../jabber/jingle/libjingle/talk/p2p/base/stun.cpp | 577 ++++++++++ .../jingle/libjingle/talk/p2p/base/stunport.cc | 171 --- .../jingle/libjingle/talk/p2p/base/stunport.cpp | 171 +++ .../jingle/libjingle/talk/p2p/base/stunrequest.cc | 198 ---- .../jingle/libjingle/talk/p2p/base/stunrequest.cpp | 198 ++++ .../jingle/libjingle/talk/p2p/base/stunserver.cc | 161 --- .../jingle/libjingle/talk/p2p/base/stunserver.cpp | 161 +++ .../jingle/libjingle/talk/p2p/base/stunserver.pro | 8 +- .../libjingle/talk/p2p/base/stunserver_main.cc | 67 -- .../libjingle/talk/p2p/base/stunserver_main.cpp | 67 ++ .../jingle/libjingle/talk/p2p/base/tcpport.cc | 250 ----- .../jingle/libjingle/talk/p2p/base/tcpport.cpp | 250 +++++ .../jingle/libjingle/talk/p2p/base/udpport.cc | 117 -- .../jingle/libjingle/talk/p2p/base/udpport.cpp | 117 ++ .../libjingle/talk/p2p/client/CMakeLists.txt | 2 +- .../jingle/libjingle/talk/p2p/client/Makefile.am | 6 +- .../talk/p2p/client/basicportallocator.cc | 667 ------------ .../talk/p2p/client/basicportallocator.cpp | 667 ++++++++++++ .../libjingle/talk/p2p/client/sessionclient.cc | 545 ---------- .../libjingle/talk/p2p/client/sessionclient.cpp | 545 ++++++++++ .../libjingle/talk/p2p/client/socketmonitor.cc | 149 --- .../libjingle/talk/p2p/client/socketmonitor.cpp | 149 +++ .../libjingle/talk/session/phone/CMakeLists.txt | 4 +- .../libjingle/talk/session/phone/Makefile.am | 12 +- .../libjingle/talk/session/phone/audiomonitor.cc | 119 -- .../libjingle/talk/session/phone/audiomonitor.cpp | 119 ++ .../jingle/libjingle/talk/session/phone/call.cc | 258 ----- .../jingle/libjingle/talk/session/phone/call.cpp | 258 +++++ .../libjingle/talk/session/phone/channelmanager.cc | 203 ---- .../talk/session/phone/channelmanager.cpp | 203 ++++ .../talk/session/phone/linphonemediaengine.cc | 172 --- .../talk/session/phone/linphonemediaengine.cpp | 172 +++ .../talk/session/phone/phonesessionclient.cc | 269 ----- .../talk/session/phone/phonesessionclient.cpp | 269 +++++ .../talk/session/phone/portaudiomediaengine.cc | 331 ------ .../talk/session/phone/portaudiomediaengine.cpp | 331 ++++++ .../libjingle/talk/session/phone/voicechannel.cc | 331 ------ .../libjingle/talk/session/phone/voicechannel.cpp | 331 ++++++ .../jingle/libjingle/talk/xmllite/CMakeLists.txt | 4 +- .../jingle/libjingle/talk/xmllite/Makefile.am | 14 +- .../jabber/jingle/libjingle/talk/xmllite/qname.cc | 167 --- .../jabber/jingle/libjingle/talk/xmllite/qname.cpp | 167 +++ .../jingle/libjingle/talk/xmllite/xmlbuilder.cc | 151 --- .../jingle/libjingle/talk/xmllite/xmlbuilder.cpp | 151 +++ .../jingle/libjingle/talk/xmllite/xmlconstants.cc | 65 -- .../jingle/libjingle/talk/xmllite/xmlconstants.cpp | 65 ++ .../jingle/libjingle/talk/xmllite/xmlelement.cc | 491 --------- .../jingle/libjingle/talk/xmllite/xmlelement.cpp | 491 +++++++++ .../jingle/libjingle/talk/xmllite/xmlnsstack.cc | 205 ---- .../jingle/libjingle/talk/xmllite/xmlnsstack.cpp | 205 ++++ .../jingle/libjingle/talk/xmllite/xmlparser.cc | 250 ----- .../jingle/libjingle/talk/xmllite/xmlparser.cpp | 250 +++++ .../jingle/libjingle/talk/xmllite/xmlprinter.cc | 190 ---- .../jingle/libjingle/talk/xmllite/xmlprinter.cpp | 190 ++++ .../jingle/libjingle/talk/xmpp/CMakeLists.txt | 4 +- .../jabber/jingle/libjingle/talk/xmpp/Makefile.am | 18 +- .../jabber/jingle/libjingle/talk/xmpp/constants.cc | 331 ------ .../jingle/libjingle/talk/xmpp/constants.cpp | 331 ++++++ .../jabber/jingle/libjingle/talk/xmpp/jid.cc | 477 --------- .../jabber/jingle/libjingle/talk/xmpp/jid.cpp | 477 +++++++++ .../jingle/libjingle/talk/xmpp/saslmechanism.cc | 68 -- .../jingle/libjingle/talk/xmpp/saslmechanism.cpp | 68 ++ .../jingle/libjingle/talk/xmpp/xmppclient.cc | 372 ------- .../jingle/libjingle/talk/xmpp/xmppclient.cpp | 372 +++++++ .../jingle/libjingle/talk/xmpp/xmppengineimpl.cc | 480 --------- .../jingle/libjingle/talk/xmpp/xmppengineimpl.cpp | 480 +++++++++ .../libjingle/talk/xmpp/xmppengineimpl_iq.cc | 279 ----- .../libjingle/talk/xmpp/xmppengineimpl_iq.cpp | 279 +++++ .../jingle/libjingle/talk/xmpp/xmpplogintask.cc | 357 ------ .../jingle/libjingle/talk/xmpp/xmpplogintask.cpp | 357 ++++++ .../jingle/libjingle/talk/xmpp/xmppstanzaparser.cc | 104 -- .../libjingle/talk/xmpp/xmppstanzaparser.cpp | 104 ++ .../jabber/jingle/libjingle/talk/xmpp/xmpptask.cc | 168 --- .../jabber/jingle/libjingle/talk/xmpp/xmpptask.cpp | 168 +++ 155 files changed, 19240 insertions(+), 19240 deletions(-) delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/host.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/task.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cpp delete mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cpp (limited to 'kopete/protocols/jabber/jingle') diff --git a/kopete/protocols/jabber/jingle/libjingle/libjingle.pro b/kopete/protocols/jabber/jingle/libjingle/libjingle.pro index 53c8e293..0ad01942 100644 --- a/kopete/protocols/jabber/jingle/libjingle/libjingle.pro +++ b/kopete/protocols/jabber/jingle/libjingle/libjingle.pro @@ -15,82 +15,82 @@ OBJECTS_DIR = $$JINGLE_CPP/.obj # Base SOURCES += \ - $$JINGLE_CPP/talk/base/asyncpacketsocket.cc \ - $$JINGLE_CPP/talk/base/asynctcpsocket.cc \ - $$JINGLE_CPP/talk/base/asyncudpsocket.cc \ - $$JINGLE_CPP/talk/base/base64.cc \ - $$JINGLE_CPP/talk/base/bytebuffer.cc \ + $$JINGLE_CPP/talk/base/asyncpacketsocket.cpp \ + $$JINGLE_CPP/talk/base/asynctcpsocket.cpp \ + $$JINGLE_CPP/talk/base/asyncudpsocket.cpp \ + $$JINGLE_CPP/talk/base/base64.cpp \ + $$JINGLE_CPP/talk/base/bytebuffer.cpp \ $$JINGLE_CPP/talk/base/md5c.c \ - $$JINGLE_CPP/talk/base/messagequeue.cc \ - $$JINGLE_CPP/talk/base/network.cc \ - $$JINGLE_CPP/talk/base/physicalsocketserver.cc \ - $$JINGLE_CPP/talk/base/socketadapters.cc \ - $$JINGLE_CPP/talk/base/socketaddress.cc \ - $$JINGLE_CPP/talk/base/task.cc \ - $$JINGLE_CPP/talk/base/taskrunner.cc \ - $$JINGLE_CPP/talk/base/thread.cc \ - $$JINGLE_CPP/talk/base/time.cc + $$JINGLE_CPP/talk/base/messagequeue.cpp \ + $$JINGLE_CPP/talk/base/network.cpp \ + $$JINGLE_CPP/talk/base/physicalsocketserver.cpp \ + $$JINGLE_CPP/talk/base/socketadapters.cpp \ + $$JINGLE_CPP/talk/base/socketaddress.cpp \ + $$JINGLE_CPP/talk/base/task.cpp \ + $$JINGLE_CPP/talk/base/taskrunner.cpp \ + $$JINGLE_CPP/talk/base/thread.cpp \ + $$JINGLE_CPP/talk/base/time.cpp # Not needed ? -#$$JINGLE_CPP/talk/base/socketaddresspair.cc \ -#$$JINGLE_CPP/talk/base/host.cc \ +#$$JINGLE_CPP/talk/base/socketaddresspair.cpp \ +#$$JINGLE_CPP/talk/base/host.cpp \ # P2P Base SOURCES += \ - $$JINGLE_CPP/talk/p2p/base/helpers.cc \ - $$JINGLE_CPP/talk/p2p/base/p2psocket.cc \ - $$JINGLE_CPP/talk/p2p/base/port.cc \ - $$JINGLE_CPP/talk/p2p/base/relayport.cc \ - $$JINGLE_CPP/talk/p2p/base/session.cc \ - $$JINGLE_CPP/talk/p2p/base/sessionmanager.cc \ - $$JINGLE_CPP/talk/p2p/base/socketmanager.cc \ - $$JINGLE_CPP/talk/p2p/base/stun.cc \ - $$JINGLE_CPP/talk/p2p/base/stunport.cc \ - $$JINGLE_CPP/talk/p2p/base/stunrequest.cc \ - $$JINGLE_CPP/talk/p2p/base/tcpport.cc \ - $$JINGLE_CPP/talk/p2p/base/udpport.cc + $$JINGLE_CPP/talk/p2p/base/helpers.cpp \ + $$JINGLE_CPP/talk/p2p/base/p2psocket.cpp \ + $$JINGLE_CPP/talk/p2p/base/port.cpp \ + $$JINGLE_CPP/talk/p2p/base/relayport.cpp \ + $$JINGLE_CPP/talk/p2p/base/session.cpp \ + $$JINGLE_CPP/talk/p2p/base/sessionmanager.cpp \ + $$JINGLE_CPP/talk/p2p/base/socketmanager.cpp \ + $$JINGLE_CPP/talk/p2p/base/stun.cpp \ + $$JINGLE_CPP/talk/p2p/base/stunport.cpp \ + $$JINGLE_CPP/talk/p2p/base/stunrequest.cpp \ + $$JINGLE_CPP/talk/p2p/base/tcpport.cpp \ + $$JINGLE_CPP/talk/p2p/base/udpport.cpp # P2P Client SOURCES += \ - $$JINGLE_CPP/talk/p2p/client/basicportallocator.cc \ - $$JINGLE_CPP/talk/p2p/client/sessionclient.cc \ - $$JINGLE_CPP/talk/p2p/client/socketmonitor.cc + $$JINGLE_CPP/talk/p2p/client/basicportallocator.cpp \ + $$JINGLE_CPP/talk/p2p/client/sessionclient.cpp \ + $$JINGLE_CPP/talk/p2p/client/socketmonitor.cpp # XMLLite SOURCES += \ - $$JINGLE_CPP/talk/xmllite/qname.cc \ - $$JINGLE_CPP/talk/xmllite/xmlbuilder.cc \ - $$JINGLE_CPP/talk/xmllite/xmlconstants.cc \ - $$JINGLE_CPP/talk/xmllite/xmlelement.cc \ - $$JINGLE_CPP/talk/xmllite/xmlnsstack.cc \ - $$JINGLE_CPP/talk/xmllite/xmlparser.cc \ - $$JINGLE_CPP/talk/xmllite/xmlprinter.cc + $$JINGLE_CPP/talk/xmllite/qname.cpp \ + $$JINGLE_CPP/talk/xmllite/xmlbuilder.cpp \ + $$JINGLE_CPP/talk/xmllite/xmlconstants.cpp \ + $$JINGLE_CPP/talk/xmllite/xmlelement.cpp \ + $$JINGLE_CPP/talk/xmllite/xmlnsstack.cpp \ + $$JINGLE_CPP/talk/xmllite/xmlparser.cpp \ + $$JINGLE_CPP/talk/xmllite/xmlprinter.cpp # XMPP SOURCES += \ - $$JINGLE_CPP/talk/xmpp/constants.cc \ - $$JINGLE_CPP/talk/xmpp/jid.cc \ - $$JINGLE_CPP/talk/xmpp/saslmechanism.cc \ - $$JINGLE_CPP/talk/xmpp/xmppclient.cc \ - $$JINGLE_CPP/talk/xmpp/xmppengineimpl.cc \ - $$JINGLE_CPP/talk/xmpp/xmppengineimpl_iq.cc \ - $$JINGLE_CPP/talk/xmpp/xmpplogintask.cc \ - $$JINGLE_CPP/talk/xmpp/xmppstanzaparser.cc \ - $$JINGLE_CPP/talk/xmpp/xmpptask.cc + $$JINGLE_CPP/talk/xmpp/constants.cpp \ + $$JINGLE_CPP/talk/xmpp/jid.cpp \ + $$JINGLE_CPP/talk/xmpp/saslmechanism.cpp \ + $$JINGLE_CPP/talk/xmpp/xmppclient.cpp \ + $$JINGLE_CPP/talk/xmpp/xmppengineimpl.cpp \ + $$JINGLE_CPP/talk/xmpp/xmppengineimpl_iq.cpp \ + $$JINGLE_CPP/talk/xmpp/xmpplogintask.cpp \ + $$JINGLE_CPP/talk/xmpp/xmppstanzaparser.cpp \ + $$JINGLE_CPP/talk/xmpp/xmpptask.cpp # Session SOURCES += \ - $$JINGLE_CPP/talk/session/phone/call.cc \ - $$JINGLE_CPP/talk/session/phone/audiomonitor.cc \ - $$JINGLE_CPP/talk/session/phone/phonesessionclient.cc \ - $$JINGLE_CPP/talk/session/phone/channelmanager.cc \ - $$JINGLE_CPP/talk/session/phone/linphonemediaengine.cc \ - $$JINGLE_CPP/talk/session/phone/voicechannel.cc + $$JINGLE_CPP/talk/session/phone/call.cpp \ + $$JINGLE_CPP/talk/session/phone/audiomonitor.cpp \ + $$JINGLE_CPP/talk/session/phone/phonesessionclient.cpp \ + $$JINGLE_CPP/talk/session/phone/channelmanager.cpp \ + $$JINGLE_CPP/talk/session/phone/linphonemediaengine.cpp \ + $$JINGLE_CPP/talk/session/phone/voicechannel.cpp #contains(DEFINES, HAVE_PORTAUDIO) { # SOURCES += \ -# $$JINGLE_CPP/talk/session/phone/portaudiomediaengine.cc +# $$JINGLE_CPP/talk/session/phone/portaudiomediaengine.cpp #} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt index 8f037a9a..978eb5bb 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/CMakeLists.txt @@ -25,8 +25,8 @@ include_directories( tde_add_library( cricketbase STATIC_PIC SOURCES - socketaddress.cc jtime.cc asyncudpsocket.cc messagequeue.cc - thread.cc physicalsocketserver.cc bytebuffer.cc asyncpacketsocket.cc - network.cc asynctcpsocket.cc socketadapters.cc md5c.c base64.cc - task.cc taskrunner.cc host.cc socketaddresspair.cc + socketaddress.cpp jtime.cpp asyncudpsocket.cpp messagequeue.cpp + thread.cpp physicalsocketserver.cpp bytebuffer.cpp asyncpacketsocket.cpp + network.cpp asynctcpsocket.cpp socketadapters.cpp md5c.c base64.cpp + task.cpp taskrunner.cpp host.cpp socketaddresspair.cpp ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am index 2921049a..ddffd93e 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am @@ -1,23 +1,23 @@ ## Does not compile with final KDE_OPTIONS = nofinal -libcricketbase_la_SOURCES = socketaddress.cc \ - jtime.cc \ - asyncudpsocket.cc \ - messagequeue.cc \ - thread.cc \ - physicalsocketserver.cc \ - bytebuffer.cc \ - asyncpacketsocket.cc \ - network.cc \ - asynctcpsocket.cc \ - socketadapters.cc \ +libcricketbase_la_SOURCES = socketaddress.cpp \ + jtime.cpp \ + asyncudpsocket.cpp \ + messagequeue.cpp \ + thread.cpp \ + physicalsocketserver.cpp \ + bytebuffer.cpp \ + asyncpacketsocket.cpp \ + network.cpp \ + asynctcpsocket.cpp \ + socketadapters.cpp \ md5c.c \ - base64.cc \ - task.cc \ - taskrunner.cc \ - host.cc \ - socketaddresspair.cc + base64.cpp \ + task.cpp \ + taskrunner.cpp \ + host.cpp \ + socketaddresspair.cpp noinst_HEADERS = asyncfile.h \ common.h \ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc deleted file mode 100644 index 10cfa617..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/asyncpacketsocket.h" - -namespace cricket { - -AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) { -} - -AsyncPacketSocket::~AsyncPacketSocket() { - delete socket_; -} - -SocketAddress AsyncPacketSocket::GetLocalAddress() const { - return socket_->GetLocalAddress(); -} - -SocketAddress AsyncPacketSocket::GetRemoteAddress() const { - return socket_->GetRemoteAddress(); -} - -int AsyncPacketSocket::Bind(const SocketAddress& addr) { - return socket_->Bind(addr); -} - -int AsyncPacketSocket::Connect(const SocketAddress& addr) { - return socket_->Connect(addr); -} - -int AsyncPacketSocket::Send(const void *pv, size_t cb) { - return socket_->Send(pv, cb); -} - -int AsyncPacketSocket::SendTo( - const void *pv, size_t cb, const SocketAddress& addr) { - return socket_->SendTo(pv, cb, addr); -} - -int AsyncPacketSocket::Close() { - return socket_->Close(); -} - -int AsyncPacketSocket::SetOption(Socket::Option opt, int value) { - return socket_->SetOption(opt, value); -} - -int AsyncPacketSocket::GetError() const { - return socket_->GetError(); -} - -void AsyncPacketSocket::SetError(int error) { - return socket_->SetError(error); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cpp new file mode 100644 index 00000000..10cfa617 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cpp @@ -0,0 +1,83 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asyncpacketsocket.h" + +namespace cricket { + +AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) { +} + +AsyncPacketSocket::~AsyncPacketSocket() { + delete socket_; +} + +SocketAddress AsyncPacketSocket::GetLocalAddress() const { + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncPacketSocket::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + +int AsyncPacketSocket::Bind(const SocketAddress& addr) { + return socket_->Bind(addr); +} + +int AsyncPacketSocket::Connect(const SocketAddress& addr) { + return socket_->Connect(addr); +} + +int AsyncPacketSocket::Send(const void *pv, size_t cb) { + return socket_->Send(pv, cb); +} + +int AsyncPacketSocket::SendTo( + const void *pv, size_t cb, const SocketAddress& addr) { + return socket_->SendTo(pv, cb, addr); +} + +int AsyncPacketSocket::Close() { + return socket_->Close(); +} + +int AsyncPacketSocket::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncPacketSocket::GetError() const { + return socket_->GetError(); +} + +void AsyncPacketSocket::SetError(int error) { + return socket_->SetError(error); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc deleted file mode 100644 index 8bf66a38..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc +++ /dev/null @@ -1,198 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/asynctcpsocket.h" -#include "talk/base/byteorder.h" -#include "talk/base/common.h" -#include "talk/base/logging.h" -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -const size_t MAX_PACKET_SIZE = 64 * 1024; - -typedef uint16 PacketLength; -const size_t PKT_LEN_SIZE = sizeof(PacketLength); - -const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE; - -AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), outpos_(0) { - inbuf_ = new char[insize_]; - outbuf_ = new char[outsize_]; - - ASSERT(socket_ != NULL); - socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent); - socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent); - socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent); - socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent); -} - -AsyncTCPSocket::~AsyncTCPSocket() { - delete [] inbuf_; - delete [] outbuf_; -} - -int AsyncTCPSocket::Send(const void *pv, size_t cb) { - if (cb > MAX_PACKET_SIZE) { - socket_->SetError(EMSGSIZE); - return -1; - } - - // If we are blocking on send, then silently drop this packet - if (outpos_) - return static_cast(cb); - - PacketLength pkt_len = HostToNetwork16(static_cast(cb)); - memcpy(outbuf_, &pkt_len, PKT_LEN_SIZE); - memcpy(outbuf_ + PKT_LEN_SIZE, pv, cb); - outpos_ = PKT_LEN_SIZE + cb; - - int res = Flush(); - if (res <= 0) { - // drop packet if we made no progress - outpos_ = 0; - return res; - } - - // We claim to have sent the whole thing, even if we only sent partial - return static_cast(cb); -} - -int AsyncTCPSocket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { - if (addr == GetRemoteAddress()) - return Send(pv, cb); - - ASSERT(false); - socket_->SetError(ENOTCONN); - return -1; -} - -int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) { - if (outpos_ + cb > outsize_) { - socket_->SetError(EMSGSIZE); - return -1; - } - - memcpy(outbuf_ + outpos_, pv, cb); - outpos_ += cb; - - return Flush(); -} - -void AsyncTCPSocket::ProcessInput(char * data, size_t& len) { - SocketAddress remote_addr(GetRemoteAddress()); - - while (true) { - if (len < PKT_LEN_SIZE) - return; - - PacketLength pkt_len; - memcpy(&pkt_len, data, PKT_LEN_SIZE); - pkt_len = NetworkToHost16(pkt_len); - - if (len < PKT_LEN_SIZE + pkt_len) - return; - - SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this); - - len -= PKT_LEN_SIZE + pkt_len; - if (len > 0) { - memmove(data, data + PKT_LEN_SIZE + pkt_len, len); - } - } -} - -int AsyncTCPSocket::Flush() { - int res = socket_->Send(outbuf_, outpos_); - if (res <= 0) { - return res; - } - if (static_cast(res) <= outpos_) { - outpos_ -= res; - } else { - ASSERT(false); - return -1; - } - if (outpos_ > 0) { - memmove(outbuf_, outbuf_ + res, outpos_); - } - return res; -} - -void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) { - SignalConnect(this); -} - -void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) { - ASSERT(socket == socket_); - - int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); - if (len < 0) { - // TODO: Do something better like forwarding the error to the user. - LOG(INFO) << "recvfrom: " << errno << " " << std::strerror(errno); - return; - } - - inpos_ += len; - - ProcessInput(inbuf_, inpos_); - - if (inpos_ >= insize_) { - LOG(INFO) << "input buffer overflow"; - ASSERT(false); - inpos_ = 0; - } -} - -void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) { - ASSERT(socket == socket_); - - if (outpos_ > 0) { - Flush(); - } -} - -void AsyncTCPSocket::OnCloseEvent(AsyncSocket* socket, int error) { - SignalClose(this, error); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cpp new file mode 100644 index 00000000..8bf66a38 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cpp @@ -0,0 +1,198 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asynctcpsocket.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +const size_t MAX_PACKET_SIZE = 64 * 1024; + +typedef uint16 PacketLength; +const size_t PKT_LEN_SIZE = sizeof(PacketLength); + +const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE; + +AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), outpos_(0) { + inbuf_ = new char[insize_]; + outbuf_ = new char[outsize_]; + + ASSERT(socket_ != NULL); + socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent); +} + +AsyncTCPSocket::~AsyncTCPSocket() { + delete [] inbuf_; + delete [] outbuf_; +} + +int AsyncTCPSocket::Send(const void *pv, size_t cb) { + if (cb > MAX_PACKET_SIZE) { + socket_->SetError(EMSGSIZE); + return -1; + } + + // If we are blocking on send, then silently drop this packet + if (outpos_) + return static_cast(cb); + + PacketLength pkt_len = HostToNetwork16(static_cast(cb)); + memcpy(outbuf_, &pkt_len, PKT_LEN_SIZE); + memcpy(outbuf_ + PKT_LEN_SIZE, pv, cb); + outpos_ = PKT_LEN_SIZE + cb; + + int res = Flush(); + if (res <= 0) { + // drop packet if we made no progress + outpos_ = 0; + return res; + } + + // We claim to have sent the whole thing, even if we only sent partial + return static_cast(cb); +} + +int AsyncTCPSocket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + if (addr == GetRemoteAddress()) + return Send(pv, cb); + + ASSERT(false); + socket_->SetError(ENOTCONN); + return -1; +} + +int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) { + if (outpos_ + cb > outsize_) { + socket_->SetError(EMSGSIZE); + return -1; + } + + memcpy(outbuf_ + outpos_, pv, cb); + outpos_ += cb; + + return Flush(); +} + +void AsyncTCPSocket::ProcessInput(char * data, size_t& len) { + SocketAddress remote_addr(GetRemoteAddress()); + + while (true) { + if (len < PKT_LEN_SIZE) + return; + + PacketLength pkt_len; + memcpy(&pkt_len, data, PKT_LEN_SIZE); + pkt_len = NetworkToHost16(pkt_len); + + if (len < PKT_LEN_SIZE + pkt_len) + return; + + SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this); + + len -= PKT_LEN_SIZE + pkt_len; + if (len > 0) { + memmove(data, data + PKT_LEN_SIZE + pkt_len, len); + } + } +} + +int AsyncTCPSocket::Flush() { + int res = socket_->Send(outbuf_, outpos_); + if (res <= 0) { + return res; + } + if (static_cast(res) <= outpos_) { + outpos_ -= res; + } else { + ASSERT(false); + return -1; + } + if (outpos_ > 0) { + memmove(outbuf_, outbuf_ + res, outpos_); + } + return res; +} + +void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) { + SignalConnect(this); +} + +void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + + int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "recvfrom: " << errno << " " << std::strerror(errno); + return; + } + + inpos_ += len; + + ProcessInput(inbuf_, inpos_); + + if (inpos_ >= insize_) { + LOG(INFO) << "input buffer overflow"; + ASSERT(false); + inpos_ = 0; + } +} + +void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + + if (outpos_ > 0) { + Flush(); + } +} + +void AsyncTCPSocket::OnCloseEvent(AsyncSocket* socket, int error) { + SignalClose(this, error); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc deleted file mode 100644 index 5b8c2466..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/asyncudpsocket.h" -#include "talk/base/logging.h" -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -const int BUF_SIZE = 64 * 1024; - -AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket) { - size_ = BUF_SIZE; - buf_ = new char[size_]; - - assert(socket_); - // The socket should start out readable but not writable. - socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent); -} - -AsyncUDPSocket::~AsyncUDPSocket() { - delete [] buf_; -} - -void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { - assert(socket == socket_); - - SocketAddress remote_addr; - int len = socket_->RecvFrom(buf_, size_, &remote_addr); - if (len < 0) { - // TODO: Do something better like forwarding the error to the user. - PLOG(LS_ERROR, socket_->GetError()) << "recvfrom"; - return; - } - - // TODO: Make sure that we got all of the packet. If we did not, then we - // should resize our buffer to be large enough. - - SignalReadPacket(buf_, (size_t)len, remote_addr, this); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cpp new file mode 100644 index 00000000..5b8c2466 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cpp @@ -0,0 +1,83 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asyncudpsocket.h" +#include "talk/base/logging.h" +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +const int BUF_SIZE = 64 * 1024; + +AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket) { + size_ = BUF_SIZE; + buf_ = new char[size_]; + + assert(socket_); + // The socket should start out readable but not writable. + socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent); +} + +AsyncUDPSocket::~AsyncUDPSocket() { + delete [] buf_; +} + +void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { + assert(socket == socket_); + + SocketAddress remote_addr; + int len = socket_->RecvFrom(buf_, size_, &remote_addr); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + PLOG(LS_ERROR, socket_->GetError()) << "recvfrom"; + return; + } + + // TODO: Make sure that we got all of the packet. If we did not, then we + // should resize our buffer to be large enough. + + SignalReadPacket(buf_, (size_t)len, remote_addr, this); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc deleted file mode 100644 index e0ec1b90..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc +++ /dev/null @@ -1,194 +0,0 @@ - -//********************************************************************* -//* Base64 - a simple base64 encoder and decoder. -//* -//* Copyright (c) 1999, Bob Withers - bwit@pobox.com -//* -//* This code may be freely used for any purpose, either personal -//* or commercial, provided the authors copyright notice remains -//* intact. -//* -//* Enhancements by Stanley Yamane: -//* o reverse lookup table for the decode function -//* o reserve string buffer space in advance -//* -//********************************************************************* - -#include "talk/base/base64.h" - -using namespace std; - -static const char fillchar = '='; -static const string::size_type np = string::npos; - -const string Base64::Base64Table( - // 0000000000111111111122222222223333333333444444444455555555556666 - // 0123456789012345678901234567890123456789012345678901234567890123 - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); - -// Decode Table gives the index of any valid base64 character in the Base64 table] -// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == / - - // 0 1 2 3 4 5 6 7 8 9 -const string::size_type Base64::DecodeTable[] = { - np,np,np,np,np,np,np,np,np,np, // 0 - 9 - np,np,np,np,np,np,np,np,np,np, //10 -19 - np,np,np,np,np,np,np,np,np,np, //20 -29 - np,np,np,np,np,np,np,np,np,np, //30 -39 - np,np,np,62,np,np,np,63,52,53, //40 -49 - 54,55,56,57,58,59,60,61,np,np, //50 -59 - np,np,np,np,np, 0, 1, 2, 3, 4, //60 -69 - 5, 6, 7, 8, 9,10,11,12,13,14, //70 -79 - 15,16,17,18,19,20,21,22,23,24, //80 -89 - 25,np,np,np,np,np,np,26,27,28, //90 -99 - 29,30,31,32,33,34,35,36,37,38, //100 -109 - 39,40,41,42,43,44,45,46,47,48, //110 -119 - 49,50,51,np,np,np,np,np,np,np, //120 -129 - np,np,np,np,np,np,np,np,np,np, //130 -139 - np,np,np,np,np,np,np,np,np,np, //140 -149 - np,np,np,np,np,np,np,np,np,np, //150 -159 - np,np,np,np,np,np,np,np,np,np, //160 -169 - np,np,np,np,np,np,np,np,np,np, //170 -179 - np,np,np,np,np,np,np,np,np,np, //180 -189 - np,np,np,np,np,np,np,np,np,np, //190 -199 - np,np,np,np,np,np,np,np,np,np, //200 -209 - np,np,np,np,np,np,np,np,np,np, //210 -219 - np,np,np,np,np,np,np,np,np,np, //220 -229 - np,np,np,np,np,np,np,np,np,np, //230 -239 - np,np,np,np,np,np,np,np,np,np, //240 -249 - np,np,np,np,np,np //250 -256 -}; - -string Base64::encodeFromArray(const char * data, size_t len) { - size_t i; - char c; - string ret; - - ret.reserve(len * 2); - - for (i = 0; i < len; ++i) - { - c = (data[i] >> 2) & 0x3f; - ret.append(1, Base64Table[c]); - c = (data[i] << 4) & 0x3f; - if (++i < len) - c |= (data[i] >> 4) & 0x0f; - - ret.append(1, Base64Table[c]); - if (i < len) - { - c = (data[i] << 2) & 0x3f; - if (++i < len) - c |= (data[i] >> 6) & 0x03; - - ret.append(1, Base64Table[c]); - } - else - { - ++i; - ret.append(1, fillchar); - } - - if (i < len) - { - c = data[i] & 0x3f; - ret.append(1, Base64Table[c]); - } - else - { - ret.append(1, fillchar); - } - } - - return(ret); -} - - -string Base64::encode(const string& data) -{ - string::size_type i; - char c; - string::size_type len = data.length(); - string ret; - - ret.reserve(len * 2); - - for (i = 0; i < len; ++i) - { - c = (data[i] >> 2) & 0x3f; - ret.append(1, Base64Table[c]); - c = (data[i] << 4) & 0x3f; - if (++i < len) - c |= (data[i] >> 4) & 0x0f; - - ret.append(1, Base64Table[c]); - if (i < len) - { - c = (data[i] << 2) & 0x3f; - if (++i < len) - c |= (data[i] >> 6) & 0x03; - - ret.append(1, Base64Table[c]); - } - else - { - ++i; - ret.append(1, fillchar); - } - - if (i < len) - { - c = data[i] & 0x3f; - ret.append(1, Base64Table[c]); - } - else - { - ret.append(1, fillchar); - } - } - - return(ret); -} - -string Base64::decode(const string& data) -{ - string::size_type i; - char c; - char c1; - string::size_type len = data.length(); - string ret; - - ret.reserve(len); - - for (i = 0; i < len; ++i) - { - c = static_cast(DecodeTable[static_cast(data[i])]); - ++i; - c1 = static_cast(DecodeTable[static_cast(data[i])]); - c = (c << 2) | ((c1 >> 4) & 0x3); - ret.append(1, c); - if (++i < len) - { - c = data[i]; - if (fillchar == c) - break; - - c = static_cast(DecodeTable[static_cast(data[i])]); - c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); - ret.append(1, c1); - } - - if (++i < len) - { - c1 = data[i]; - if (fillchar == c1) - break; - - c1 = static_cast(DecodeTable[static_cast(data[i])]); - c = ((c << 6) & 0xc0) | c1; - ret.append(1, c); - } - } - - return(ret); -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cpp new file mode 100644 index 00000000..e0ec1b90 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cpp @@ -0,0 +1,194 @@ + +//********************************************************************* +//* Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//* +//* Enhancements by Stanley Yamane: +//* o reverse lookup table for the decode function +//* o reserve string buffer space in advance +//* +//********************************************************************* + +#include "talk/base/base64.h" + +using namespace std; + +static const char fillchar = '='; +static const string::size_type np = string::npos; + +const string Base64::Base64Table( + // 0000000000111111111122222222223333333333444444444455555555556666 + // 0123456789012345678901234567890123456789012345678901234567890123 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + +// Decode Table gives the index of any valid base64 character in the Base64 table] +// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == / + + // 0 1 2 3 4 5 6 7 8 9 +const string::size_type Base64::DecodeTable[] = { + np,np,np,np,np,np,np,np,np,np, // 0 - 9 + np,np,np,np,np,np,np,np,np,np, //10 -19 + np,np,np,np,np,np,np,np,np,np, //20 -29 + np,np,np,np,np,np,np,np,np,np, //30 -39 + np,np,np,62,np,np,np,63,52,53, //40 -49 + 54,55,56,57,58,59,60,61,np,np, //50 -59 + np,np,np,np,np, 0, 1, 2, 3, 4, //60 -69 + 5, 6, 7, 8, 9,10,11,12,13,14, //70 -79 + 15,16,17,18,19,20,21,22,23,24, //80 -89 + 25,np,np,np,np,np,np,26,27,28, //90 -99 + 29,30,31,32,33,34,35,36,37,38, //100 -109 + 39,40,41,42,43,44,45,46,47,48, //110 -119 + 49,50,51,np,np,np,np,np,np,np, //120 -129 + np,np,np,np,np,np,np,np,np,np, //130 -139 + np,np,np,np,np,np,np,np,np,np, //140 -149 + np,np,np,np,np,np,np,np,np,np, //150 -159 + np,np,np,np,np,np,np,np,np,np, //160 -169 + np,np,np,np,np,np,np,np,np,np, //170 -179 + np,np,np,np,np,np,np,np,np,np, //180 -189 + np,np,np,np,np,np,np,np,np,np, //190 -199 + np,np,np,np,np,np,np,np,np,np, //200 -209 + np,np,np,np,np,np,np,np,np,np, //210 -219 + np,np,np,np,np,np,np,np,np,np, //220 -229 + np,np,np,np,np,np,np,np,np,np, //230 -239 + np,np,np,np,np,np,np,np,np,np, //240 -249 + np,np,np,np,np,np //250 -256 +}; + +string Base64::encodeFromArray(const char * data, size_t len) { + size_t i; + char c; + string ret; + + ret.reserve(len * 2); + + for (i = 0; i < len; ++i) + { + c = (data[i] >> 2) & 0x3f; + ret.append(1, Base64Table[c]); + c = (data[i] << 4) & 0x3f; + if (++i < len) + c |= (data[i] >> 4) & 0x0f; + + ret.append(1, Base64Table[c]); + if (i < len) + { + c = (data[i] << 2) & 0x3f; + if (++i < len) + c |= (data[i] >> 6) & 0x03; + + ret.append(1, Base64Table[c]); + } + else + { + ++i; + ret.append(1, fillchar); + } + + if (i < len) + { + c = data[i] & 0x3f; + ret.append(1, Base64Table[c]); + } + else + { + ret.append(1, fillchar); + } + } + + return(ret); +} + + +string Base64::encode(const string& data) +{ + string::size_type i; + char c; + string::size_type len = data.length(); + string ret; + + ret.reserve(len * 2); + + for (i = 0; i < len; ++i) + { + c = (data[i] >> 2) & 0x3f; + ret.append(1, Base64Table[c]); + c = (data[i] << 4) & 0x3f; + if (++i < len) + c |= (data[i] >> 4) & 0x0f; + + ret.append(1, Base64Table[c]); + if (i < len) + { + c = (data[i] << 2) & 0x3f; + if (++i < len) + c |= (data[i] >> 6) & 0x03; + + ret.append(1, Base64Table[c]); + } + else + { + ++i; + ret.append(1, fillchar); + } + + if (i < len) + { + c = data[i] & 0x3f; + ret.append(1, Base64Table[c]); + } + else + { + ret.append(1, fillchar); + } + } + + return(ret); +} + +string Base64::decode(const string& data) +{ + string::size_type i; + char c; + char c1; + string::size_type len = data.length(); + string ret; + + ret.reserve(len); + + for (i = 0; i < len; ++i) + { + c = static_cast(DecodeTable[static_cast(data[i])]); + ++i; + c1 = static_cast(DecodeTable[static_cast(data[i])]); + c = (c << 2) | ((c1 >> 4) & 0x3); + ret.append(1, c); + if (++i < len) + { + c = data[i]; + if (fillchar == c) + break; + + c = static_cast(DecodeTable[static_cast(data[i])]); + c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); + ret.append(1, c1); + } + + if (++i < len) + { + c1 = data[i]; + if (fillchar == c1) + break; + + c1 = static_cast(DecodeTable[static_cast(data[i])]); + c = ((c << 6) & 0xc0) | c1; + ret.append(1, c); + } + } + + return(ret); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc deleted file mode 100644 index e3af08b7..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/basictypes.h" -#include "talk/base/bytebuffer.h" -#include "talk/base/byteorder.h" -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::memcpy; -} -#endif - -namespace cricket { - -const int DEFAULT_SIZE = 4096; - -ByteBuffer::ByteBuffer() { - start_ = 0; - end_ = 0; - size_ = DEFAULT_SIZE; - bytes_ = new char[size_]; -} - -ByteBuffer::ByteBuffer(const char* bytes, size_t len) { - start_ = 0; - end_ = len; - size_ = len; - bytes_ = new char[size_]; - memcpy(bytes_, bytes, end_); -} - -ByteBuffer::ByteBuffer(const char* bytes) { - start_ = 0; - end_ = strlen(bytes); - size_ = end_; - bytes_ = new char[size_]; - memcpy(bytes_, bytes, end_); -} - -ByteBuffer::~ByteBuffer() { - delete bytes_; -} - -bool ByteBuffer::ReadUInt8(uint8& val) { - return ReadBytes(reinterpret_cast(&val), 1); -} - -bool ByteBuffer::ReadUInt16(uint16& val) { - uint16 v; - if (!ReadBytes(reinterpret_cast(&v), 2)) { - return false; - } else { - val = NetworkToHost16(v); - return true; - } -} - -bool ByteBuffer::ReadUInt32(uint32& val) { - uint32 v; - if (!ReadBytes(reinterpret_cast(&v), 4)) { - return false; - } else { - val = NetworkToHost32(v); - return true; - } -} - -bool ByteBuffer::ReadString(std::string& val, size_t len) { - if (len > Length()) { - return false; - } else { - val.append(bytes_ + start_, len); - start_ += len; - return true; - } -} - -bool ByteBuffer::ReadBytes(char* val, size_t len) { - if (len > Length()) { - return false; - } else { - memcpy(val, bytes_ + start_, len); - start_ += len; - return true; - } -} - -void ByteBuffer::WriteUInt8(uint8 val) { - WriteBytes(reinterpret_cast(&val), 1); -} - -void ByteBuffer::WriteUInt16(uint16 val) { - uint16 v = HostToNetwork16(val); - WriteBytes(reinterpret_cast(&v), 2); -} - -void ByteBuffer::WriteUInt32(uint32 val) { - uint32 v = HostToNetwork32(val); - WriteBytes(reinterpret_cast(&v), 4); -} - -void ByteBuffer::WriteString(const std::string& val) { - WriteBytes(val.c_str(), val.size()); -} - -void ByteBuffer::WriteBytes(const char* val, size_t len) { - if (Length() + len > Capacity()) - Resize(Length() + len); - - memcpy(bytes_ + end_, val, len); - end_ += len; -} - -void ByteBuffer::Resize(size_t size) { - if (size > size_) - size = _max(size, 3 * size_ / 2); - - size_t len = _min(end_ - start_, size); - char* new_bytes = new char[size]; - memcpy(new_bytes, bytes_ + start_, len); - delete [] bytes_; - - start_ = 0; - end_ = len; - size_ = size; - bytes_ = new_bytes; -} - -void ByteBuffer::Shift(size_t size) { - if (size > Length()) - return; - - end_ = Length() - size; - memmove(bytes_, bytes_ + start_ + size, end_); - start_ = 0; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cpp new file mode 100644 index 00000000..e3af08b7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cpp @@ -0,0 +1,166 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/basictypes.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/byteorder.h" +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcpy; +} +#endif + +namespace cricket { + +const int DEFAULT_SIZE = 4096; + +ByteBuffer::ByteBuffer() { + start_ = 0; + end_ = 0; + size_ = DEFAULT_SIZE; + bytes_ = new char[size_]; +} + +ByteBuffer::ByteBuffer(const char* bytes, size_t len) { + start_ = 0; + end_ = len; + size_ = len; + bytes_ = new char[size_]; + memcpy(bytes_, bytes, end_); +} + +ByteBuffer::ByteBuffer(const char* bytes) { + start_ = 0; + end_ = strlen(bytes); + size_ = end_; + bytes_ = new char[size_]; + memcpy(bytes_, bytes, end_); +} + +ByteBuffer::~ByteBuffer() { + delete bytes_; +} + +bool ByteBuffer::ReadUInt8(uint8& val) { + return ReadBytes(reinterpret_cast(&val), 1); +} + +bool ByteBuffer::ReadUInt16(uint16& val) { + uint16 v; + if (!ReadBytes(reinterpret_cast(&v), 2)) { + return false; + } else { + val = NetworkToHost16(v); + return true; + } +} + +bool ByteBuffer::ReadUInt32(uint32& val) { + uint32 v; + if (!ReadBytes(reinterpret_cast(&v), 4)) { + return false; + } else { + val = NetworkToHost32(v); + return true; + } +} + +bool ByteBuffer::ReadString(std::string& val, size_t len) { + if (len > Length()) { + return false; + } else { + val.append(bytes_ + start_, len); + start_ += len; + return true; + } +} + +bool ByteBuffer::ReadBytes(char* val, size_t len) { + if (len > Length()) { + return false; + } else { + memcpy(val, bytes_ + start_, len); + start_ += len; + return true; + } +} + +void ByteBuffer::WriteUInt8(uint8 val) { + WriteBytes(reinterpret_cast(&val), 1); +} + +void ByteBuffer::WriteUInt16(uint16 val) { + uint16 v = HostToNetwork16(val); + WriteBytes(reinterpret_cast(&v), 2); +} + +void ByteBuffer::WriteUInt32(uint32 val) { + uint32 v = HostToNetwork32(val); + WriteBytes(reinterpret_cast(&v), 4); +} + +void ByteBuffer::WriteString(const std::string& val) { + WriteBytes(val.c_str(), val.size()); +} + +void ByteBuffer::WriteBytes(const char* val, size_t len) { + if (Length() + len > Capacity()) + Resize(Length() + len); + + memcpy(bytes_ + end_, val, len); + end_ += len; +} + +void ByteBuffer::Resize(size_t size) { + if (size > size_) + size = _max(size, 3 * size_ / 2); + + size_t len = _min(end_ - start_, size); + char* new_bytes = new char[size]; + memcpy(new_bytes, bytes_ + start_, len); + delete [] bytes_; + + start_ = 0; + end_ = len; + size_ = size; + bytes_ = new_bytes; +} + +void ByteBuffer::Shift(size_t size) { + if (size > Length()) + return; + + end_ = Length() - size; + memmove(bytes_, bytes_ + start_ + size, end_); + start_ = 0; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h index b21be2f1..b5947037 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h @@ -214,7 +214,7 @@ inline void operator delete[](void * ptr, const char * fname, int line) { buzz:: #endif // TRACK_ARRAY_ALLOC_PROBLEM -// If you put "#define new TRACK_NEW" in your .cc file after all includes, it should track the calling function name +// If you put "#define new TRACK_NEW" in your .cpp file after all includes, it should track the calling function name #define TRACK_NEW new(__FILE__,__LINE__) #define TRACK_DEL delete(__FILE__,__LINE__) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc deleted file mode 100644 index f604050f..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" -#include "talk/base/logging.h" -#include "talk/base/network.h" -#include "talk/base/socket.h" -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; - using ::exit; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace { - -void FatalError(const std::string& name, int err) { - PLOG(LERROR, err) << name; - std::exit(1); -} - -} - -namespace cricket { - -#ifdef POSIX -std::string GetHostName() { - struct utsname nm; - if (uname(&nm) < 0) - FatalError("uname", errno); - return std::string(nm.nodename); -} -#endif - -#ifdef WIN32 -std::string GetHostName() { - // TODO: fix this - return "cricket"; -} -#endif - -// Records information about the local host. -Host* gLocalHost = 0; - -const Host& LocalHost() { - if (!gLocalHost) { - std::vector* networks = new std::vector; - NetworkManager::CreateNetworks(*networks); -#ifdef WIN32 - // This is sort of problematic... one part of the code (the unittests) wants - // 127.0.0.1 to be present and another part (port allocators) don't. Right - // now, they use different APIs, so we can have different behavior. But - // there is something wrong with this. - networks->push_back(new Network("localhost", - SocketAddress::StringToIP("127.0.0.1"))); -#endif - gLocalHost = new Host(GetHostName(), networks); - assert(gLocalHost->networks().size() > 0); - } - - return *gLocalHost; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cpp new file mode 100644 index 00000000..f604050f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cpp @@ -0,0 +1,101 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/base/network.h" +#include "talk/base/socket.h" +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; + using ::exit; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace { + +void FatalError(const std::string& name, int err) { + PLOG(LERROR, err) << name; + std::exit(1); +} + +} + +namespace cricket { + +#ifdef POSIX +std::string GetHostName() { + struct utsname nm; + if (uname(&nm) < 0) + FatalError("uname", errno); + return std::string(nm.nodename); +} +#endif + +#ifdef WIN32 +std::string GetHostName() { + // TODO: fix this + return "cricket"; +} +#endif + +// Records information about the local host. +Host* gLocalHost = 0; + +const Host& LocalHost() { + if (!gLocalHost) { + std::vector* networks = new std::vector; + NetworkManager::CreateNetworks(*networks); +#ifdef WIN32 + // This is sort of problematic... one part of the code (the unittests) wants + // 127.0.0.1 to be present and another part (port allocators) don't. Right + // now, they use different APIs, so we can have different behavior. But + // there is something wrong with this. + networks->push_back(new Network("localhost", + SocketAddress::StringToIP("127.0.0.1"))); +#endif + gLocalHost = new Host(GetHostName(), networks); + assert(gLocalHost->networks().size() > 0); + } + + return *gLocalHost; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc deleted file mode 100644 index 5befe9fd..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/jtime.h" -#include -#include -#include - -namespace cricket { - -#ifdef POSIX -#include -uint32 Time() { - struct timeval tv; - gettimeofday(&tv, 0); - return tv.tv_sec * 1000 + tv.tv_usec / 1000; -} -#endif - -#ifdef WIN32 -#include -uint32 Time() { - return GetTickCount(); -} -#endif - -bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier) { - if (earlier <= later) { - return ((earlier <= middle) && (middle <= later)); - } else { - return !((later < middle) && (middle < earlier)); - } -} - -int32 TimeDiff(uint32 later, uint32 earlier) { - uint32 LAST = 0xFFFFFFFF; - uint32 HALF = 0x80000000; - if (TimeIsBetween(earlier + HALF, later, earlier)) { - if (earlier <= later) { - return static_cast(later - earlier); - } else { - return static_cast(later + (LAST - earlier) + 1); - } - } else { - if (later <= earlier) { - return -static_cast(earlier - later); - } else { - return -static_cast(earlier + (LAST - later) + 1); - } - } -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cpp new file mode 100644 index 00000000..5befe9fd --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cpp @@ -0,0 +1,77 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/jtime.h" +#include +#include +#include + +namespace cricket { + +#ifdef POSIX +#include +uint32 Time() { + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} +#endif + +#ifdef WIN32 +#include +uint32 Time() { + return GetTickCount(); +} +#endif + +bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier) { + if (earlier <= later) { + return ((earlier <= middle) && (middle <= later)); + } else { + return !((later < middle) && (middle < earlier)); + } +} + +int32 TimeDiff(uint32 later, uint32 earlier) { + uint32 LAST = 0xFFFFFFFF; + uint32 HALF = 0x80000000; + if (TimeIsBetween(earlier + HALF, later, earlier)) { + if (earlier <= later) { + return static_cast(later - earlier); + } else { + return static_cast(later + (LAST - earlier) + 1); + } + } else { + if (later <= earlier) { + return -static_cast(earlier - later); + } else { + return -static_cast(earlier + (LAST - later) + 1); + } + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc deleted file mode 100644 index f10489f7..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc +++ /dev/null @@ -1,321 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/messagequeue.h" -#include "talk/base/physicalsocketserver.h" - -#ifdef POSIX -extern "C" { -#include -} -#endif - -namespace cricket { - -//------------------------------------------------------------------ -// MessageQueueManager - -MessageQueueManager* MessageQueueManager::instance_; - -MessageQueueManager* MessageQueueManager::Instance() { - // Note: This is not thread safe, but it is first called before threads are - // spawned. - if (!instance_) - instance_ = new MessageQueueManager; - return instance_; -} - -MessageQueueManager::MessageQueueManager() { -} - -MessageQueueManager::~MessageQueueManager() { -} - -void MessageQueueManager::Add(MessageQueue *message_queue) { - CritScope cs(&crit_); - message_queues_.push_back(message_queue); -} - -void MessageQueueManager::Remove(MessageQueue *message_queue) { - CritScope cs(&crit_); - std::vector::iterator iter; - iter = std::find(message_queues_.begin(), message_queues_.end(), message_queue); - if (iter != message_queues_.end()) - message_queues_.erase(iter); -} - -void MessageQueueManager::Clear(MessageHandler *handler) { - CritScope cs(&crit_); - std::vector::iterator iter; - for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++) - (*iter)->Clear(handler); -} - -//------------------------------------------------------------------ -// MessageQueue - -MessageQueue::MessageQueue(SocketServer* ss) - : ss_(ss), new_ss(false), fStop_(false), fPeekKeep_(false) { - if (!ss_) { - new_ss = true; - ss_ = new PhysicalSocketServer(); - } - MessageQueueManager::Instance()->Add(this); -} - -MessageQueue::~MessageQueue() { - Clear(NULL); - if (new_ss) - delete ss_; - MessageQueueManager::Instance()->Remove(this); -} - -void MessageQueue::set_socketserver(SocketServer* ss) { - if (new_ss) - delete ss_; - new_ss = false; - ss_ = ss; -} - -void MessageQueue::Stop() { - fStop_ = true; - ss_->WakeUp(); -} - -bool MessageQueue::IsStopping() { - return fStop_; -} - -void MessageQueue::Restart() { - fStop_ = false; -} - -bool MessageQueue::Peek(Message *pmsg, int cmsWait) { - if (fStop_) - return false; - if (fPeekKeep_) { - *pmsg = msgPeek_; - return true; - } - if (!Get(pmsg, cmsWait)) - return false; - msgPeek_ = *pmsg; - fPeekKeep_ = true; - return true; -} - -bool MessageQueue::Get(Message *pmsg, int cmsWait) { - // Force stopping - - if (fStop_) - return false; - - // Return and clear peek if present - // Always return the peek if it exists so there is Peek/Get symmetry - - if (fPeekKeep_) { - *pmsg = msgPeek_; - fPeekKeep_ = false; - return true; - } - - // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch - - int cmsTotal = cmsWait; - int cmsElapsed = 0; - uint32 msStart = GetMillisecondCount(); - uint32 msCurrent = msStart; - while (!fStop_) { - // Check for sent messages - - ReceiveSends(); - - // Check queues - - int cmsDelayNext = -1; - { - CritScope cs(&crit_); - - // Check for delayed messages that have been triggered - // Calc the next trigger too - - while (!dmsgq_.empty()) { - if (msCurrent < dmsgq_.top().msTrigger_) { - cmsDelayNext = dmsgq_.top().msTrigger_ - msCurrent; - break; - } - msgq_.push(dmsgq_.top().msg_); - dmsgq_.pop(); - } - - // Check for posted events - - if (!msgq_.empty()) { - *pmsg = msgq_.front(); - msgq_.pop(); - return true; - } - } - - // Which is shorter, the delay wait or the asked wait? - - int cmsNext; - if (cmsWait == -1) { - cmsNext = cmsDelayNext; - } else { - cmsNext = cmsTotal - cmsElapsed; - if (cmsNext < 0) - cmsNext = 0; - if (cmsDelayNext != -1 && cmsDelayNext < cmsNext) - cmsNext = cmsDelayNext; - } - - // Wait and multiplex in the meantime - ss_->Wait(cmsNext, true); - - // If the specified timeout expired, return - - msCurrent = GetMillisecondCount(); - cmsElapsed = msCurrent - msStart; - if (cmsWait != -1) { - if (cmsElapsed >= cmsWait) - return false; - } - } - return false; -} - -void MessageQueue::ReceiveSends() { -} - -void MessageQueue::Post(MessageHandler *phandler, uint32 id, - MessageData *pdata) { - // Keep thread safe - // Add the message to the end of the queue - // Signal for the multiplexer to return - - CritScope cs(&crit_); - Message msg; - msg.phandler = phandler; - msg.message_id = id; - msg.pdata = pdata; - msgq_.push(msg); - ss_->WakeUp(); -} - -void MessageQueue::PostDelayed(int cmsDelay, MessageHandler *phandler, - uint32 id, MessageData *pdata) { - // Keep thread safe - // Add to the priority queue. Gets sorted soonest first. - // Signal for the multiplexer to return. - - CritScope cs(&crit_); - Message msg; - msg.phandler = phandler; - msg.message_id = id; - msg.pdata = pdata; - dmsgq_.push(DelayedMessage(cmsDelay, &msg)); - ss_->WakeUp(); -} - -int MessageQueue::GetDelay() { - CritScope cs(&crit_); - - if (!msgq_.empty()) - return 0; - - if (!dmsgq_.empty()) { - int delay = dmsgq_.top().msTrigger_ - GetMillisecondCount(); - if (delay < 0) - delay = 0; - return delay; - } - - return -1; -} - -void MessageQueue::Clear(MessageHandler *phandler, uint32 id) { - CritScope cs(&crit_); - - // Remove messages with phandler - - if (fPeekKeep_) { - if (phandler == NULL || msgPeek_.phandler == phandler) { - if (id == (uint32)-1 || msgPeek_.message_id == id) { - delete msgPeek_.pdata; - fPeekKeep_ = false; - } - } - } - - // Remove from ordered message queue - - size_t c = msgq_.size(); - while (c-- != 0) { - Message msg = msgq_.front(); - msgq_.pop(); - if (phandler != NULL && msg.phandler != phandler) { - msgq_.push(msg); - } else { - if (id == (uint32)-1 || msg.message_id == id) { - delete msg.pdata; - } else { - msgq_.push(msg); - } - } - } - - // Remove from priority queue. Not directly iterable, so use this approach - - std::queue dmsgs; - while (!dmsgq_.empty()) { - DelayedMessage dmsg = dmsgq_.top(); - dmsgq_.pop(); - if (phandler != NULL && dmsg.msg_.phandler != phandler) { - dmsgs.push(dmsg); - } else { - if (id == (uint32)-1 || dmsg.msg_.message_id == id) { - delete dmsg.msg_.pdata; - } else { - dmsgs.push(dmsg); - } - } - } - while (!dmsgs.empty()) { - dmsgq_.push(dmsgs.front()); - dmsgs.pop(); - } -} - -void MessageQueue::Dispatch(Message *pmsg) { - pmsg->phandler->OnMessage(pmsg); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cpp new file mode 100644 index 00000000..f10489f7 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cpp @@ -0,0 +1,321 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/messagequeue.h" +#include "talk/base/physicalsocketserver.h" + +#ifdef POSIX +extern "C" { +#include +} +#endif + +namespace cricket { + +//------------------------------------------------------------------ +// MessageQueueManager + +MessageQueueManager* MessageQueueManager::instance_; + +MessageQueueManager* MessageQueueManager::Instance() { + // Note: This is not thread safe, but it is first called before threads are + // spawned. + if (!instance_) + instance_ = new MessageQueueManager; + return instance_; +} + +MessageQueueManager::MessageQueueManager() { +} + +MessageQueueManager::~MessageQueueManager() { +} + +void MessageQueueManager::Add(MessageQueue *message_queue) { + CritScope cs(&crit_); + message_queues_.push_back(message_queue); +} + +void MessageQueueManager::Remove(MessageQueue *message_queue) { + CritScope cs(&crit_); + std::vector::iterator iter; + iter = std::find(message_queues_.begin(), message_queues_.end(), message_queue); + if (iter != message_queues_.end()) + message_queues_.erase(iter); +} + +void MessageQueueManager::Clear(MessageHandler *handler) { + CritScope cs(&crit_); + std::vector::iterator iter; + for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++) + (*iter)->Clear(handler); +} + +//------------------------------------------------------------------ +// MessageQueue + +MessageQueue::MessageQueue(SocketServer* ss) + : ss_(ss), new_ss(false), fStop_(false), fPeekKeep_(false) { + if (!ss_) { + new_ss = true; + ss_ = new PhysicalSocketServer(); + } + MessageQueueManager::Instance()->Add(this); +} + +MessageQueue::~MessageQueue() { + Clear(NULL); + if (new_ss) + delete ss_; + MessageQueueManager::Instance()->Remove(this); +} + +void MessageQueue::set_socketserver(SocketServer* ss) { + if (new_ss) + delete ss_; + new_ss = false; + ss_ = ss; +} + +void MessageQueue::Stop() { + fStop_ = true; + ss_->WakeUp(); +} + +bool MessageQueue::IsStopping() { + return fStop_; +} + +void MessageQueue::Restart() { + fStop_ = false; +} + +bool MessageQueue::Peek(Message *pmsg, int cmsWait) { + if (fStop_) + return false; + if (fPeekKeep_) { + *pmsg = msgPeek_; + return true; + } + if (!Get(pmsg, cmsWait)) + return false; + msgPeek_ = *pmsg; + fPeekKeep_ = true; + return true; +} + +bool MessageQueue::Get(Message *pmsg, int cmsWait) { + // Force stopping + + if (fStop_) + return false; + + // Return and clear peek if present + // Always return the peek if it exists so there is Peek/Get symmetry + + if (fPeekKeep_) { + *pmsg = msgPeek_; + fPeekKeep_ = false; + return true; + } + + // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch + + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = GetMillisecondCount(); + uint32 msCurrent = msStart; + while (!fStop_) { + // Check for sent messages + + ReceiveSends(); + + // Check queues + + int cmsDelayNext = -1; + { + CritScope cs(&crit_); + + // Check for delayed messages that have been triggered + // Calc the next trigger too + + while (!dmsgq_.empty()) { + if (msCurrent < dmsgq_.top().msTrigger_) { + cmsDelayNext = dmsgq_.top().msTrigger_ - msCurrent; + break; + } + msgq_.push(dmsgq_.top().msg_); + dmsgq_.pop(); + } + + // Check for posted events + + if (!msgq_.empty()) { + *pmsg = msgq_.front(); + msgq_.pop(); + return true; + } + } + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == -1) { + cmsNext = cmsDelayNext; + } else { + cmsNext = cmsTotal - cmsElapsed; + if (cmsNext < 0) + cmsNext = 0; + if (cmsDelayNext != -1 && cmsDelayNext < cmsNext) + cmsNext = cmsDelayNext; + } + + // Wait and multiplex in the meantime + ss_->Wait(cmsNext, true); + + // If the specified timeout expired, return + + msCurrent = GetMillisecondCount(); + cmsElapsed = msCurrent - msStart; + if (cmsWait != -1) { + if (cmsElapsed >= cmsWait) + return false; + } + } + return false; +} + +void MessageQueue::ReceiveSends() { +} + +void MessageQueue::Post(MessageHandler *phandler, uint32 id, + MessageData *pdata) { + // Keep thread safe + // Add the message to the end of the queue + // Signal for the multiplexer to return + + CritScope cs(&crit_); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + msgq_.push(msg); + ss_->WakeUp(); +} + +void MessageQueue::PostDelayed(int cmsDelay, MessageHandler *phandler, + uint32 id, MessageData *pdata) { + // Keep thread safe + // Add to the priority queue. Gets sorted soonest first. + // Signal for the multiplexer to return. + + CritScope cs(&crit_); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + dmsgq_.push(DelayedMessage(cmsDelay, &msg)); + ss_->WakeUp(); +} + +int MessageQueue::GetDelay() { + CritScope cs(&crit_); + + if (!msgq_.empty()) + return 0; + + if (!dmsgq_.empty()) { + int delay = dmsgq_.top().msTrigger_ - GetMillisecondCount(); + if (delay < 0) + delay = 0; + return delay; + } + + return -1; +} + +void MessageQueue::Clear(MessageHandler *phandler, uint32 id) { + CritScope cs(&crit_); + + // Remove messages with phandler + + if (fPeekKeep_) { + if (phandler == NULL || msgPeek_.phandler == phandler) { + if (id == (uint32)-1 || msgPeek_.message_id == id) { + delete msgPeek_.pdata; + fPeekKeep_ = false; + } + } + } + + // Remove from ordered message queue + + size_t c = msgq_.size(); + while (c-- != 0) { + Message msg = msgq_.front(); + msgq_.pop(); + if (phandler != NULL && msg.phandler != phandler) { + msgq_.push(msg); + } else { + if (id == (uint32)-1 || msg.message_id == id) { + delete msg.pdata; + } else { + msgq_.push(msg); + } + } + } + + // Remove from priority queue. Not directly iterable, so use this approach + + std::queue dmsgs; + while (!dmsgq_.empty()) { + DelayedMessage dmsg = dmsgq_.top(); + dmsgq_.pop(); + if (phandler != NULL && dmsg.msg_.phandler != phandler) { + dmsgs.push(dmsg); + } else { + if (id == (uint32)-1 || dmsg.msg_.message_id == id) { + delete dmsg.msg_.pdata; + } else { + dmsgs.push(dmsg); + } + } + } + while (!dmsgs.empty()) { + dmsgq_.push(dmsgs.front()); + dmsgs.pop(); + } +} + +void MessageQueue::Dispatch(Message *pmsg) { + pmsg->phandler->OnMessage(pmsg); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc deleted file mode 100644 index 21b3a08f..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc +++ /dev/null @@ -1,382 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" -#include "talk/base/logging.h" -#include "talk/base/network.h" -#include "talk/base/socket.h" // this includes something that makes windows happy -#include "talk/base/jtime.h" -#include "talk/base/basicdefs.h" - -#include -#include -#include -#include -#include - -#ifdef POSIX -extern "C" { -#include -#include -#include -#include -#include -} -#endif // POSIX - -#ifdef WIN32 -#include -#endif - -namespace { - -const double kAlpha = 0.5; // weight for data infinitely far in the past -const double kHalfLife = 2000; // half life of exponential decay (in ms) -const double kLog2 = 0.693147180559945309417; -const double kLambda = kLog2 / kHalfLife; - -// assume so-so quality unless data says otherwise -const double kDefaultQuality = cricket::QUALITY_FAIR; - -typedef std::map StrMap; - -void BuildMap(const StrMap& map, std::string& str) { - str.append("{"); - bool first = true; - for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) { - if (!first) str.append(","); - str.append(i->first); - str.append("="); - str.append(i->second); - first = false; - } - str.append("}"); -} - -void ParseCheck(std::istringstream& ist, char ch) { - if (ist.get() != ch) - LOG(LERROR) << "Expecting '" << ch << "'"; -} - -std::string ParseString(std::istringstream& ist) { - std::string str; - int count = 0; - while (ist) { - char ch = ist.peek(); - if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) { - break; - } else if (ch == '{') { - count += 1; - } else if (ch == '}') { - count -= 1; - if (count < 0) - LOG(LERROR) << "mismatched '{' and '}'"; - } - str.append(1, static_cast(ist.get())); - } - return str; -} - -void ParseMap(const std::string& str, StrMap& map) { - if (str.size() == 0) - return; - std::istringstream ist(str); - ParseCheck(ist, '{'); - for (;;) { - std::string key = ParseString(ist); - ParseCheck(ist, '='); - std::string val = ParseString(ist); - map[key] = val; - if (ist.peek() == ',') - ist.get(); - else - break; - } - ParseCheck(ist, '}'); - if (ist.rdbuf()->in_avail() != 0) - LOG(LERROR) << "Unexpected characters at end"; -} - -#if 0 -const std::string TEST_MAP0_IN = ""; -const std::string TEST_MAP0_OUT = "{}"; -const std::string TEST_MAP1 = "{a=12345}"; -const std::string TEST_MAP2 = "{a=12345,b=67890}"; -const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}"; -const std::string TEST_MAP4 = "{a={d=12345,e=67890}}"; -const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}"; -const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}"; -const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}"; - -class MyTest { -public: - MyTest() { - test(TEST_MAP0_IN, TEST_MAP0_OUT); - test(TEST_MAP1, TEST_MAP1); - test(TEST_MAP2, TEST_MAP2); - test(TEST_MAP3, TEST_MAP3); - test(TEST_MAP4, TEST_MAP4); - test(TEST_MAP5, TEST_MAP5); - test(TEST_MAP6, TEST_MAP6); - test(TEST_MAP7, TEST_MAP7); - } - void test(const std::string& input, const std::string& exp_output) { - StrMap map; - ParseMap(input, map); - std::string output; - BuildMap(map, output); - LOG(INFO) << " ******** " << (output == exp_output); - } -}; - -static MyTest myTest; -#endif - -template -std::string ToString(T val) { - std::ostringstream ost; - ost << val; - return ost.str(); -} - -template -T FromString(std::string str) { - std::istringstream ist(str); - T val; - ist >> val; - return val; -} - -} - -namespace cricket { - -#ifdef POSIX -void NetworkManager::CreateNetworks(std::vector& networks) { - int fd; - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - PLOG(LERROR, errno) << "socket"; - return; - } - - struct ifconf ifc; - ifc.ifc_len = 64 * sizeof(struct ifreq); - ifc.ifc_buf = new char[ifc.ifc_len]; - - if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { - PLOG(LERROR, errno) << "ioctl"; - return; - } - assert(ifc.ifc_len < static_cast(64 * sizeof(struct ifreq))); - - struct ifreq* ptr = reinterpret_cast(ifc.ifc_buf); - struct ifreq* end = - reinterpret_cast(ifc.ifc_buf + ifc.ifc_len); - - while (ptr < end) { - struct sockaddr_in* inaddr = - reinterpret_cast(&ptr->ifr_ifru.ifru_addr); - if (inaddr->sin_family == AF_INET) { - uint32 ip = ntohl(inaddr->sin_addr.s_addr); - networks.push_back(new Network(std::string(ptr->ifr_name), ip)); - } -#ifdef _SIZEOF_ADDR_IFREQ - ptr = reinterpret_cast( - reinterpret_cast(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); -#else - ptr++; -#endif - } - - delete [] ifc.ifc_buf; - close(fd); -} -#endif - -#ifdef WIN32 -void NetworkManager::CreateNetworks(std::vector& networks) { - IP_ADAPTER_INFO info_temp; - ULONG len = 0; - - if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW) - return; - IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len]; - if (GetAdaptersInfo(infos, &len) != NO_ERROR) - return; - - int count = 0; - for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) { - if (info->Type == MIB_IF_TYPE_LOOPBACK) - continue; - if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0) - continue; - - // In production, don't transmit the network name because of - // privacy concerns. Transmit a number instead. - - std::string name; -#if defined(PRODUCTION) - std::ostringstream ost; - ost << count; - name = ost.str(); - count++; -#else - name = info->Description; -#endif - - networks.push_back(new Network(name, - SocketAddress::StringToIP(info->IpAddressList.IpAddress.String))); - } - - delete infos; -} -#endif - -void NetworkManager::GetNetworks(std::vector& result) { - std::vector list; - CreateNetworks(list); - - for (uint32 i = 0; i < list.size(); ++i) { - NetworkMap::iterator iter = networks_.find(list[i]->name()); - - Network* network; - if (iter == networks_.end()) { - network = list[i]; - } else { - network = iter->second; - network->set_ip(list[i]->ip()); - delete list[i]; - } - - networks_[network->name()] = network; - result.push_back(network); - } -} - -std::string NetworkManager::GetState() { - StrMap map; - for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i) - map[i->first] = i->second->GetState(); - - std::string str; - BuildMap(map, str); - return str; -} - -void NetworkManager::SetState(std::string str) { - StrMap map; - ParseMap(str, map); - - for (StrMap::iterator i = map.begin(); i != map.end(); ++i) { - std::string name = i->first; - std::string state = i->second; - - Network* network = new Network(name, 0); - network->SetState(state); - networks_[name] = network; - } -} - -Network::Network(const std::string& name, uint32 ip) - : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0), - exponential_numerator_(0), exponential_denominator_(0), - quality_(kDefaultQuality) { - - last_data_time_ = Time(); - - // TODO: seed the historical data with one data point based on the link speed - // metric from XP (4.0 if < 50, 3.0 otherwise). -} - -void Network::StartSession(NetworkSession* session) { - assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end()); - sessions_.push_back(session); -} - -void Network::StopSession(NetworkSession* session) { - SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session); - if (iter != sessions_.end()) - sessions_.erase(iter); -} - -void Network::EstimateQuality() { - uint32 now = Time(); - - // Add new data points for the current time. - for (uint32 i = 0; i < sessions_.size(); ++i) { - if (sessions_[i]->HasQuality()) - AddDataPoint(now, sessions_[i]->GetCurrentQuality()); - } - - // Construct the weighted average using both uniform and exponential weights. - - double exp_shift = exp(-kLambda * (now - last_data_time_)); - double numerator = uniform_numerator_ + exp_shift * exponential_numerator_; - double denominator = uniform_denominator_ + exp_shift * exponential_denominator_; - - if (denominator < DBL_EPSILON) - quality_ = kDefaultQuality; - else - quality_ = numerator / denominator; -} - -void Network::AddDataPoint(uint32 time, double quality) { - uniform_numerator_ += kAlpha * quality; - uniform_denominator_ += kAlpha; - - double exp_shift = exp(-kLambda * (time - last_data_time_)); - exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_; - exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_; - - last_data_time_ = time; -} - -std::string Network::GetState() { - StrMap map; - map["lt"] = ToString(last_data_time_); - map["un"] = ToString(uniform_numerator_); - map["ud"] = ToString(uniform_denominator_); - map["en"] = ToString(exponential_numerator_); - map["ed"] = ToString(exponential_denominator_); - - std::string str; - BuildMap(map, str); - return str; -} - -void Network::SetState(std::string str) { - StrMap map; - ParseMap(str, map); - - last_data_time_ = FromString(map["lt"]); - uniform_numerator_ = FromString(map["un"]); - uniform_denominator_ = FromString(map["ud"]); - exponential_numerator_ = FromString(map["en"]); - exponential_denominator_ = FromString(map["ed"]); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp new file mode 100644 index 00000000..21b3a08f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp @@ -0,0 +1,382 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/base/network.h" +#include "talk/base/socket.h" // this includes something that makes windows happy +#include "talk/base/jtime.h" +#include "talk/base/basicdefs.h" + +#include +#include +#include +#include +#include + +#ifdef POSIX +extern "C" { +#include +#include +#include +#include +#include +} +#endif // POSIX + +#ifdef WIN32 +#include +#endif + +namespace { + +const double kAlpha = 0.5; // weight for data infinitely far in the past +const double kHalfLife = 2000; // half life of exponential decay (in ms) +const double kLog2 = 0.693147180559945309417; +const double kLambda = kLog2 / kHalfLife; + +// assume so-so quality unless data says otherwise +const double kDefaultQuality = cricket::QUALITY_FAIR; + +typedef std::map StrMap; + +void BuildMap(const StrMap& map, std::string& str) { + str.append("{"); + bool first = true; + for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) { + if (!first) str.append(","); + str.append(i->first); + str.append("="); + str.append(i->second); + first = false; + } + str.append("}"); +} + +void ParseCheck(std::istringstream& ist, char ch) { + if (ist.get() != ch) + LOG(LERROR) << "Expecting '" << ch << "'"; +} + +std::string ParseString(std::istringstream& ist) { + std::string str; + int count = 0; + while (ist) { + char ch = ist.peek(); + if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) { + break; + } else if (ch == '{') { + count += 1; + } else if (ch == '}') { + count -= 1; + if (count < 0) + LOG(LERROR) << "mismatched '{' and '}'"; + } + str.append(1, static_cast(ist.get())); + } + return str; +} + +void ParseMap(const std::string& str, StrMap& map) { + if (str.size() == 0) + return; + std::istringstream ist(str); + ParseCheck(ist, '{'); + for (;;) { + std::string key = ParseString(ist); + ParseCheck(ist, '='); + std::string val = ParseString(ist); + map[key] = val; + if (ist.peek() == ',') + ist.get(); + else + break; + } + ParseCheck(ist, '}'); + if (ist.rdbuf()->in_avail() != 0) + LOG(LERROR) << "Unexpected characters at end"; +} + +#if 0 +const std::string TEST_MAP0_IN = ""; +const std::string TEST_MAP0_OUT = "{}"; +const std::string TEST_MAP1 = "{a=12345}"; +const std::string TEST_MAP2 = "{a=12345,b=67890}"; +const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}"; +const std::string TEST_MAP4 = "{a={d=12345,e=67890}}"; +const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}"; +const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}"; +const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}"; + +class MyTest { +public: + MyTest() { + test(TEST_MAP0_IN, TEST_MAP0_OUT); + test(TEST_MAP1, TEST_MAP1); + test(TEST_MAP2, TEST_MAP2); + test(TEST_MAP3, TEST_MAP3); + test(TEST_MAP4, TEST_MAP4); + test(TEST_MAP5, TEST_MAP5); + test(TEST_MAP6, TEST_MAP6); + test(TEST_MAP7, TEST_MAP7); + } + void test(const std::string& input, const std::string& exp_output) { + StrMap map; + ParseMap(input, map); + std::string output; + BuildMap(map, output); + LOG(INFO) << " ******** " << (output == exp_output); + } +}; + +static MyTest myTest; +#endif + +template +std::string ToString(T val) { + std::ostringstream ost; + ost << val; + return ost.str(); +} + +template +T FromString(std::string str) { + std::istringstream ist(str); + T val; + ist >> val; + return val; +} + +} + +namespace cricket { + +#ifdef POSIX +void NetworkManager::CreateNetworks(std::vector& networks) { + int fd; + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + PLOG(LERROR, errno) << "socket"; + return; + } + + struct ifconf ifc; + ifc.ifc_len = 64 * sizeof(struct ifreq); + ifc.ifc_buf = new char[ifc.ifc_len]; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + PLOG(LERROR, errno) << "ioctl"; + return; + } + assert(ifc.ifc_len < static_cast(64 * sizeof(struct ifreq))); + + struct ifreq* ptr = reinterpret_cast(ifc.ifc_buf); + struct ifreq* end = + reinterpret_cast(ifc.ifc_buf + ifc.ifc_len); + + while (ptr < end) { + struct sockaddr_in* inaddr = + reinterpret_cast(&ptr->ifr_ifru.ifru_addr); + if (inaddr->sin_family == AF_INET) { + uint32 ip = ntohl(inaddr->sin_addr.s_addr); + networks.push_back(new Network(std::string(ptr->ifr_name), ip)); + } +#ifdef _SIZEOF_ADDR_IFREQ + ptr = reinterpret_cast( + reinterpret_cast(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); +#else + ptr++; +#endif + } + + delete [] ifc.ifc_buf; + close(fd); +} +#endif + +#ifdef WIN32 +void NetworkManager::CreateNetworks(std::vector& networks) { + IP_ADAPTER_INFO info_temp; + ULONG len = 0; + + if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW) + return; + IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len]; + if (GetAdaptersInfo(infos, &len) != NO_ERROR) + return; + + int count = 0; + for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) { + if (info->Type == MIB_IF_TYPE_LOOPBACK) + continue; + if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0) + continue; + + // In production, don't transmit the network name because of + // privacy concerns. Transmit a number instead. + + std::string name; +#if defined(PRODUCTION) + std::ostringstream ost; + ost << count; + name = ost.str(); + count++; +#else + name = info->Description; +#endif + + networks.push_back(new Network(name, + SocketAddress::StringToIP(info->IpAddressList.IpAddress.String))); + } + + delete infos; +} +#endif + +void NetworkManager::GetNetworks(std::vector& result) { + std::vector list; + CreateNetworks(list); + + for (uint32 i = 0; i < list.size(); ++i) { + NetworkMap::iterator iter = networks_.find(list[i]->name()); + + Network* network; + if (iter == networks_.end()) { + network = list[i]; + } else { + network = iter->second; + network->set_ip(list[i]->ip()); + delete list[i]; + } + + networks_[network->name()] = network; + result.push_back(network); + } +} + +std::string NetworkManager::GetState() { + StrMap map; + for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i) + map[i->first] = i->second->GetState(); + + std::string str; + BuildMap(map, str); + return str; +} + +void NetworkManager::SetState(std::string str) { + StrMap map; + ParseMap(str, map); + + for (StrMap::iterator i = map.begin(); i != map.end(); ++i) { + std::string name = i->first; + std::string state = i->second; + + Network* network = new Network(name, 0); + network->SetState(state); + networks_[name] = network; + } +} + +Network::Network(const std::string& name, uint32 ip) + : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0), + exponential_numerator_(0), exponential_denominator_(0), + quality_(kDefaultQuality) { + + last_data_time_ = Time(); + + // TODO: seed the historical data with one data point based on the link speed + // metric from XP (4.0 if < 50, 3.0 otherwise). +} + +void Network::StartSession(NetworkSession* session) { + assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end()); + sessions_.push_back(session); +} + +void Network::StopSession(NetworkSession* session) { + SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session); + if (iter != sessions_.end()) + sessions_.erase(iter); +} + +void Network::EstimateQuality() { + uint32 now = Time(); + + // Add new data points for the current time. + for (uint32 i = 0; i < sessions_.size(); ++i) { + if (sessions_[i]->HasQuality()) + AddDataPoint(now, sessions_[i]->GetCurrentQuality()); + } + + // Construct the weighted average using both uniform and exponential weights. + + double exp_shift = exp(-kLambda * (now - last_data_time_)); + double numerator = uniform_numerator_ + exp_shift * exponential_numerator_; + double denominator = uniform_denominator_ + exp_shift * exponential_denominator_; + + if (denominator < DBL_EPSILON) + quality_ = kDefaultQuality; + else + quality_ = numerator / denominator; +} + +void Network::AddDataPoint(uint32 time, double quality) { + uniform_numerator_ += kAlpha * quality; + uniform_denominator_ += kAlpha; + + double exp_shift = exp(-kLambda * (time - last_data_time_)); + exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_; + exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_; + + last_data_time_ = time; +} + +std::string Network::GetState() { + StrMap map; + map["lt"] = ToString(last_data_time_); + map["un"] = ToString(uniform_numerator_); + map["ud"] = ToString(uniform_denominator_); + map["en"] = ToString(exponential_numerator_); + map["ed"] = ToString(exponential_denominator_); + + std::string str; + BuildMap(map, str); + return str; +} + +void Network::SetState(std::string str) { + StrMap map; + ParseMap(str, map); + + last_data_time_ = FromString(map["lt"]); + uniform_numerator_ = FromString(map["un"]); + uniform_denominator_ = FromString(map["ud"]); + exponential_numerator_ = FromString(map["en"]); + exponential_denominator_ = FromString(map["ed"]); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc deleted file mode 100644 index 37836302..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc +++ /dev/null @@ -1,1116 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif - -#include -#include - -#ifdef POSIX -extern "C" { -#include -#include -#include -#include -#include -} -#endif - -#include "talk/base/basictypes.h" -#include "talk/base/byteorder.h" -#include "talk/base/common.h" -#include "talk/base/logging.h" -#include "talk/base/physicalsocketserver.h" -#include "talk/base/jtime.h" -#include "talk/base/winping.h" - -#ifdef __linux -#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h -#endif // __linux - -#ifdef WIN32 -#include -#include -#define _WINSOCKAPI_ -#include -#undef SetPort - -class WinsockInitializer { -public: - WinsockInitializer() { - WSADATA wsaData; - WORD wVersionRequested = MAKEWORD(1, 0); - err_ = WSAStartup(wVersionRequested, &wsaData); - } - ~WinsockInitializer() { - WSACleanup(); - } - int error() { - return err_; - } -private: - int err_; -}; -WinsockInitializer g_winsockinit; -#endif - -namespace cricket { - -const int kfRead = 0x0001; -const int kfWrite = 0x0002; -const int kfConnect = 0x0004; -const int kfClose = 0x0008; - - -// Standard MTUs -const uint16 PACKET_MAXIMUMS[] = { - 65535, // Theoretical maximum, Hyperchannel - 32000, // Nothing - 17914, // 16Mb IBM Token Ring - 8166, // IEEE 802.4 - //4464, // IEEE 802.5 (4Mb max) - 4352, // FDDI - //2048, // Wideband Network - 2002, // IEEE 802.5 (4Mb recommended) - //1536, // Expermental Ethernet Networks - //1500, // Ethernet, Point-to-Point (default) - 1492, // IEEE 802.3 - 1006, // SLIP, ARPANET - //576, // X.25 Networks - //544, // DEC IP Portal - //512, // NETBIOS - 508, // IEEE 802/Source-Rt Bridge, ARCNET - 296, // Point-to-Point (low delay) - 68, // Official minimum - 0, // End of list marker -}; - -const uint32 IP_HEADER_SIZE = 20; -const uint32 ICMP_HEADER_SIZE = 8; - -class PhysicalSocket : public AsyncSocket { -public: - PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET) - : ss_(ss), s_(s), enabled_events_(0), error_(0), - state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED) { - if (s != INVALID_SOCKET) - enabled_events_ = kfRead | kfWrite; - } - - virtual ~PhysicalSocket() { - Close(); - } - - // Creates the underlying OS socket (same as the "socket" function). - virtual bool Create(int type) { - Close(); - s_ = ::socket(AF_INET, type, 0); - UpdateLastError(); - enabled_events_ = kfRead | kfWrite; - return s_ != INVALID_SOCKET; - } - - SocketAddress GetLocalAddress() const { - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - int result = ::getsockname(s_, (struct sockaddr*)&addr, &addrlen); - assert(addrlen == sizeof(addr)); - if (result >= 0) { - return SocketAddress(NetworkToHost32(addr.sin_addr.s_addr), - NetworkToHost16(addr.sin_port)); - } else { - return SocketAddress(); - } - } - - SocketAddress GetRemoteAddress() const { - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - int result = ::getpeername(s_, (struct sockaddr*)&addr, &addrlen); - assert(addrlen == sizeof(addr)); - if (result >= 0) { - return SocketAddress( - NetworkToHost32(addr.sin_addr.s_addr), - NetworkToHost16(addr.sin_port)); - } else { - assert(errno == ENOTCONN); - return SocketAddress(); - } - } - - int Bind(const SocketAddress& addr) { - struct sockaddr_in saddr; - IP2SA(&addr, &saddr); - int err = ::bind(s_, (struct sockaddr*)&saddr, sizeof(saddr)); - UpdateLastError(); - return err; - } - - int Connect(const SocketAddress& addr) { - // TODO: Implicit creation is required to reconnect... - // ...but should we make it more explicit? - if ((s_ == INVALID_SOCKET) && !Create(SOCK_STREAM)) - return SOCKET_ERROR; - SocketAddress addr2(addr); - if (addr2.IsUnresolved()) { - LOG(INFO) << "Resolving addr in PhysicalSocket::Connect"; - addr2.Resolve(); // TODO: Do this async later? - } - struct sockaddr_in saddr; - IP2SA(&addr2, &saddr); - int err = ::connect(s_, (struct sockaddr*)&saddr, sizeof(saddr)); - UpdateLastError(); - //LOG(INFO) << "SOCK[" << static_cast(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_; - if (err == 0) { - state_ = CS_CONNECTED; - } else if (IsBlockingError(error_)) { - state_ = CS_CONNECTING; - enabled_events_ |= kfConnect; - } - return err; - } - - int GetError() const { - return error_; - } - - void SetError(int error) { - error_ = error; - } - - ConnState GetState() const { - return state_; - } - - int SetOption(Option opt, int value) { - assert(opt == OPT_DONTFRAGMENT); -#ifdef WIN32 - value = (value == 0) ? 0 : 1; - return ::setsockopt( - s_, IPPROTO_IP, IP_DONTFRAGMENT, reinterpret_cast(&value), - sizeof(value)); -#endif -#ifdef __linux - value = (value == 0) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO; - return ::setsockopt( - s_, IPPROTO_IP, IP_MTU_DISCOVER, &value, sizeof(value)); -#endif -#ifdef OSX - // This is not possible on OSX. - return -1; -#endif - } - - int Send(const void *pv, size_t cb) { - int sent = ::send(s_, reinterpret_cast(pv), (int)cb, 0); - UpdateLastError(); - //LOG(INFO) << "SOCK[" << static_cast(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_; - ASSERT(sent <= static_cast(cb)); // We have seen minidumps where this may be false - if ((sent < 0) && IsBlockingError(error_)) { - enabled_events_ |= kfWrite; - } - return sent; - } - - int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { - struct sockaddr_in saddr; - IP2SA(&addr, &saddr); - int sent = ::sendto( - s_, (const char *)pv, (int)cb, 0, (struct sockaddr*)&saddr, - sizeof(saddr)); - UpdateLastError(); - ASSERT(sent <= static_cast(cb)); // We have seen minidumps where this may be false - if ((sent < 0) && IsBlockingError(error_)) { - enabled_events_ |= kfWrite; - } - return sent; - } - - int Recv(void *pv, size_t cb) { - int received = ::recv(s_, (char *)pv, (int)cb, 0); - UpdateLastError(); - if ((received >= 0) || IsBlockingError(error_)) { - enabled_events_ |= kfRead; - } - return received; - } - - int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { - struct sockaddr saddr; - socklen_t cbAddr = sizeof(saddr); - int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, &saddr, &cbAddr); - UpdateLastError(); - if ((received >= 0) && (paddr != NULL)) - SA2IP(&saddr, paddr); - if ((received >= 0) || IsBlockingError(error_)) { - enabled_events_ |= kfRead; - } - return received; - } - - int Listen(int backlog) { - int err = ::listen(s_, backlog); - UpdateLastError(); - if (err == 0) - state_ = CS_CONNECTING; - return err; - } - - Socket* Accept(SocketAddress *paddr) { - struct sockaddr saddr; - socklen_t cbAddr = sizeof(saddr); - SOCKET s = ::accept(s_, &saddr, &cbAddr); - UpdateLastError(); - if (s == INVALID_SOCKET) - return NULL; - if (paddr != NULL) - SA2IP(&saddr, paddr); - return ss_->WrapSocket(s); - } - - int Close() { - if (s_ == INVALID_SOCKET) - return 0; - int err = ::closesocket(s_); - UpdateLastError(); - //LOG(INFO) << "SOCK[" << static_cast(s_) << "] Close() Ret: " << err << " Error: " << error_; - s_ = INVALID_SOCKET; - state_ = CS_CLOSED; - enabled_events_ = 0; - return err; - } - - int EstimateMTU(uint16* mtu) { - SocketAddress addr = GetRemoteAddress(); - if (addr.IsAny()) { - error_ = ENOTCONN; - return -1; - } - -#ifdef WIN32 - - WinPing ping; - if (!ping.IsValid()) { - error_ = EINVAL; // can't think of a better error ID - return -1; - } - - for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { - int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE; - if (ping.Ping(addr.ip(), size, 0, 1, false) != WinPing::PING_TOO_LARGE) { - *mtu = PACKET_MAXIMUMS[level]; - return 0; - } - } - - assert(false); - return 0; - -#endif // WIN32 - -#ifdef __linux - - int value; - socklen_t vlen = sizeof(value); - int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen); - if (err < 0) { - UpdateLastError(); - return err; - } - - assert((0 <= value) && (value <= 65536)); - *mtu = uint16(value); - return 0; - -#endif // __linux - - // TODO: OSX support - } - - SocketServer* socketserver() { return ss_; } - -protected: - PhysicalSocketServer* ss_; - SOCKET s_; - uint32 enabled_events_; - int error_; - ConnState state_; - - void UpdateLastError() { -#ifdef WIN32 - error_ = WSAGetLastError(); -#endif -#ifdef POSIX - error_ = errno; -#endif - } - - void IP2SA(const SocketAddress *paddr, struct sockaddr_in *psaddr) { - memset(psaddr, 0, sizeof(*psaddr)); - psaddr->sin_family = AF_INET; - psaddr->sin_port = HostToNetwork16(paddr->port()); - if (paddr->ip() == 0) - psaddr->sin_addr.s_addr = INADDR_ANY; - else - psaddr->sin_addr.s_addr = HostToNetwork32(paddr->ip()); - } - - void SA2IP(const struct sockaddr *psaddr, SocketAddress *paddr) { - const struct sockaddr_in *psaddr_in = - reinterpret_cast(psaddr); - paddr->SetIP(NetworkToHost32(psaddr_in->sin_addr.s_addr)); - paddr->SetPort(NetworkToHost16(psaddr_in->sin_port)); - } -}; - -#ifdef POSIX -class Dispatcher { -public: - virtual uint32 GetRequestedEvents() = 0; - virtual void OnPreEvent(uint32 ff) = 0; - virtual void OnEvent(uint32 ff, int err) = 0; - virtual int GetDescriptor() = 0; -}; - -class EventDispatcher : public Dispatcher { -public: - EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) { - if (pipe(afd_) < 0) - LOG(LERROR) << "pipe failed"; - ss_->Add(this); - } - - virtual ~EventDispatcher() { - ss_->Remove(this); - close(afd_[0]); - close(afd_[1]); - } - - virtual void Signal() { - CritScope cs(&crit_); - if (!fSignaled_) { - uint8 b = 0; - if (write(afd_[1], &b, sizeof(b)) < 0) - LOG(LERROR) << "write failed"; - fSignaled_ = true; - } - } - - virtual uint32 GetRequestedEvents() { - return kfRead; - } - - virtual void OnPreEvent(uint32 ff) { - // It is not possible to perfectly emulate an auto-resetting event with - // pipes. This simulates it by resetting before the event is handled. - - CritScope cs(&crit_); - if (fSignaled_) { - uint8 b; - read(afd_[0], &b, sizeof(b)); - fSignaled_ = false; - } - } - - virtual void OnEvent(uint32 ff, int err) { - assert(false); - } - - virtual int GetDescriptor() { - return afd_[0]; - } - -private: - PhysicalSocketServer *ss_; - int afd_[2]; - bool fSignaled_; - CriticalSection crit_; -}; - -class SocketDispatcher : public Dispatcher, public PhysicalSocket { -public: - SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) { - ss_->Add(this); - } - SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) { - ss_->Add(this); - } - - virtual ~SocketDispatcher() { - ss_->Remove(this); - } - - bool Initialize() { - fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK); - return true; - } - - virtual bool Create(int type) { - // Change the socket to be non-blocking. - if (!PhysicalSocket::Create(type)) - return false; - - return Initialize(); - } - - virtual int GetDescriptor() { - return s_; - } - - virtual uint32 GetRequestedEvents() { - return enabled_events_; - } - - virtual void OnPreEvent(uint32 ff) { - } - - virtual void OnEvent(uint32 ff, int err) { - if ((ff & kfRead) != 0) { - enabled_events_ &= ~kfRead; - SignalReadEvent(this); - } - if ((ff & kfWrite) != 0) { - enabled_events_ &= ~kfWrite; - SignalWriteEvent(this); - } - if ((ff & kfConnect) != 0) { - enabled_events_ &= ~kfConnect; - SignalConnectEvent(this); - } - if ((ff & kfClose) != 0) - SignalCloseEvent(this, err); - } -}; - -class FileDispatcher: public Dispatcher, public AsyncFile { -public: - FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) { - set_readable(true); - - ss_->Add(this); - - fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK); - } - - virtual ~FileDispatcher() { - ss_->Remove(this); - } - - SocketServer* socketserver() { return ss_; } - - virtual int GetDescriptor() { - return fd_; - } - - virtual uint32 GetRequestedEvents() { - return flags_; - } - - virtual void OnPreEvent(uint32 ff) { - } - - virtual void OnEvent(uint32 ff, int err) { - if ((ff & kfRead) != 0) - SignalReadEvent(this); - if ((ff & kfWrite) != 0) - SignalWriteEvent(this); - if ((ff & kfClose) != 0) - SignalCloseEvent(this, err); - } - - virtual bool readable() { - return (flags_ & kfRead) != 0; - } - - virtual void set_readable(bool value) { - flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead); - } - - virtual bool writable() { - return (flags_ & kfWrite) != 0; - } - - virtual void set_writable(bool value) { - flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite); - } - -private: - PhysicalSocketServer* ss_; - int fd_; - int flags_; -}; - -AsyncFile* PhysicalSocketServer::CreateFile(int fd) { - return new FileDispatcher(fd, this); -} - -#endif // POSIX - -#ifdef WIN32 -class Dispatcher { -public: - virtual uint32 GetRequestedEvents() = 0; - virtual void OnPreEvent(uint32 ff) = 0; - virtual void OnEvent(uint32 ff, int err) = 0; - virtual WSAEVENT GetWSAEvent() = 0; - virtual SOCKET GetSocket() = 0; - virtual bool CheckSignalClose() = 0; -}; - -uint32 FlagsToEvents(uint32 events) { - uint32 ffFD = FD_CLOSE | FD_ACCEPT; - if (events & kfRead) - ffFD |= FD_READ; - if (events & kfWrite) - ffFD |= FD_WRITE; - if (events & kfConnect) - ffFD |= FD_CONNECT; - return ffFD; -} - -class EventDispatcher : public Dispatcher { -public: - EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) { - if (hev_ = WSACreateEvent()) { - ss_->Add(this); - } - } - - ~EventDispatcher() { - if (hev_ != NULL) { - ss_->Remove(this); - WSACloseEvent(hev_); - hev_ = NULL; - } - } - - virtual void Signal() { - if (hev_ != NULL) - WSASetEvent(hev_); - } - - virtual uint32 GetRequestedEvents() { - return 0; - } - - virtual void OnPreEvent(uint32 ff) { - WSAResetEvent(hev_); - } - - virtual void OnEvent(uint32 ff, int err) { - } - - virtual WSAEVENT GetWSAEvent() { - return hev_; - } - - virtual SOCKET GetSocket() { - return INVALID_SOCKET; - } - - virtual bool CheckSignalClose() { return false; } - -private: - PhysicalSocketServer* ss_; - WSAEVENT hev_; -}; - -class SocketDispatcher : public Dispatcher, public PhysicalSocket { -public: - static int next_id_; - int id_; - bool signal_close_; - int signal_err_; - - SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) { - } - SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) { - } - - virtual ~SocketDispatcher() { - Close(); - } - - bool Initialize() { - assert(s_ != INVALID_SOCKET); - // Must be a non-blocking - u_long argp = 1; - ioctlsocket(s_, FIONBIO, &argp); - ss_->Add(this); - return true; - } - - virtual bool Create(int type) { - // Create socket - if (!PhysicalSocket::Create(type)) - return false; - - if (!Initialize()) - return false; - - do { id_ = ++next_id_; } while (id_ == 0); - return true; - } - - virtual int Close() { - if (s_ == INVALID_SOCKET) - return 0; - - id_ = 0; - signal_close_ = false; - ss_->Remove(this); - return PhysicalSocket::Close(); - } - - virtual uint32 GetRequestedEvents() { - return enabled_events_; - } - - virtual void OnPreEvent(uint32 ff) { - if ((ff & kfConnect) != 0) - state_ = CS_CONNECTED; - } - - virtual void OnEvent(uint32 ff, int err) { - int cache_id = id_; - if ((ff & kfRead) != 0) { - enabled_events_ &= ~kfRead; - SignalReadEvent(this); - } - if (((ff & kfWrite) != 0) && (id_ == cache_id)) { - enabled_events_ &= ~kfWrite; - SignalWriteEvent(this); - } - if (((ff & kfConnect) != 0) && (id_ == cache_id)) { - enabled_events_ &= ~kfConnect; - SignalConnectEvent(this); - } - if (((ff & kfClose) != 0) && (id_ == cache_id)) { - //LOG(INFO) << "SOCK[" << static_cast(s_) << "] OnClose() Error: " << err; - signal_close_ = true; - signal_err_ = err; - } - } - - virtual WSAEVENT GetWSAEvent() { - return WSA_INVALID_EVENT; - } - - virtual SOCKET GetSocket() { - return s_; - } - - virtual bool CheckSignalClose() { - if (!signal_close_) - return false; - - char ch; - if (recv(s_, &ch, 1, MSG_PEEK) > 0) - return false; - - signal_close_ = false; - SignalCloseEvent(this, signal_err_); - return true; - } -}; - -int SocketDispatcher::next_id_ = 0; - -#endif // WIN32 - -// Sets the value of a boolean value to false when signaled. -class Signaler : public EventDispatcher { -public: - Signaler(PhysicalSocketServer* ss, bool* pf) - : EventDispatcher(ss), pf_(pf) { - } - virtual ~Signaler() { } - - void OnEvent(uint32 ff, int err) { - if (pf_) - *pf_ = false; - } - -private: - bool *pf_; -}; - -PhysicalSocketServer::PhysicalSocketServer() : fWait_(false), - last_tick_tracked_(0), last_tick_dispatch_count_(0) { - signal_wakeup_ = new Signaler(this, &fWait_); -} - -PhysicalSocketServer::~PhysicalSocketServer() { - delete signal_wakeup_; -} - -void PhysicalSocketServer::WakeUp() { - signal_wakeup_->Signal(); -} - -Socket* PhysicalSocketServer::CreateSocket(int type) { - PhysicalSocket* socket = new PhysicalSocket(this); - if (socket->Create(type)) { - return socket; - } else { - delete socket; - return 0; - } -} - -AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) { - SocketDispatcher* dispatcher = new SocketDispatcher(this); - if (dispatcher->Create(type)) { - return dispatcher; - } else { - delete dispatcher; - return 0; - } -} - -AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) { - SocketDispatcher* dispatcher = new SocketDispatcher(s, this); - if (dispatcher->Initialize()) { - return dispatcher; - } else { - delete dispatcher; - return 0; - } -} - -void PhysicalSocketServer::Add(Dispatcher *pdispatcher) { - CritScope cs(&crit_); - dispatchers_.push_back(pdispatcher); -} - -void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) { - CritScope cs(&crit_); - dispatchers_.erase(std::remove(dispatchers_.begin(), dispatchers_.end(), pdispatcher), dispatchers_.end()); -} - -#ifdef POSIX -bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { - // Calculate timing information - - struct timeval *ptvWait = NULL; - struct timeval tvWait; - struct timeval tvStop; - if (cmsWait != -1) { - // Calculate wait timeval - tvWait.tv_sec = cmsWait / 1000; - tvWait.tv_usec = (cmsWait % 1000) * 1000; - ptvWait = &tvWait; - - // Calculate when to return in a timeval - gettimeofday(&tvStop, NULL); - tvStop.tv_sec += tvWait.tv_sec; - tvStop.tv_usec += tvWait.tv_usec; - if (tvStop.tv_usec >= 1000000) { - tvStop.tv_usec -= 1000000; - tvStop.tv_sec += 1; - } - } - - // Zero all fd_sets. Don't need to do this inside the loop since - // select() zeros the descriptors not signaled - - fd_set fdsRead; - FD_ZERO(&fdsRead); - fd_set fdsWrite; - FD_ZERO(&fdsWrite); - - fWait_ = true; - - while (fWait_) { - int fdmax = -1; - { - CritScope cr(&crit_); - for (unsigned i = 0; i < dispatchers_.size(); i++) { - // Query dispatchers for read and write wait state - - Dispatcher *pdispatcher = dispatchers_[i]; - assert(pdispatcher); - if (!process_io && (pdispatcher != signal_wakeup_)) - continue; - int fd = pdispatcher->GetDescriptor(); - if (fd > fdmax) - fdmax = fd; - uint32 ff = pdispatcher->GetRequestedEvents(); - if (ff & kfRead) - FD_SET(fd, &fdsRead); - if (ff & (kfWrite | kfConnect)) - FD_SET(fd, &fdsWrite); - } - } - - // Wait then call handlers as appropriate - // < 0 means error - // 0 means timeout - // > 0 means count of descriptors ready - int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); - - // If error, return error - // todo: do something intelligent - - if (n < 0) - return false; - - // If timeout, return success - - if (n == 0) - return true; - - // We have signaled descriptors - - { - CritScope cr(&crit_); - for (unsigned i = 0; i < dispatchers_.size(); i++) { - Dispatcher *pdispatcher = dispatchers_[i]; - int fd = pdispatcher->GetDescriptor(); - uint32 ff = 0; - if (FD_ISSET(fd, &fdsRead)) { - FD_CLR(fd, &fdsRead); - ff |= kfRead; - } - if (FD_ISSET(fd, &fdsWrite)) { - FD_CLR(fd, &fdsWrite); - if (pdispatcher->GetRequestedEvents() & kfConnect) { - ff |= kfConnect; - } else { - ff |= kfWrite; - } - } - if (ff != 0) { - pdispatcher->OnPreEvent(ff); - pdispatcher->OnEvent(ff, 0); - } - } - } - - // Recalc the time remaining to wait. Doing it here means it doesn't get - // calced twice the first time through the loop - - if (cmsWait != -1) { - ptvWait->tv_sec = 0; - ptvWait->tv_usec = 0; - struct timeval tvT; - gettimeofday(&tvT, NULL); - if (tvStop.tv_sec >= tvT.tv_sec) { - ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; - ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; - if (ptvWait->tv_usec < 0) { - ptvWait->tv_usec += 1000000; - ptvWait->tv_sec -= 1; - } - } - } - } - - return true; -} -#endif // POSIX - -#ifdef WIN32 -bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) -{ - int cmsTotal = cmsWait; - int cmsElapsed = 0; - uint32 msStart = GetMillisecondCount(); - -#if LOGGING - if (last_tick_dispatch_count_ == 0) { - last_tick_tracked_ = msStart; - } -#endif - - WSAEVENT socket_ev = WSACreateEvent(); - - fWait_ = true; - while (fWait_) { - std::vector events; - std::vector event_owners; - - events.push_back(socket_ev); - - { - CritScope cr(&crit_); - for (size_t i = 0; i < dispatchers_.size(); ++i) { - Dispatcher * disp = dispatchers_[i]; - if (!process_io && (disp != signal_wakeup_)) - continue; - SOCKET s = disp->GetSocket(); - if (disp->CheckSignalClose()) { - // We just signalled close, don't poll this socket - } else if (s != INVALID_SOCKET) { - WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents())); - } else { - events.push_back(disp->GetWSAEvent()); - event_owners.push_back(disp); - } - } - } - - // Which is shorter, the delay wait or the asked wait? - - int cmsNext; - if (cmsWait == -1) { - cmsNext = cmsWait; - } else { - cmsNext = cmsTotal - cmsElapsed; - if (cmsNext < 0) - cmsNext = 0; - } - - // Wait for one of the events to signal - DWORD dw = WSAWaitForMultipleEvents(static_cast(events.size()), &events[0], false, cmsNext, false); - -#if 0 // LOGGING - // we track this information purely for logging purposes. - last_tick_dispatch_count_++; - if (last_tick_dispatch_count_ >= 1000) { - uint32 now = GetMillisecondCount(); - LOG(INFO) << "PhysicalSocketServer took " << TimeDiff(now, last_tick_tracked_) << "ms for 1000 events"; - - // If we get more than 1000 events in a second, we are spinning badly - // (normally it should take about 8-20 seconds). - assert(TimeDiff(now, last_tick_tracked_) > 1000); - - last_tick_tracked_ = now; - last_tick_dispatch_count_ = 0; - } -#endif - - // Failed? - // todo: need a better strategy than this! - - if (dw == WSA_WAIT_FAILED) { - int error = WSAGetLastError(); - assert(false); - WSACloseEvent(socket_ev); - return false; - } - - // Timeout? - - if (dw == WSA_WAIT_TIMEOUT) { - WSACloseEvent(socket_ev); - return true; - } - - // Figure out which one it is and call it - - { - CritScope cr(&crit_); - int index = dw - WSA_WAIT_EVENT_0; - if (index > 0) { - --index; // The first event is the socket event - event_owners[index]->OnPreEvent(0); - event_owners[index]->OnEvent(0, 0); - } else if (process_io) { - for (size_t i = 0; i < dispatchers_.size(); ++i) { - Dispatcher * disp = dispatchers_[i]; - SOCKET s = disp->GetSocket(); - if (s == INVALID_SOCKET) - continue; - - WSANETWORKEVENTS wsaEvents; - int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents); - if (err == 0) { - -#if LOGGING - { - if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT]; - } - if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT]; - } - if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT]; - } - if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; - } - if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT]; - } - } -#endif - uint32 ff = 0; - int errcode = 0; - if (wsaEvents.lNetworkEvents & FD_READ) - ff |= kfRead; - if (wsaEvents.lNetworkEvents & FD_WRITE) - ff |= kfWrite; - if (wsaEvents.lNetworkEvents & FD_CONNECT) { - if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) { - ff |= kfConnect; - } else { - // TODO: Decide whether we want to signal connect, but with an error code - ff |= kfClose; - errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT]; - } - } - if (wsaEvents.lNetworkEvents & FD_ACCEPT) - ff |= kfRead; - if (wsaEvents.lNetworkEvents & FD_CLOSE) { - ff |= kfClose; - errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT]; - } - if (ff != 0) { - disp->OnPreEvent(ff); - disp->OnEvent(ff, errcode); - } - } - } - } - - // Reset the network event until new activity occurs - WSAResetEvent(socket_ev); - } - - // Break? - - if (!fWait_) - break; - cmsElapsed = GetMillisecondCount() - msStart; - if (cmsWait != -1) { - if (cmsElapsed >= cmsWait) - break; - } - } - - // Done - - WSACloseEvent(socket_ev); - return true; -} -#endif // WIN32 - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cpp new file mode 100644 index 00000000..37836302 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cpp @@ -0,0 +1,1116 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include +#include + +#ifdef POSIX +extern "C" { +#include +#include +#include +#include +#include +} +#endif + +#include "talk/base/basictypes.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/jtime.h" +#include "talk/base/winping.h" + +#ifdef __linux +#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h +#endif // __linux + +#ifdef WIN32 +#include +#include +#define _WINSOCKAPI_ +#include +#undef SetPort + +class WinsockInitializer { +public: + WinsockInitializer() { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(1, 0); + err_ = WSAStartup(wVersionRequested, &wsaData); + } + ~WinsockInitializer() { + WSACleanup(); + } + int error() { + return err_; + } +private: + int err_; +}; +WinsockInitializer g_winsockinit; +#endif + +namespace cricket { + +const int kfRead = 0x0001; +const int kfWrite = 0x0002; +const int kfConnect = 0x0004; +const int kfClose = 0x0008; + + +// Standard MTUs +const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + //4464, // IEEE 802.5 (4Mb max) + 4352, // FDDI + //2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + //1536, // Expermental Ethernet Networks + //1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + //576, // X.25 Networks + //544, // DEC IP Portal + //512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + 68, // Official minimum + 0, // End of list marker +}; + +const uint32 IP_HEADER_SIZE = 20; +const uint32 ICMP_HEADER_SIZE = 8; + +class PhysicalSocket : public AsyncSocket { +public: + PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET) + : ss_(ss), s_(s), enabled_events_(0), error_(0), + state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED) { + if (s != INVALID_SOCKET) + enabled_events_ = kfRead | kfWrite; + } + + virtual ~PhysicalSocket() { + Close(); + } + + // Creates the underlying OS socket (same as the "socket" function). + virtual bool Create(int type) { + Close(); + s_ = ::socket(AF_INET, type, 0); + UpdateLastError(); + enabled_events_ = kfRead | kfWrite; + return s_ != INVALID_SOCKET; + } + + SocketAddress GetLocalAddress() const { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(s_, (struct sockaddr*)&addr, &addrlen); + assert(addrlen == sizeof(addr)); + if (result >= 0) { + return SocketAddress(NetworkToHost32(addr.sin_addr.s_addr), + NetworkToHost16(addr.sin_port)); + } else { + return SocketAddress(); + } + } + + SocketAddress GetRemoteAddress() const { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(s_, (struct sockaddr*)&addr, &addrlen); + assert(addrlen == sizeof(addr)); + if (result >= 0) { + return SocketAddress( + NetworkToHost32(addr.sin_addr.s_addr), + NetworkToHost16(addr.sin_port)); + } else { + assert(errno == ENOTCONN); + return SocketAddress(); + } + } + + int Bind(const SocketAddress& addr) { + struct sockaddr_in saddr; + IP2SA(&addr, &saddr); + int err = ::bind(s_, (struct sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + return err; + } + + int Connect(const SocketAddress& addr) { + // TODO: Implicit creation is required to reconnect... + // ...but should we make it more explicit? + if ((s_ == INVALID_SOCKET) && !Create(SOCK_STREAM)) + return SOCKET_ERROR; + SocketAddress addr2(addr); + if (addr2.IsUnresolved()) { + LOG(INFO) << "Resolving addr in PhysicalSocket::Connect"; + addr2.Resolve(); // TODO: Do this async later? + } + struct sockaddr_in saddr; + IP2SA(&addr2, &saddr); + int err = ::connect(s_, (struct sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_; + if (err == 0) { + state_ = CS_CONNECTED; + } else if (IsBlockingError(error_)) { + state_ = CS_CONNECTING; + enabled_events_ |= kfConnect; + } + return err; + } + + int GetError() const { + return error_; + } + + void SetError(int error) { + error_ = error; + } + + ConnState GetState() const { + return state_; + } + + int SetOption(Option opt, int value) { + assert(opt == OPT_DONTFRAGMENT); +#ifdef WIN32 + value = (value == 0) ? 0 : 1; + return ::setsockopt( + s_, IPPROTO_IP, IP_DONTFRAGMENT, reinterpret_cast(&value), + sizeof(value)); +#endif +#ifdef __linux + value = (value == 0) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO; + return ::setsockopt( + s_, IPPROTO_IP, IP_MTU_DISCOVER, &value, sizeof(value)); +#endif +#ifdef OSX + // This is not possible on OSX. + return -1; +#endif + } + + int Send(const void *pv, size_t cb) { + int sent = ::send(s_, reinterpret_cast(pv), (int)cb, 0); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_; + ASSERT(sent <= static_cast(cb)); // We have seen minidumps where this may be false + if ((sent < 0) && IsBlockingError(error_)) { + enabled_events_ |= kfWrite; + } + return sent; + } + + int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + struct sockaddr_in saddr; + IP2SA(&addr, &saddr); + int sent = ::sendto( + s_, (const char *)pv, (int)cb, 0, (struct sockaddr*)&saddr, + sizeof(saddr)); + UpdateLastError(); + ASSERT(sent <= static_cast(cb)); // We have seen minidumps where this may be false + if ((sent < 0) && IsBlockingError(error_)) { + enabled_events_ |= kfWrite; + } + return sent; + } + + int Recv(void *pv, size_t cb) { + int received = ::recv(s_, (char *)pv, (int)cb, 0); + UpdateLastError(); + if ((received >= 0) || IsBlockingError(error_)) { + enabled_events_ |= kfRead; + } + return received; + } + + int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + struct sockaddr saddr; + socklen_t cbAddr = sizeof(saddr); + int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, &saddr, &cbAddr); + UpdateLastError(); + if ((received >= 0) && (paddr != NULL)) + SA2IP(&saddr, paddr); + if ((received >= 0) || IsBlockingError(error_)) { + enabled_events_ |= kfRead; + } + return received; + } + + int Listen(int backlog) { + int err = ::listen(s_, backlog); + UpdateLastError(); + if (err == 0) + state_ = CS_CONNECTING; + return err; + } + + Socket* Accept(SocketAddress *paddr) { + struct sockaddr saddr; + socklen_t cbAddr = sizeof(saddr); + SOCKET s = ::accept(s_, &saddr, &cbAddr); + UpdateLastError(); + if (s == INVALID_SOCKET) + return NULL; + if (paddr != NULL) + SA2IP(&saddr, paddr); + return ss_->WrapSocket(s); + } + + int Close() { + if (s_ == INVALID_SOCKET) + return 0; + int err = ::closesocket(s_); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast(s_) << "] Close() Ret: " << err << " Error: " << error_; + s_ = INVALID_SOCKET; + state_ = CS_CLOSED; + enabled_events_ = 0; + return err; + } + + int EstimateMTU(uint16* mtu) { + SocketAddress addr = GetRemoteAddress(); + if (addr.IsAny()) { + error_ = ENOTCONN; + return -1; + } + +#ifdef WIN32 + + WinPing ping; + if (!ping.IsValid()) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + + for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { + int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE; + if (ping.Ping(addr.ip(), size, 0, 1, false) != WinPing::PING_TOO_LARGE) { + *mtu = PACKET_MAXIMUMS[level]; + return 0; + } + } + + assert(false); + return 0; + +#endif // WIN32 + +#ifdef __linux + + int value; + socklen_t vlen = sizeof(value); + int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen); + if (err < 0) { + UpdateLastError(); + return err; + } + + assert((0 <= value) && (value <= 65536)); + *mtu = uint16(value); + return 0; + +#endif // __linux + + // TODO: OSX support + } + + SocketServer* socketserver() { return ss_; } + +protected: + PhysicalSocketServer* ss_; + SOCKET s_; + uint32 enabled_events_; + int error_; + ConnState state_; + + void UpdateLastError() { +#ifdef WIN32 + error_ = WSAGetLastError(); +#endif +#ifdef POSIX + error_ = errno; +#endif + } + + void IP2SA(const SocketAddress *paddr, struct sockaddr_in *psaddr) { + memset(psaddr, 0, sizeof(*psaddr)); + psaddr->sin_family = AF_INET; + psaddr->sin_port = HostToNetwork16(paddr->port()); + if (paddr->ip() == 0) + psaddr->sin_addr.s_addr = INADDR_ANY; + else + psaddr->sin_addr.s_addr = HostToNetwork32(paddr->ip()); + } + + void SA2IP(const struct sockaddr *psaddr, SocketAddress *paddr) { + const struct sockaddr_in *psaddr_in = + reinterpret_cast(psaddr); + paddr->SetIP(NetworkToHost32(psaddr_in->sin_addr.s_addr)); + paddr->SetPort(NetworkToHost16(psaddr_in->sin_port)); + } +}; + +#ifdef POSIX +class Dispatcher { +public: + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; + virtual int GetDescriptor() = 0; +}; + +class EventDispatcher : public Dispatcher { +public: + EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) { + if (pipe(afd_) < 0) + LOG(LERROR) << "pipe failed"; + ss_->Add(this); + } + + virtual ~EventDispatcher() { + ss_->Remove(this); + close(afd_[0]); + close(afd_[1]); + } + + virtual void Signal() { + CritScope cs(&crit_); + if (!fSignaled_) { + uint8 b = 0; + if (write(afd_[1], &b, sizeof(b)) < 0) + LOG(LERROR) << "write failed"; + fSignaled_ = true; + } + } + + virtual uint32 GetRequestedEvents() { + return kfRead; + } + + virtual void OnPreEvent(uint32 ff) { + // It is not possible to perfectly emulate an auto-resetting event with + // pipes. This simulates it by resetting before the event is handled. + + CritScope cs(&crit_); + if (fSignaled_) { + uint8 b; + read(afd_[0], &b, sizeof(b)); + fSignaled_ = false; + } + } + + virtual void OnEvent(uint32 ff, int err) { + assert(false); + } + + virtual int GetDescriptor() { + return afd_[0]; + } + +private: + PhysicalSocketServer *ss_; + int afd_[2]; + bool fSignaled_; + CriticalSection crit_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { +public: + SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) { + ss_->Add(this); + } + SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) { + ss_->Add(this); + } + + virtual ~SocketDispatcher() { + ss_->Remove(this); + } + + bool Initialize() { + fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK); + return true; + } + + virtual bool Create(int type) { + // Change the socket to be non-blocking. + if (!PhysicalSocket::Create(type)) + return false; + + return Initialize(); + } + + virtual int GetDescriptor() { + return s_; + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & kfRead) != 0) { + enabled_events_ &= ~kfRead; + SignalReadEvent(this); + } + if ((ff & kfWrite) != 0) { + enabled_events_ &= ~kfWrite; + SignalWriteEvent(this); + } + if ((ff & kfConnect) != 0) { + enabled_events_ &= ~kfConnect; + SignalConnectEvent(this); + } + if ((ff & kfClose) != 0) + SignalCloseEvent(this, err); + } +}; + +class FileDispatcher: public Dispatcher, public AsyncFile { +public: + FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) { + set_readable(true); + + ss_->Add(this); + + fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK); + } + + virtual ~FileDispatcher() { + ss_->Remove(this); + } + + SocketServer* socketserver() { return ss_; } + + virtual int GetDescriptor() { + return fd_; + } + + virtual uint32 GetRequestedEvents() { + return flags_; + } + + virtual void OnPreEvent(uint32 ff) { + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & kfRead) != 0) + SignalReadEvent(this); + if ((ff & kfWrite) != 0) + SignalWriteEvent(this); + if ((ff & kfClose) != 0) + SignalCloseEvent(this, err); + } + + virtual bool readable() { + return (flags_ & kfRead) != 0; + } + + virtual void set_readable(bool value) { + flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead); + } + + virtual bool writable() { + return (flags_ & kfWrite) != 0; + } + + virtual void set_writable(bool value) { + flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite); + } + +private: + PhysicalSocketServer* ss_; + int fd_; + int flags_; +}; + +AsyncFile* PhysicalSocketServer::CreateFile(int fd) { + return new FileDispatcher(fd, this); +} + +#endif // POSIX + +#ifdef WIN32 +class Dispatcher { +public: + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; + virtual WSAEVENT GetWSAEvent() = 0; + virtual SOCKET GetSocket() = 0; + virtual bool CheckSignalClose() = 0; +}; + +uint32 FlagsToEvents(uint32 events) { + uint32 ffFD = FD_CLOSE | FD_ACCEPT; + if (events & kfRead) + ffFD |= FD_READ; + if (events & kfWrite) + ffFD |= FD_WRITE; + if (events & kfConnect) + ffFD |= FD_CONNECT; + return ffFD; +} + +class EventDispatcher : public Dispatcher { +public: + EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) { + if (hev_ = WSACreateEvent()) { + ss_->Add(this); + } + } + + ~EventDispatcher() { + if (hev_ != NULL) { + ss_->Remove(this); + WSACloseEvent(hev_); + hev_ = NULL; + } + } + + virtual void Signal() { + if (hev_ != NULL) + WSASetEvent(hev_); + } + + virtual uint32 GetRequestedEvents() { + return 0; + } + + virtual void OnPreEvent(uint32 ff) { + WSAResetEvent(hev_); + } + + virtual void OnEvent(uint32 ff, int err) { + } + + virtual WSAEVENT GetWSAEvent() { + return hev_; + } + + virtual SOCKET GetSocket() { + return INVALID_SOCKET; + } + + virtual bool CheckSignalClose() { return false; } + +private: + PhysicalSocketServer* ss_; + WSAEVENT hev_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { +public: + static int next_id_; + int id_; + bool signal_close_; + int signal_err_; + + SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) { + } + SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) { + } + + virtual ~SocketDispatcher() { + Close(); + } + + bool Initialize() { + assert(s_ != INVALID_SOCKET); + // Must be a non-blocking + u_long argp = 1; + ioctlsocket(s_, FIONBIO, &argp); + ss_->Add(this); + return true; + } + + virtual bool Create(int type) { + // Create socket + if (!PhysicalSocket::Create(type)) + return false; + + if (!Initialize()) + return false; + + do { id_ = ++next_id_; } while (id_ == 0); + return true; + } + + virtual int Close() { + if (s_ == INVALID_SOCKET) + return 0; + + id_ = 0; + signal_close_ = false; + ss_->Remove(this); + return PhysicalSocket::Close(); + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + if ((ff & kfConnect) != 0) + state_ = CS_CONNECTED; + } + + virtual void OnEvent(uint32 ff, int err) { + int cache_id = id_; + if ((ff & kfRead) != 0) { + enabled_events_ &= ~kfRead; + SignalReadEvent(this); + } + if (((ff & kfWrite) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~kfWrite; + SignalWriteEvent(this); + } + if (((ff & kfConnect) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~kfConnect; + SignalConnectEvent(this); + } + if (((ff & kfClose) != 0) && (id_ == cache_id)) { + //LOG(INFO) << "SOCK[" << static_cast(s_) << "] OnClose() Error: " << err; + signal_close_ = true; + signal_err_ = err; + } + } + + virtual WSAEVENT GetWSAEvent() { + return WSA_INVALID_EVENT; + } + + virtual SOCKET GetSocket() { + return s_; + } + + virtual bool CheckSignalClose() { + if (!signal_close_) + return false; + + char ch; + if (recv(s_, &ch, 1, MSG_PEEK) > 0) + return false; + + signal_close_ = false; + SignalCloseEvent(this, signal_err_); + return true; + } +}; + +int SocketDispatcher::next_id_ = 0; + +#endif // WIN32 + +// Sets the value of a boolean value to false when signaled. +class Signaler : public EventDispatcher { +public: + Signaler(PhysicalSocketServer* ss, bool* pf) + : EventDispatcher(ss), pf_(pf) { + } + virtual ~Signaler() { } + + void OnEvent(uint32 ff, int err) { + if (pf_) + *pf_ = false; + } + +private: + bool *pf_; +}; + +PhysicalSocketServer::PhysicalSocketServer() : fWait_(false), + last_tick_tracked_(0), last_tick_dispatch_count_(0) { + signal_wakeup_ = new Signaler(this, &fWait_); +} + +PhysicalSocketServer::~PhysicalSocketServer() { + delete signal_wakeup_; +} + +void PhysicalSocketServer::WakeUp() { + signal_wakeup_->Signal(); +} + +Socket* PhysicalSocketServer::CreateSocket(int type) { + PhysicalSocket* socket = new PhysicalSocket(this); + if (socket->Create(type)) { + return socket; + } else { + delete socket; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) { + SocketDispatcher* dispatcher = new SocketDispatcher(this); + if (dispatcher->Create(type)) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) { + SocketDispatcher* dispatcher = new SocketDispatcher(s, this); + if (dispatcher->Initialize()) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +void PhysicalSocketServer::Add(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + dispatchers_.push_back(pdispatcher); +} + +void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + dispatchers_.erase(std::remove(dispatchers_.begin(), dispatchers_.end(), pdispatcher), dispatchers_.end()); +} + +#ifdef POSIX +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { + // Calculate timing information + + struct timeval *ptvWait = NULL; + struct timeval tvWait; + struct timeval tvStop; + if (cmsWait != -1) { + // Calculate wait timeval + tvWait.tv_sec = cmsWait / 1000; + tvWait.tv_usec = (cmsWait % 1000) * 1000; + ptvWait = &tvWait; + + // Calculate when to return in a timeval + gettimeofday(&tvStop, NULL); + tvStop.tv_sec += tvWait.tv_sec; + tvStop.tv_usec += tvWait.tv_usec; + if (tvStop.tv_usec >= 1000000) { + tvStop.tv_usec -= 1000000; + tvStop.tv_sec += 1; + } + } + + // Zero all fd_sets. Don't need to do this inside the loop since + // select() zeros the descriptors not signaled + + fd_set fdsRead; + FD_ZERO(&fdsRead); + fd_set fdsWrite; + FD_ZERO(&fdsWrite); + + fWait_ = true; + + while (fWait_) { + int fdmax = -1; + { + CritScope cr(&crit_); + for (unsigned i = 0; i < dispatchers_.size(); i++) { + // Query dispatchers for read and write wait state + + Dispatcher *pdispatcher = dispatchers_[i]; + assert(pdispatcher); + if (!process_io && (pdispatcher != signal_wakeup_)) + continue; + int fd = pdispatcher->GetDescriptor(); + if (fd > fdmax) + fdmax = fd; + uint32 ff = pdispatcher->GetRequestedEvents(); + if (ff & kfRead) + FD_SET(fd, &fdsRead); + if (ff & (kfWrite | kfConnect)) + FD_SET(fd, &fdsWrite); + } + } + + // Wait then call handlers as appropriate + // < 0 means error + // 0 means timeout + // > 0 means count of descriptors ready + int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); + + // If error, return error + // todo: do something intelligent + + if (n < 0) + return false; + + // If timeout, return success + + if (n == 0) + return true; + + // We have signaled descriptors + + { + CritScope cr(&crit_); + for (unsigned i = 0; i < dispatchers_.size(); i++) { + Dispatcher *pdispatcher = dispatchers_[i]; + int fd = pdispatcher->GetDescriptor(); + uint32 ff = 0; + if (FD_ISSET(fd, &fdsRead)) { + FD_CLR(fd, &fdsRead); + ff |= kfRead; + } + if (FD_ISSET(fd, &fdsWrite)) { + FD_CLR(fd, &fdsWrite); + if (pdispatcher->GetRequestedEvents() & kfConnect) { + ff |= kfConnect; + } else { + ff |= kfWrite; + } + } + if (ff != 0) { + pdispatcher->OnPreEvent(ff); + pdispatcher->OnEvent(ff, 0); + } + } + } + + // Recalc the time remaining to wait. Doing it here means it doesn't get + // calced twice the first time through the loop + + if (cmsWait != -1) { + ptvWait->tv_sec = 0; + ptvWait->tv_usec = 0; + struct timeval tvT; + gettimeofday(&tvT, NULL); + if (tvStop.tv_sec >= tvT.tv_sec) { + ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; + ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; + if (ptvWait->tv_usec < 0) { + ptvWait->tv_usec += 1000000; + ptvWait->tv_sec -= 1; + } + } + } + } + + return true; +} +#endif // POSIX + +#ifdef WIN32 +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) +{ + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = GetMillisecondCount(); + +#if LOGGING + if (last_tick_dispatch_count_ == 0) { + last_tick_tracked_ = msStart; + } +#endif + + WSAEVENT socket_ev = WSACreateEvent(); + + fWait_ = true; + while (fWait_) { + std::vector events; + std::vector event_owners; + + events.push_back(socket_ev); + + { + CritScope cr(&crit_); + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher * disp = dispatchers_[i]; + if (!process_io && (disp != signal_wakeup_)) + continue; + SOCKET s = disp->GetSocket(); + if (disp->CheckSignalClose()) { + // We just signalled close, don't poll this socket + } else if (s != INVALID_SOCKET) { + WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents())); + } else { + events.push_back(disp->GetWSAEvent()); + event_owners.push_back(disp); + } + } + } + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == -1) { + cmsNext = cmsWait; + } else { + cmsNext = cmsTotal - cmsElapsed; + if (cmsNext < 0) + cmsNext = 0; + } + + // Wait for one of the events to signal + DWORD dw = WSAWaitForMultipleEvents(static_cast(events.size()), &events[0], false, cmsNext, false); + +#if 0 // LOGGING + // we track this information purely for logging purposes. + last_tick_dispatch_count_++; + if (last_tick_dispatch_count_ >= 1000) { + uint32 now = GetMillisecondCount(); + LOG(INFO) << "PhysicalSocketServer took " << TimeDiff(now, last_tick_tracked_) << "ms for 1000 events"; + + // If we get more than 1000 events in a second, we are spinning badly + // (normally it should take about 8-20 seconds). + assert(TimeDiff(now, last_tick_tracked_) > 1000); + + last_tick_tracked_ = now; + last_tick_dispatch_count_ = 0; + } +#endif + + // Failed? + // todo: need a better strategy than this! + + if (dw == WSA_WAIT_FAILED) { + int error = WSAGetLastError(); + assert(false); + WSACloseEvent(socket_ev); + return false; + } + + // Timeout? + + if (dw == WSA_WAIT_TIMEOUT) { + WSACloseEvent(socket_ev); + return true; + } + + // Figure out which one it is and call it + + { + CritScope cr(&crit_); + int index = dw - WSA_WAIT_EVENT_0; + if (index > 0) { + --index; // The first event is the socket event + event_owners[index]->OnPreEvent(0); + event_owners[index]->OnEvent(0, 0); + } else if (process_io) { + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher * disp = dispatchers_[i]; + SOCKET s = disp->GetSocket(); + if (s == INVALID_SOCKET) + continue; + + WSANETWORKEVENTS wsaEvents; + int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents); + if (err == 0) { + +#if LOGGING + { + if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + } +#endif + uint32 ff = 0; + int errcode = 0; + if (wsaEvents.lNetworkEvents & FD_READ) + ff |= kfRead; + if (wsaEvents.lNetworkEvents & FD_WRITE) + ff |= kfWrite; + if (wsaEvents.lNetworkEvents & FD_CONNECT) { + if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) { + ff |= kfConnect; + } else { + // TODO: Decide whether we want to signal connect, but with an error code + ff |= kfClose; + errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + } + if (wsaEvents.lNetworkEvents & FD_ACCEPT) + ff |= kfRead; + if (wsaEvents.lNetworkEvents & FD_CLOSE) { + ff |= kfClose; + errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + if (ff != 0) { + disp->OnPreEvent(ff); + disp->OnEvent(ff, errcode); + } + } + } + } + + // Reset the network event until new activity occurs + WSAResetEvent(socket_ev); + } + + // Break? + + if (!fWait_) + break; + cmsElapsed = GetMillisecondCount() - msStart; + if (cmsWait != -1) { + if (cmsElapsed >= cmsWait) + break; + } + } + + // Done + + WSACloseEvent(socket_ev); + return true; +} +#endif // WIN32 + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc deleted file mode 100644 index 99f663b2..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc +++ /dev/null @@ -1,1131 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif - -#include - -#ifdef WIN32 -#include -#include -#define _WINSOCKAPI_ -#include -#include // HTTP_STATUS_PROXY_AUTH_REQ -#define SECURITY_WIN32 -#include -#endif - -#include -#include - -#include "talk/base/base64.h" -#include "talk/base/basicdefs.h" -#include "talk/base/bytebuffer.h" -#include "talk/base/common.h" -#include "talk/base/logging.h" -#include "talk/base/md5.h" -#include "talk/base/socketadapters.h" -#include "talk/base/stringutils.h" - -#include - - -#ifdef WIN32 -#include "talk/base/sec_buffer.h" -#endif // WIN32 - -namespace cricket { - -#ifdef WIN32 -extern const ConstantLabel SECURITY_ERRORS[]; -#endif - -BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size) - : AsyncSocketAdapter(socket), buffer_size_(buffer_size), data_len_(0), buffering_(false) { - buffer_ = new char[buffer_size_]; -} - -BufferedReadAdapter::~BufferedReadAdapter() { - delete [] buffer_; -} - -int BufferedReadAdapter::Send(const void *pv, size_t cb) { - if (buffering_) { - // TODO: Spoof error better; Signal Writeable - socket_->SetError(EWOULDBLOCK); - return -1; - } - return AsyncSocketAdapter::Send(pv, cb); -} - -int BufferedReadAdapter::Recv(void *pv, size_t cb) { - if (buffering_) { - socket_->SetError(EWOULDBLOCK); - return -1; - } - - size_t read = 0; - - if (data_len_) { - read = _min(cb, data_len_); - memcpy(pv, buffer_, read); - data_len_ -= read; - if (data_len_ > 0) { - memmove(buffer_, buffer_ + read, data_len_); - } - pv = static_cast(pv) + read; - cb -= read; - } - - // FIX: If cb == 0, we won't generate another read event - - int res = AsyncSocketAdapter::Recv(pv, cb); - if (res < 0) - return res; - - return res + static_cast(read); -} - -void BufferedReadAdapter::BufferInput(bool on) { - buffering_ = on; -} - -void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) { - assert(socket == socket_); - - if (!buffering_) { - AsyncSocketAdapter::OnReadEvent(socket); - return; - } - - if (data_len_ >= buffer_size_) { - LOG(INFO) << "Input buffer overflow"; - assert(false); - data_len_ = 0; - } - - int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_); - if (len < 0) { - // TODO: Do something better like forwarding the error to the user. - LOG(INFO) << "Recv: " << errno << " " << std::strerror(errno); - return; - } - - data_len_ += len; - - ProcessInput(buffer_, data_len_); -} - -/////////////////////////////////////////////////////////////////////////////// - -const uint8 SSL_SERVER_HELLO[] = { - 22,3,1,0,74,2,0,0,70,3,1,66,133,69,167,39,169,93,160, - 179,197,231,83,218,72,43,63,198,90,202,137,193,88,82, - 161,120,60,91,23,70,0,133,63,32,14,211,6,114,91,91, - 27,95,21,172,19,249,136,83,157,155,232,61,123,12,48, - 50,110,56,77,162,117,87,65,108,52,92,0,4,0 -}; - -const signed char SSL_CLIENT_HELLO[] = { - -128,70,1,3,1,0,45,0,0,0,16,1,0,-128,3,0,-128,7,0,-64,6,0,64,2,0, - -128,4,0,-128,0,0,4,0,-2,-1,0,0,10,0,-2,-2,0,0,9,0,0,100,0,0,98,0, - 0,3,0,0,6,31,23,12,-90,47,0,120,-4,70,85,46,-79,-125,57,-15,-22 -}; - -AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) : BufferedReadAdapter(socket, 1024) { -} - -int AsyncSSLSocket::Connect(const SocketAddress& addr) { - // Begin buffering before we connect, so that there isn't a race condition between - // potential senders and receiving the OnConnectEvent signal - BufferInput(true); - return BufferedReadAdapter::Connect(addr); -} - -void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) { - assert(socket == socket_); - - // TODO: we could buffer output too... - int res = DirectSend(SSL_CLIENT_HELLO, sizeof(SSL_CLIENT_HELLO)); - assert(res == sizeof(SSL_CLIENT_HELLO)); -} - -void AsyncSSLSocket::ProcessInput(char * data, size_t& len) { - if (len < sizeof(SSL_SERVER_HELLO)) - return; - - if (memcmp(SSL_SERVER_HELLO, data, sizeof(SSL_SERVER_HELLO)) != 0) { - Close(); - SignalCloseEvent(this, 0); // TODO: error code? - return; - } - - len -= sizeof(SSL_SERVER_HELLO); - if (len > 0) { - memmove(data, data + sizeof(SSL_SERVER_HELLO), len); - } - - bool remainder = (len > 0); - BufferInput(false); - SignalConnectEvent(this); - - // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble - if (remainder) - SignalReadEvent(this); -} - -/////////////////////////////////////////////////////////////////////////////// - -#define TEST_DIGEST 0 -#if TEST_DIGEST -/* -const char * const DIGEST_CHALLENGE = - "Digest realm=\"testrealm@host.com\"," - " qop=\"auth,auth-int\"," - " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," - " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; -const char * const DIGEST_METHOD = "GET"; -const char * const DIGEST_URI = - "/dir/index.html";; -const char * const DIGEST_CNONCE = - "0a4f113b"; -const char * const DIGEST_RESPONSE = - "6629fae49393a05397450978507c4ef1"; -//user_ = "Mufasa"; -//pass_ = "Circle Of Life"; -*/ -const char * const DIGEST_CHALLENGE = - "Digest realm=\"Squid proxy-caching web server\"," - " nonce=\"Nny4QuC5PwiSDixJ\"," - " qop=\"auth\"," - " stale=false"; -const char * const DIGEST_URI = - "/"; -const char * const DIGEST_CNONCE = - "6501d58e9a21cee1e7b5fec894ded024"; -const char * const DIGEST_RESPONSE = - "edffcb0829e755838b073a4a42de06bc"; -#endif - -static std::string MD5(const std::string& data) { - MD5_CTX ctx; - MD5Init(&ctx); - MD5Update(&ctx, const_cast(reinterpret_cast(data.data())), static_cast(data.size())); - unsigned char digest[16]; - MD5Final(digest, &ctx); - std::string hex_digest; - const char HEX[] = "0123456789abcdef"; - for (int i=0; i<16; ++i) { - hex_digest += HEX[digest[i] >> 4]; - hex_digest += HEX[digest[i] & 0xf]; - } - return hex_digest; -} - -static std::string Quote(const std::string& str) { - std::string result; - result.push_back('"'); - for (size_t i=0; i args; - ParseAuth(challenge, len, auth_method, args); - - if (context && (context->auth_method != auth_method)) - return AR_IGNORE; - - // BASIC - if (stricmp(auth_method.c_str(), "basic") == 0) { - if (context) - return AR_CREDENTIALS; // Bad credentials - if (username.empty()) - return AR_CREDENTIALS; // Missing credentials - - context = new AuthContext(auth_method); - - // TODO: convert sensitive to a secure buffer that gets securely deleted - //std::string decoded = username + ":" + password; - size_t len = username.size() + password.GetLength() + 2; - char * sensitive = new char[len]; - size_t pos = strcpyn(sensitive, len, username.data(), username.size()); - pos += strcpyn(sensitive + pos, len - pos, ":"); - password.CopyTo(sensitive + pos, true); - - response = auth_method; - response.append(" "); - // TODO: create a sensitive-source version of Base64::encode - response.append(Base64::encode(sensitive)); - memset(sensitive, 0, len); - delete [] sensitive; - return AR_RESPONSE; - } - - // DIGEST - if (stricmp(auth_method.c_str(), "digest") == 0) { - if (context) - return AR_CREDENTIALS; // Bad credentials - if (username.empty()) - return AR_CREDENTIALS; // Missing credentials - - context = new AuthContext(auth_method); - - std::string cnonce, ncount; -#if TEST_DIGEST - method = DIGEST_METHOD; - uri = DIGEST_URI; - cnonce = DIGEST_CNONCE; -#else - char buffer[256]; - sprintf(buffer, "%d", time(0)); - cnonce = MD5(buffer); -#endif - ncount = "00000001"; - - // TODO: convert sensitive to be secure buffer - //std::string A1 = username + ":" + args["realm"] + ":" + password; - size_t len = username.size() + args["realm"].size() + password.GetLength() + 3; - char * sensitive = new char[len]; // A1 - size_t pos = strcpyn(sensitive, len, username.data(), username.size()); - pos += strcpyn(sensitive + pos, len - pos, ":"); - pos += strcpyn(sensitive + pos, len - pos, args["realm"].c_str()); - pos += strcpyn(sensitive + pos, len - pos, ":"); - password.CopyTo(sensitive + pos, true); - - std::string A2 = method + ":" + uri; - std::string middle; - if (args.find("qop") != args.end()) { - args["qop"] = "auth"; - middle = args["nonce"] + ":" + ncount + ":" + cnonce + ":" + args["qop"]; - } else { - middle = args["nonce"]; - } - std::string HA1 = MD5(sensitive); - memset(sensitive, 0, len); - delete [] sensitive; - std::string HA2 = MD5(A2); - std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2); - -#if TEST_DIGEST - assert(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0); -#endif - - std::stringstream ss; - ss << auth_method; - ss << " username=" << Quote(username); - ss << ", realm=" << Quote(args["realm"]); - ss << ", nonce=" << Quote(args["nonce"]); - ss << ", uri=" << Quote(uri); - if (args.find("qop") != args.end()) { - ss << ", qop=" << args["qop"]; - ss << ", nc=" << ncount; - ss << ", cnonce=" << Quote(cnonce); - } - ss << ", response=\"" << dig_response << "\""; - if (args.find("opaque") != args.end()) { - ss << ", opaque=" << Quote(args["opaque"]); - } - response = ss.str(); - return AR_RESPONSE; - } - -#ifdef WIN32 -#if 1 - bool want_negotiate = (stricmp(auth_method.c_str(), "negotiate") == 0); - bool want_ntlm = (stricmp(auth_method.c_str(), "ntlm") == 0); - // SPNEGO & NTLM - if (want_negotiate || want_ntlm) { - const size_t MAX_MESSAGE = 12000, MAX_SPN = 256; - char out_buf[MAX_MESSAGE], spn[MAX_SPN]; - -#if 0 // Requires funky windows versions - DWORD len = MAX_SPN; - if (DsMakeSpn("HTTP", server.IPAsString().c_str(), NULL, server.port(), 0, &len, spn) != ERROR_SUCCESS) { - LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) - DsMakeSpn failed"; - return AR_IGNORE; - } -#else - sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str()); -#endif - - SecBuffer out_sec; - out_sec.pvBuffer = out_buf; - out_sec.cbBuffer = sizeof(out_buf); - out_sec.BufferType = SECBUFFER_TOKEN; - - SecBufferDesc out_buf_desc; - out_buf_desc.ulVersion = 0; - out_buf_desc.cBuffers = 1; - out_buf_desc.pBuffers = &out_sec; - - const ULONG NEG_FLAGS_DEFAULT = - //ISC_REQ_ALLOCATE_MEMORY - ISC_REQ_CONFIDENTIALITY - //| ISC_REQ_EXTENDED_ERROR - //| ISC_REQ_INTEGRITY - | ISC_REQ_REPLAY_DETECT - | ISC_REQ_SEQUENCE_DETECT - //| ISC_REQ_STREAM - //| ISC_REQ_USE_SUPPLIED_CREDS - ; - - TimeStamp lifetime; - SECURITY_STATUS ret = S_OK; - ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT; - - bool specify_credentials = !username.empty(); - size_t steps = 0; - - //uint32 now = cricket::Time(); - - NegotiateAuthContext * neg = static_cast(context); - if (neg) { - const size_t max_steps = 10; - if (++neg->steps >= max_steps) { - LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries"; - return AR_ERROR; - } - steps = neg->steps; - - std::string decoded_challenge = Base64::decode(args[""]); - if (!decoded_challenge.empty()) { - SecBuffer in_sec; - in_sec.pvBuffer = const_cast(decoded_challenge.data()); - in_sec.cbBuffer = static_cast(decoded_challenge.size()); - in_sec.BufferType = SECBUFFER_TOKEN; - - SecBufferDesc in_buf_desc; - in_buf_desc.ulVersion = 0; - in_buf_desc.cBuffers = 1; - in_buf_desc.pBuffers = &in_sec; - - ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime); - //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now); - if (FAILED(ret)) { - LOG(LS_ERROR) << "InitializeSecurityContext returned: " - << ErrorName(ret, SECURITY_ERRORS); - return AR_ERROR; - } - } else if (neg->specified_credentials) { - // Try again with default credentials - specify_credentials = false; - delete context; - context = neg = 0; - } else { - return AR_CREDENTIALS; - } - } - - if (!neg) { - unsigned char userbuf[256], passbuf[256], domainbuf[16]; - SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0; - if (specify_credentials) { - memset(&auth_id, 0, sizeof(auth_id)); - size_t len = password.GetLength()+1; - char * sensitive = new char[len]; - password.CopyTo(sensitive, true); - std::string::size_type pos = username.find('\\'); - if (pos == std::string::npos) { - auth_id.UserLength = static_cast( - _min(sizeof(userbuf) - 1, username.size())); - memcpy(userbuf, username.c_str(), auth_id.UserLength); - userbuf[auth_id.UserLength] = 0; - auth_id.DomainLength = 0; - domainbuf[auth_id.DomainLength] = 0; - auth_id.PasswordLength = static_cast( - _min(sizeof(passbuf) - 1, password.GetLength())); - memcpy(passbuf, sensitive, auth_id.PasswordLength); - passbuf[auth_id.PasswordLength] = 0; - } else { - auth_id.UserLength = static_cast( - _min(sizeof(userbuf) - 1, username.size() - pos - 1)); - memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); - userbuf[auth_id.UserLength] = 0; - auth_id.DomainLength = static_cast( - _min(sizeof(domainbuf) - 1, pos)); - memcpy(domainbuf, username.c_str(), auth_id.DomainLength); - domainbuf[auth_id.DomainLength] = 0; - auth_id.PasswordLength = static_cast( - _min(sizeof(passbuf) - 1, password.GetLength())); - memcpy(passbuf, sensitive, auth_id.PasswordLength); - passbuf[auth_id.PasswordLength] = 0; - } - memset(sensitive, 0, len); - delete [] sensitive; - auth_id.User = userbuf; - auth_id.Domain = domainbuf; - auth_id.Password = passbuf; - auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; - pauth_id = &auth_id; - LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials"; - } else { - LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials"; - } - - CredHandle cred; - ret = AcquireCredentialsHandleA(0, want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A, SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime); - //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << cricket::TimeDiff(cricket::Time(), now); - if (ret != SEC_E_OK) { - LOG(LS_ERROR) << "AcquireCredentialsHandle error: " - << ErrorName(ret, SECURITY_ERRORS); - return AR_IGNORE; - } - - //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out; - - CtxtHandle ctx; - ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime); - //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now); - if (FAILED(ret)) { - LOG(LS_ERROR) << "InitializeSecurityContext returned: " - << ErrorName(ret, SECURITY_ERRORS); - FreeCredentialsHandle(&cred); - return AR_IGNORE; - } - - assert(!context); - context = neg = new NegotiateAuthContext(auth_method, cred, ctx); - neg->specified_credentials = specify_credentials; - neg->steps = steps; - } - - if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) { - ret = CompleteAuthToken(&neg->ctx, &out_buf_desc); - //LOG(INFO) << "$$$ CompleteAuthToken @ " << cricket::TimeDiff(cricket::Time(), now); - LOG(LS_VERBOSE) << "CompleteAuthToken returned: " - << ErrorName(ret, SECURITY_ERRORS); - if (FAILED(ret)) { - return AR_ERROR; - } - } - - //LOG(INFO) << "$$$ NEGOTIATE took " << cricket::TimeDiff(cricket::Time(), now) << "ms"; - - std::string decoded(out_buf, out_buf + out_sec.cbBuffer); - response = auth_method; - response.append(" "); - response.append(Base64::encode(decoded)); - return AR_RESPONSE; - } -#endif -#endif // WIN32 - - return AR_IGNORE; -} - -inline bool end_of_name(size_t pos, size_t len, const char * data) { - if (pos >= len) - return true; - if (isspace(data[pos])) - return true; - // The reason for this complexity is that some non-compliant auth schemes (like Negotiate) - // use base64 tokens in the challenge instead of name=value. This could confuse us when the - // base64 ends in equal signs. - if ((pos+1 < len) && (data[pos] == '=') && !isspace(data[pos+1]) && (data[pos+1] != '=')) - return true; - return false; -} - -void AsyncHttpsProxySocket::ParseAuth(const char * data, size_t len, std::string& method, std::map& args) { - size_t pos = 0; - while ((pos < len) && isspace(data[pos])) ++pos; - size_t start = pos; - while ((pos < len) && !isspace(data[pos])) ++pos; - method.assign(data + start, data + pos); - while (pos < len) { - while ((pos < len) && isspace(data[pos])) ++pos; - if (pos >= len) - return; - - start = pos; - while (!end_of_name(pos, len, data)) ++pos; - //while ((pos < len) && !isspace(data[pos]) && (data[pos] != '=')) ++pos; - std::string name(data + start, data + pos), value; - - if ((pos < len) && (data[pos] == '=')) { - ++pos; // Skip '=' - // Check if quoted value - if ((pos < len) && (data[pos] == '"')) { - while (++pos < len) { - if (data[pos] == '"') { - ++pos; - break; - } - if ((data[pos] == '\\') && (pos + 1 < len)) - ++pos; - value.append(1, data[pos]); - } - } else { - while ((pos < len) && !isspace(data[pos]) && (data[pos] != ',')) - value.append(1, data[pos++]); - } - } else { - value = name; - name.clear(); - } - - args.insert(std::make_pair(name, value)); - if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ',' - } -} - -AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket, const SocketAddress& proxy, - const std::string& username, const buzz::XmppPassword& password) - : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), - state_(PS_ERROR), context_(0) { -} - -AsyncHttpsProxySocket::~AsyncHttpsProxySocket() { - delete context_; -} - -int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) { - LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect(" << proxy_.ToString() << ")"; - dest_ = addr; - if (dest_.port() != 80) { - BufferInput(true); - } - return BufferedReadAdapter::Connect(proxy_); -} - -SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const { - return dest_; -} - -int AsyncHttpsProxySocket::Close() { - headers_.clear(); - state_ = PS_ERROR; - delete context_; - context_ = 0; - return BufferedReadAdapter::Close(); -} - -void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) { - LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent"; - // TODO: Decide whether tunneling or not should be explicitly set, - // or indicated by destination port (as below) - if (dest_.port() == 80) { - state_ = PS_TUNNEL; - BufferedReadAdapter::OnConnectEvent(socket); - return; - } - SendRequest(); -} - -void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) { - LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")"; - if ((state_ == PS_WAIT_CLOSE) && (err == 0)) { - state_ = PS_ERROR; - Connect(dest_); - } else { - BufferedReadAdapter::OnCloseEvent(socket, err); - } -} - -void AsyncHttpsProxySocket::ProcessInput(char * data, size_t& len) { - size_t start = 0; - for (size_t pos = start; (state_ < PS_TUNNEL) && (pos < len); ) { - if (state_ == PS_SKIP_BODY) { - size_t consume = _min(len - pos, content_length_); - pos += consume; - start = pos; - content_length_ -= consume; - if (content_length_ == 0) { - EndResponse(); - } - continue; - } - - if (data[pos++] != '\n') - continue; - - size_t len = pos - start - 1; - if ((len > 0) && (data[start + len - 1] == '\r')) - --len; - - data[start + len] = 0; - ProcessLine(data + start, len); - start = pos; - } - - len -= start; - if (len > 0) { - memmove(data, data + start, len); - } - - if (state_ != PS_TUNNEL) - return; - - bool remainder = (len > 0); - BufferInput(false); - SignalConnectEvent(this); - - // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble - if (remainder) - SignalReadEvent(this); // TODO: signal this?? -} - -void AsyncHttpsProxySocket::SendRequest() { - std::stringstream ss; - ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n"; - ss << "User-Agent: " USERAGENT_STRING "\r\n"; - ss << "Host: " << dest_.IPAsString() << "\r\n"; - ss << "Content-Length: 0\r\n"; - ss << "Proxy-Connection: Keep-Alive\r\n"; - ss << headers_; - ss << "\r\n"; - std::string str = ss.str(); - DirectSend(str.c_str(), str.size()); - state_ = PS_LEADER; - expect_close_ = true; - content_length_ = 0; - headers_.clear(); - - LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str; -} - -void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) { - LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data; - - if (len == 0) { - if (state_ == PS_TUNNEL_HEADERS) { - state_ = PS_TUNNEL; - } else if (state_ == PS_ERROR_HEADERS) { - Error(defer_error_); - return; - } else if (state_ == PS_SKIP_HEADERS) { - if (content_length_) { - state_ = PS_SKIP_BODY; - } else { - EndResponse(); - return; - } - } else { - static bool report = false; - if (!unknown_mechanisms_.empty() && !report) { - report = true; - std::string msg( - "Unable to connect to the Google Talk service due to an incompatibility " - "with your proxy.\r\nPlease help us resolve this issue by submitting the " - "following information to us using our technical issue submission form " - "at:\r\n\r\n" - "http://www.google.com/support/talk/bin/request.py\r\n\r\n" - "We apologize for the inconvenience.\r\n\r\n" - "Information to submit to Google: " - ); - //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: "); - msg.append(unknown_mechanisms_); -#ifdef WIN32 - MessageBoxA(0, msg.c_str(), "Oops!", MB_OK); -#endif -#ifdef POSIX - //TODO: Raise a signal or something so the UI can be separated. - LOG(LS_ERROR) << "Oops!\n\n" << msg; -#endif - } - // Unexpected end of headers - Error(0); - return; - } - } else if (state_ == PS_LEADER) { - uint32 code; - if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) { - Error(0); - return; - } - switch (code) { - case 200: - // connection good! - state_ = PS_TUNNEL_HEADERS; - return; -#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407) -#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ -#endif - case 407: // HTTP_STATUS_PROXY_AUTH_REQ - state_ = PS_AUTHENTICATE; - return; - default: - defer_error_ = 0; - state_ = PS_ERROR_HEADERS; - return; - } - } else if ((state_ == PS_AUTHENTICATE) && (strnicmp(data, "Proxy-Authenticate:", 19) == 0)) { - std::string response, auth_method; - switch (Authenticate(data + 19, len - 19, proxy_, "CONNECT", "/", user_, pass_, context_, response, auth_method)) { - case AR_IGNORE: - LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method; - if (!unknown_mechanisms_.empty()) - unknown_mechanisms_.append(", "); - unknown_mechanisms_.append(auth_method); - break; - case AR_RESPONSE: - headers_ = "Proxy-Authorization: "; - headers_.append(response); - headers_.append("\r\n"); - state_ = PS_SKIP_HEADERS; - unknown_mechanisms_.clear(); - break; - case AR_CREDENTIALS: - defer_error_ = EACCES; - state_ = PS_ERROR_HEADERS; - unknown_mechanisms_.clear(); - break; - case AR_ERROR: - defer_error_ = 0; - state_ = PS_ERROR_HEADERS; - unknown_mechanisms_.clear(); - break; - } - } else if (strnicmp(data, "Content-Length:", 15) == 0) { - content_length_ = strtoul(data + 15, 0, 0); - } else if (strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) { - expect_close_ = false; - /* - } else if (strnicmp(data, "Connection: close", 17) == 0) { - expect_close_ = true; - */ - } -} - -void AsyncHttpsProxySocket::EndResponse() { - if (!expect_close_) { - SendRequest(); - return; - } - - // No point in waiting for the server to close... let's close now - // TODO: Refactor out PS_WAIT_CLOSE - state_ = PS_WAIT_CLOSE; - BufferedReadAdapter::Close(); - OnCloseEvent(this, 0); -} - -void AsyncHttpsProxySocket::Error(int error) { - BufferInput(false); - Close(); - SetError(error); - SignalCloseEvent(this, error); -} - -/////////////////////////////////////////////////////////////////////////////// - -AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, - const std::string& username, const buzz::XmppPassword& password) - : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), - state_(SS_ERROR) { -} - -int AsyncSocksProxySocket::Connect(const SocketAddress& addr) { - dest_ = addr; - BufferInput(true); - return BufferedReadAdapter::Connect(proxy_); -} - -SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const { - return dest_; -} - -void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket * socket) { - SendHello(); -} - -void AsyncSocksProxySocket::ProcessInput(char * data, size_t& len) { - assert(state_ < SS_TUNNEL); - - ByteBuffer response(data, len); - - if (state_ == SS_HELLO) { - uint8 ver, method; - if (!response.ReadUInt8(ver) || - !response.ReadUInt8(method)) - return; - - if (ver != 5) { - Error(0); - return; - } - - if (method == 0) { - SendConnect(); - } else if (method == 2) { - SendAuth(); - } else { - Error(0); - return; - } - } else if (state_ == SS_AUTH) { - uint8 ver, status; - if (!response.ReadUInt8(ver) || - !response.ReadUInt8(status)) - return; - - if ((ver != 1) || (status != 0)) { - Error(EACCES); - return; - } - - SendConnect(); - } else if (state_ == SS_CONNECT) { - uint8 ver, rep, rsv, atyp; - if (!response.ReadUInt8(ver) || - !response.ReadUInt8(rep) || - !response.ReadUInt8(rsv) || - !response.ReadUInt8(atyp)) - return; - - if ((ver != 5) || (rep != 0)) { - Error(0); - return; - } - - uint16 port; - if (atyp == 1) { - uint32 addr; - if (!response.ReadUInt32(addr) || - !response.ReadUInt16(port)) - return; - LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; - } else if (atyp == 3) { - uint8 len; - std::string addr; - if (!response.ReadUInt8(len) || - !response.ReadString(addr, len) || - !response.ReadUInt16(port)) - return; - LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; - } else if (atyp == 4) { - std::string addr; - if (!response.ReadString(addr, 16) || - !response.ReadUInt16(port)) - return; - LOG(LS_VERBOSE) << "Bound on :" << port; - } else { - Error(0); - return; - } - - state_ = SS_TUNNEL; - } - - // Consume parsed data - len = response.Length(); - memcpy(data, response.Data(), len); - - if (state_ != SS_TUNNEL) - return; - - bool remainder = (len > 0); - BufferInput(false); - SignalConnectEvent(this); - - // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble - if (remainder) - SignalReadEvent(this); // TODO: signal this?? -} - -void AsyncSocksProxySocket::SendHello() { - ByteBuffer request; - request.WriteUInt8(5); // Socks Version - if (user_.empty()) { - request.WriteUInt8(1); // Authentication Mechanisms - request.WriteUInt8(0); // No authentication - } else { - request.WriteUInt8(2); // Authentication Mechanisms - request.WriteUInt8(0); // No authentication - request.WriteUInt8(2); // Username/Password - } - DirectSend(request.Data(), request.Length()); - state_ = SS_HELLO; -} - -void AsyncSocksProxySocket::SendAuth() { - ByteBuffer request; - request.WriteUInt8(1); // Negotiation Version - request.WriteUInt8(static_cast(user_.size())); - request.WriteString(user_); // Username - request.WriteUInt8(static_cast(pass_.GetLength())); - size_t len = pass_.GetLength() + 1; - char * sensitive = new char[len]; - pass_.CopyTo(sensitive, true); - request.WriteString(sensitive); // Password - memset(sensitive, 0, len); - delete [] sensitive; - DirectSend(request.Data(), request.Length()); - state_ = SS_AUTH; -} - -void AsyncSocksProxySocket::SendConnect() { - ByteBuffer request; - request.WriteUInt8(5); // Socks Version - request.WriteUInt8(1); // CONNECT - request.WriteUInt8(0); // Reserved - if (dest_.IsUnresolved()) { - std::string hostname = dest_.IPAsString(); - request.WriteUInt8(3); // DOMAINNAME - request.WriteUInt8(static_cast(hostname.size())); - request.WriteString(hostname); // Destination Hostname - } else { - request.WriteUInt8(1); // IPV4 - request.WriteUInt32(dest_.ip()); // Destination IP - } - request.WriteUInt16(dest_.port()); // Destination Port - DirectSend(request.Data(), request.Length()); - state_ = SS_CONNECT; -} - -void AsyncSocksProxySocket::Error(int error) { - state_ = SS_ERROR; - BufferInput(false); - Close(); - SetError(EACCES); - SignalCloseEvent(this, error); -} - -/////////////////////////////////////////////////////////////////////////////// - -LoggingAdapter::LoggingAdapter(AsyncSocket* socket, LoggingSeverity level, - const char * label) - : AsyncSocketAdapter(socket), level_(level) -{ - label_.append("["); - label_.append(label); - label_.append("]"); -} - -int -LoggingAdapter::Send(const void *pv, size_t cb) { - int res = AsyncSocketAdapter::Send(pv, cb); - if (res > 0) - LogMultiline(false, static_cast(pv), res); - return res; -} - -int -LoggingAdapter::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { - int res = AsyncSocketAdapter::SendTo(pv, cb, addr); - if (res > 0) - LogMultiline(false, static_cast(pv), res); - return res; -} - -int -LoggingAdapter::Recv(void *pv, size_t cb) { - int res = AsyncSocketAdapter::Recv(pv, cb); - if (res > 0) - LogMultiline(true, static_cast(pv), res); - return res; -} - -int -LoggingAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { - int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); - if (res > 0) - LogMultiline(true, static_cast(pv), res); - return res; -} - -void -LoggingAdapter::OnConnectEvent(AsyncSocket * socket) { - LOG(level_) << label_ << " Connected"; - AsyncSocketAdapter::OnConnectEvent(socket); -} - -void -LoggingAdapter::OnCloseEvent(AsyncSocket * socket, int err) { - LOG(level_) << label_ << " Closed with error: " << err; - AsyncSocketAdapter::OnCloseEvent(socket, err); -} - -void -LoggingAdapter::LogMultiline(bool input, const char * data, size_t len) { - const char * direction = (input ? " << " : " >> "); - std::string str(data, len); - while (!str.empty()) { - std::string::size_type pos = str.find('\n'); - std::string substr = str; - if (pos == std::string::npos) { - substr = str; - str.clear(); - } else if ((pos > 0) && (str[pos-1] == '\r')) { - substr = str.substr(0, pos - 1); - str = str.substr(pos + 1); - } else { - substr = str.substr(0, pos); - str = str.substr(pos + 1); - } - - // Filter out any private data - std::string::size_type pos_private = substr.find("Email"); - if (pos_private == std::string::npos) { - pos_private = substr.find("Passwd"); - } - if (pos_private == std::string::npos) { - LOG(level_) << label_ << direction << substr; - } else { - LOG(level_) << label_ << direction << "## TEXT REMOVED ##"; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cpp new file mode 100644 index 00000000..99f663b2 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cpp @@ -0,0 +1,1131 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include + +#ifdef WIN32 +#include +#include +#define _WINSOCKAPI_ +#include +#include // HTTP_STATUS_PROXY_AUTH_REQ +#define SECURITY_WIN32 +#include +#endif + +#include +#include + +#include "talk/base/base64.h" +#include "talk/base/basicdefs.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/md5.h" +#include "talk/base/socketadapters.h" +#include "talk/base/stringutils.h" + +#include + + +#ifdef WIN32 +#include "talk/base/sec_buffer.h" +#endif // WIN32 + +namespace cricket { + +#ifdef WIN32 +extern const ConstantLabel SECURITY_ERRORS[]; +#endif + +BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size) + : AsyncSocketAdapter(socket), buffer_size_(buffer_size), data_len_(0), buffering_(false) { + buffer_ = new char[buffer_size_]; +} + +BufferedReadAdapter::~BufferedReadAdapter() { + delete [] buffer_; +} + +int BufferedReadAdapter::Send(const void *pv, size_t cb) { + if (buffering_) { + // TODO: Spoof error better; Signal Writeable + socket_->SetError(EWOULDBLOCK); + return -1; + } + return AsyncSocketAdapter::Send(pv, cb); +} + +int BufferedReadAdapter::Recv(void *pv, size_t cb) { + if (buffering_) { + socket_->SetError(EWOULDBLOCK); + return -1; + } + + size_t read = 0; + + if (data_len_) { + read = _min(cb, data_len_); + memcpy(pv, buffer_, read); + data_len_ -= read; + if (data_len_ > 0) { + memmove(buffer_, buffer_ + read, data_len_); + } + pv = static_cast(pv) + read; + cb -= read; + } + + // FIX: If cb == 0, we won't generate another read event + + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res < 0) + return res; + + return res + static_cast(read); +} + +void BufferedReadAdapter::BufferInput(bool on) { + buffering_ = on; +} + +void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) { + assert(socket == socket_); + + if (!buffering_) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (data_len_ >= buffer_size_) { + LOG(INFO) << "Input buffer overflow"; + assert(false); + data_len_ = 0; + } + + int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "Recv: " << errno << " " << std::strerror(errno); + return; + } + + data_len_ += len; + + ProcessInput(buffer_, data_len_); +} + +/////////////////////////////////////////////////////////////////////////////// + +const uint8 SSL_SERVER_HELLO[] = { + 22,3,1,0,74,2,0,0,70,3,1,66,133,69,167,39,169,93,160, + 179,197,231,83,218,72,43,63,198,90,202,137,193,88,82, + 161,120,60,91,23,70,0,133,63,32,14,211,6,114,91,91, + 27,95,21,172,19,249,136,83,157,155,232,61,123,12,48, + 50,110,56,77,162,117,87,65,108,52,92,0,4,0 +}; + +const signed char SSL_CLIENT_HELLO[] = { + -128,70,1,3,1,0,45,0,0,0,16,1,0,-128,3,0,-128,7,0,-64,6,0,64,2,0, + -128,4,0,-128,0,0,4,0,-2,-1,0,0,10,0,-2,-2,0,0,9,0,0,100,0,0,98,0, + 0,3,0,0,6,31,23,12,-90,47,0,120,-4,70,85,46,-79,-125,57,-15,-22 +}; + +AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) : BufferedReadAdapter(socket, 1024) { +} + +int AsyncSSLSocket::Connect(const SocketAddress& addr) { + // Begin buffering before we connect, so that there isn't a race condition between + // potential senders and receiving the OnConnectEvent signal + BufferInput(true); + return BufferedReadAdapter::Connect(addr); +} + +void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) { + assert(socket == socket_); + + // TODO: we could buffer output too... + int res = DirectSend(SSL_CLIENT_HELLO, sizeof(SSL_CLIENT_HELLO)); + assert(res == sizeof(SSL_CLIENT_HELLO)); +} + +void AsyncSSLSocket::ProcessInput(char * data, size_t& len) { + if (len < sizeof(SSL_SERVER_HELLO)) + return; + + if (memcmp(SSL_SERVER_HELLO, data, sizeof(SSL_SERVER_HELLO)) != 0) { + Close(); + SignalCloseEvent(this, 0); // TODO: error code? + return; + } + + len -= sizeof(SSL_SERVER_HELLO); + if (len > 0) { + memmove(data, data + sizeof(SSL_SERVER_HELLO), len); + } + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); +} + +/////////////////////////////////////////////////////////////////////////////// + +#define TEST_DIGEST 0 +#if TEST_DIGEST +/* +const char * const DIGEST_CHALLENGE = + "Digest realm=\"testrealm@host.com\"," + " qop=\"auth,auth-int\"," + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; +const char * const DIGEST_METHOD = "GET"; +const char * const DIGEST_URI = + "/dir/index.html";; +const char * const DIGEST_CNONCE = + "0a4f113b"; +const char * const DIGEST_RESPONSE = + "6629fae49393a05397450978507c4ef1"; +//user_ = "Mufasa"; +//pass_ = "Circle Of Life"; +*/ +const char * const DIGEST_CHALLENGE = + "Digest realm=\"Squid proxy-caching web server\"," + " nonce=\"Nny4QuC5PwiSDixJ\"," + " qop=\"auth\"," + " stale=false"; +const char * const DIGEST_URI = + "/"; +const char * const DIGEST_CNONCE = + "6501d58e9a21cee1e7b5fec894ded024"; +const char * const DIGEST_RESPONSE = + "edffcb0829e755838b073a4a42de06bc"; +#endif + +static std::string MD5(const std::string& data) { + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, const_cast(reinterpret_cast(data.data())), static_cast(data.size())); + unsigned char digest[16]; + MD5Final(digest, &ctx); + std::string hex_digest; + const char HEX[] = "0123456789abcdef"; + for (int i=0; i<16; ++i) { + hex_digest += HEX[digest[i] >> 4]; + hex_digest += HEX[digest[i] & 0xf]; + } + return hex_digest; +} + +static std::string Quote(const std::string& str) { + std::string result; + result.push_back('"'); + for (size_t i=0; i args; + ParseAuth(challenge, len, auth_method, args); + + if (context && (context->auth_method != auth_method)) + return AR_IGNORE; + + // BASIC + if (stricmp(auth_method.c_str(), "basic") == 0) { + if (context) + return AR_CREDENTIALS; // Bad credentials + if (username.empty()) + return AR_CREDENTIALS; // Missing credentials + + context = new AuthContext(auth_method); + + // TODO: convert sensitive to a secure buffer that gets securely deleted + //std::string decoded = username + ":" + password; + size_t len = username.size() + password.GetLength() + 2; + char * sensitive = new char[len]; + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + response = auth_method; + response.append(" "); + // TODO: create a sensitive-source version of Base64::encode + response.append(Base64::encode(sensitive)); + memset(sensitive, 0, len); + delete [] sensitive; + return AR_RESPONSE; + } + + // DIGEST + if (stricmp(auth_method.c_str(), "digest") == 0) { + if (context) + return AR_CREDENTIALS; // Bad credentials + if (username.empty()) + return AR_CREDENTIALS; // Missing credentials + + context = new AuthContext(auth_method); + + std::string cnonce, ncount; +#if TEST_DIGEST + method = DIGEST_METHOD; + uri = DIGEST_URI; + cnonce = DIGEST_CNONCE; +#else + char buffer[256]; + sprintf(buffer, "%d", time(0)); + cnonce = MD5(buffer); +#endif + ncount = "00000001"; + + // TODO: convert sensitive to be secure buffer + //std::string A1 = username + ":" + args["realm"] + ":" + password; + size_t len = username.size() + args["realm"].size() + password.GetLength() + 3; + char * sensitive = new char[len]; // A1 + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + pos += strcpyn(sensitive + pos, len - pos, args["realm"].c_str()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + std::string A2 = method + ":" + uri; + std::string middle; + if (args.find("qop") != args.end()) { + args["qop"] = "auth"; + middle = args["nonce"] + ":" + ncount + ":" + cnonce + ":" + args["qop"]; + } else { + middle = args["nonce"]; + } + std::string HA1 = MD5(sensitive); + memset(sensitive, 0, len); + delete [] sensitive; + std::string HA2 = MD5(A2); + std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2); + +#if TEST_DIGEST + assert(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0); +#endif + + std::stringstream ss; + ss << auth_method; + ss << " username=" << Quote(username); + ss << ", realm=" << Quote(args["realm"]); + ss << ", nonce=" << Quote(args["nonce"]); + ss << ", uri=" << Quote(uri); + if (args.find("qop") != args.end()) { + ss << ", qop=" << args["qop"]; + ss << ", nc=" << ncount; + ss << ", cnonce=" << Quote(cnonce); + } + ss << ", response=\"" << dig_response << "\""; + if (args.find("opaque") != args.end()) { + ss << ", opaque=" << Quote(args["opaque"]); + } + response = ss.str(); + return AR_RESPONSE; + } + +#ifdef WIN32 +#if 1 + bool want_negotiate = (stricmp(auth_method.c_str(), "negotiate") == 0); + bool want_ntlm = (stricmp(auth_method.c_str(), "ntlm") == 0); + // SPNEGO & NTLM + if (want_negotiate || want_ntlm) { + const size_t MAX_MESSAGE = 12000, MAX_SPN = 256; + char out_buf[MAX_MESSAGE], spn[MAX_SPN]; + +#if 0 // Requires funky windows versions + DWORD len = MAX_SPN; + if (DsMakeSpn("HTTP", server.IPAsString().c_str(), NULL, server.port(), 0, &len, spn) != ERROR_SUCCESS) { + LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) - DsMakeSpn failed"; + return AR_IGNORE; + } +#else + sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str()); +#endif + + SecBuffer out_sec; + out_sec.pvBuffer = out_buf; + out_sec.cbBuffer = sizeof(out_buf); + out_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc out_buf_desc; + out_buf_desc.ulVersion = 0; + out_buf_desc.cBuffers = 1; + out_buf_desc.pBuffers = &out_sec; + + const ULONG NEG_FLAGS_DEFAULT = + //ISC_REQ_ALLOCATE_MEMORY + ISC_REQ_CONFIDENTIALITY + //| ISC_REQ_EXTENDED_ERROR + //| ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + //| ISC_REQ_STREAM + //| ISC_REQ_USE_SUPPLIED_CREDS + ; + + TimeStamp lifetime; + SECURITY_STATUS ret = S_OK; + ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT; + + bool specify_credentials = !username.empty(); + size_t steps = 0; + + //uint32 now = cricket::Time(); + + NegotiateAuthContext * neg = static_cast(context); + if (neg) { + const size_t max_steps = 10; + if (++neg->steps >= max_steps) { + LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries"; + return AR_ERROR; + } + steps = neg->steps; + + std::string decoded_challenge = Base64::decode(args[""]); + if (!decoded_challenge.empty()) { + SecBuffer in_sec; + in_sec.pvBuffer = const_cast(decoded_challenge.data()); + in_sec.cbBuffer = static_cast(decoded_challenge.size()); + in_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc in_buf_desc; + in_buf_desc.ulVersion = 0; + in_buf_desc.cBuffers = 1; + in_buf_desc.pBuffers = &in_sec; + + ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + return AR_ERROR; + } + } else if (neg->specified_credentials) { + // Try again with default credentials + specify_credentials = false; + delete context; + context = neg = 0; + } else { + return AR_CREDENTIALS; + } + } + + if (!neg) { + unsigned char userbuf[256], passbuf[256], domainbuf[16]; + SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0; + if (specify_credentials) { + memset(&auth_id, 0, sizeof(auth_id)); + size_t len = password.GetLength()+1; + char * sensitive = new char[len]; + password.CopyTo(sensitive, true); + std::string::size_type pos = username.find('\\'); + if (pos == std::string::npos) { + auth_id.UserLength = static_cast( + _min(sizeof(userbuf) - 1, username.size())); + memcpy(userbuf, username.c_str(), auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = 0; + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } else { + auth_id.UserLength = static_cast( + _min(sizeof(userbuf) - 1, username.size() - pos - 1)); + memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = static_cast( + _min(sizeof(domainbuf) - 1, pos)); + memcpy(domainbuf, username.c_str(), auth_id.DomainLength); + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } + memset(sensitive, 0, len); + delete [] sensitive; + auth_id.User = userbuf; + auth_id.Domain = domainbuf; + auth_id.Password = passbuf; + auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + pauth_id = &auth_id; + LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials"; + } else { + LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials"; + } + + CredHandle cred; + ret = AcquireCredentialsHandleA(0, want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A, SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime); + //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << cricket::TimeDiff(cricket::Time(), now); + if (ret != SEC_E_OK) { + LOG(LS_ERROR) << "AcquireCredentialsHandle error: " + << ErrorName(ret, SECURITY_ERRORS); + return AR_IGNORE; + } + + //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out; + + CtxtHandle ctx; + ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + FreeCredentialsHandle(&cred); + return AR_IGNORE; + } + + assert(!context); + context = neg = new NegotiateAuthContext(auth_method, cred, ctx); + neg->specified_credentials = specify_credentials; + neg->steps = steps; + } + + if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) { + ret = CompleteAuthToken(&neg->ctx, &out_buf_desc); + //LOG(INFO) << "$$$ CompleteAuthToken @ " << cricket::TimeDiff(cricket::Time(), now); + LOG(LS_VERBOSE) << "CompleteAuthToken returned: " + << ErrorName(ret, SECURITY_ERRORS); + if (FAILED(ret)) { + return AR_ERROR; + } + } + + //LOG(INFO) << "$$$ NEGOTIATE took " << cricket::TimeDiff(cricket::Time(), now) << "ms"; + + std::string decoded(out_buf, out_buf + out_sec.cbBuffer); + response = auth_method; + response.append(" "); + response.append(Base64::encode(decoded)); + return AR_RESPONSE; + } +#endif +#endif // WIN32 + + return AR_IGNORE; +} + +inline bool end_of_name(size_t pos, size_t len, const char * data) { + if (pos >= len) + return true; + if (isspace(data[pos])) + return true; + // The reason for this complexity is that some non-compliant auth schemes (like Negotiate) + // use base64 tokens in the challenge instead of name=value. This could confuse us when the + // base64 ends in equal signs. + if ((pos+1 < len) && (data[pos] == '=') && !isspace(data[pos+1]) && (data[pos+1] != '=')) + return true; + return false; +} + +void AsyncHttpsProxySocket::ParseAuth(const char * data, size_t len, std::string& method, std::map& args) { + size_t pos = 0; + while ((pos < len) && isspace(data[pos])) ++pos; + size_t start = pos; + while ((pos < len) && !isspace(data[pos])) ++pos; + method.assign(data + start, data + pos); + while (pos < len) { + while ((pos < len) && isspace(data[pos])) ++pos; + if (pos >= len) + return; + + start = pos; + while (!end_of_name(pos, len, data)) ++pos; + //while ((pos < len) && !isspace(data[pos]) && (data[pos] != '=')) ++pos; + std::string name(data + start, data + pos), value; + + if ((pos < len) && (data[pos] == '=')) { + ++pos; // Skip '=' + // Check if quoted value + if ((pos < len) && (data[pos] == '"')) { + while (++pos < len) { + if (data[pos] == '"') { + ++pos; + break; + } + if ((data[pos] == '\\') && (pos + 1 < len)) + ++pos; + value.append(1, data[pos]); + } + } else { + while ((pos < len) && !isspace(data[pos]) && (data[pos] != ',')) + value.append(1, data[pos++]); + } + } else { + value = name; + name.clear(); + } + + args.insert(std::make_pair(name, value)); + if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ',' + } +} + +AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const buzz::XmppPassword& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), + state_(PS_ERROR), context_(0) { +} + +AsyncHttpsProxySocket::~AsyncHttpsProxySocket() { + delete context_; +} + +int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect(" << proxy_.ToString() << ")"; + dest_ = addr; + if (dest_.port() != 80) { + BufferInput(true); + } + return BufferedReadAdapter::Connect(proxy_); +} + +SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const { + return dest_; +} + +int AsyncHttpsProxySocket::Close() { + headers_.clear(); + state_ = PS_ERROR; + delete context_; + context_ = 0; + return BufferedReadAdapter::Close(); +} + +void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent"; + // TODO: Decide whether tunneling or not should be explicitly set, + // or indicated by destination port (as below) + if (dest_.port() == 80) { + state_ = PS_TUNNEL; + BufferedReadAdapter::OnConnectEvent(socket); + return; + } + SendRequest(); +} + +void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")"; + if ((state_ == PS_WAIT_CLOSE) && (err == 0)) { + state_ = PS_ERROR; + Connect(dest_); + } else { + BufferedReadAdapter::OnCloseEvent(socket, err); + } +} + +void AsyncHttpsProxySocket::ProcessInput(char * data, size_t& len) { + size_t start = 0; + for (size_t pos = start; (state_ < PS_TUNNEL) && (pos < len); ) { + if (state_ == PS_SKIP_BODY) { + size_t consume = _min(len - pos, content_length_); + pos += consume; + start = pos; + content_length_ -= consume; + if (content_length_ == 0) { + EndResponse(); + } + continue; + } + + if (data[pos++] != '\n') + continue; + + size_t len = pos - start - 1; + if ((len > 0) && (data[start + len - 1] == '\r')) + --len; + + data[start + len] = 0; + ProcessLine(data + start, len); + start = pos; + } + + len -= start; + if (len > 0) { + memmove(data, data + start, len); + } + + if (state_ != PS_TUNNEL) + return; + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncHttpsProxySocket::SendRequest() { + std::stringstream ss; + ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n"; + ss << "User-Agent: " USERAGENT_STRING "\r\n"; + ss << "Host: " << dest_.IPAsString() << "\r\n"; + ss << "Content-Length: 0\r\n"; + ss << "Proxy-Connection: Keep-Alive\r\n"; + ss << headers_; + ss << "\r\n"; + std::string str = ss.str(); + DirectSend(str.c_str(), str.size()); + state_ = PS_LEADER; + expect_close_ = true; + content_length_ = 0; + headers_.clear(); + + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str; +} + +void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data; + + if (len == 0) { + if (state_ == PS_TUNNEL_HEADERS) { + state_ = PS_TUNNEL; + } else if (state_ == PS_ERROR_HEADERS) { + Error(defer_error_); + return; + } else if (state_ == PS_SKIP_HEADERS) { + if (content_length_) { + state_ = PS_SKIP_BODY; + } else { + EndResponse(); + return; + } + } else { + static bool report = false; + if (!unknown_mechanisms_.empty() && !report) { + report = true; + std::string msg( + "Unable to connect to the Google Talk service due to an incompatibility " + "with your proxy.\r\nPlease help us resolve this issue by submitting the " + "following information to us using our technical issue submission form " + "at:\r\n\r\n" + "http://www.google.com/support/talk/bin/request.py\r\n\r\n" + "We apologize for the inconvenience.\r\n\r\n" + "Information to submit to Google: " + ); + //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: "); + msg.append(unknown_mechanisms_); +#ifdef WIN32 + MessageBoxA(0, msg.c_str(), "Oops!", MB_OK); +#endif +#ifdef POSIX + //TODO: Raise a signal or something so the UI can be separated. + LOG(LS_ERROR) << "Oops!\n\n" << msg; +#endif + } + // Unexpected end of headers + Error(0); + return; + } + } else if (state_ == PS_LEADER) { + uint32 code; + if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) { + Error(0); + return; + } + switch (code) { + case 200: + // connection good! + state_ = PS_TUNNEL_HEADERS; + return; +#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407) +#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ +#endif + case 407: // HTTP_STATUS_PROXY_AUTH_REQ + state_ = PS_AUTHENTICATE; + return; + default: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + return; + } + } else if ((state_ == PS_AUTHENTICATE) && (strnicmp(data, "Proxy-Authenticate:", 19) == 0)) { + std::string response, auth_method; + switch (Authenticate(data + 19, len - 19, proxy_, "CONNECT", "/", user_, pass_, context_, response, auth_method)) { + case AR_IGNORE: + LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method; + if (!unknown_mechanisms_.empty()) + unknown_mechanisms_.append(", "); + unknown_mechanisms_.append(auth_method); + break; + case AR_RESPONSE: + headers_ = "Proxy-Authorization: "; + headers_.append(response); + headers_.append("\r\n"); + state_ = PS_SKIP_HEADERS; + unknown_mechanisms_.clear(); + break; + case AR_CREDENTIALS: + defer_error_ = EACCES; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + case AR_ERROR: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + } + } else if (strnicmp(data, "Content-Length:", 15) == 0) { + content_length_ = strtoul(data + 15, 0, 0); + } else if (strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) { + expect_close_ = false; + /* + } else if (strnicmp(data, "Connection: close", 17) == 0) { + expect_close_ = true; + */ + } +} + +void AsyncHttpsProxySocket::EndResponse() { + if (!expect_close_) { + SendRequest(); + return; + } + + // No point in waiting for the server to close... let's close now + // TODO: Refactor out PS_WAIT_CLOSE + state_ = PS_WAIT_CLOSE; + BufferedReadAdapter::Close(); + OnCloseEvent(this, 0); +} + +void AsyncHttpsProxySocket::Error(int error) { + BufferInput(false); + Close(); + SetError(error); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const buzz::XmppPassword& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), + state_(SS_ERROR) { +} + +int AsyncSocksProxySocket::Connect(const SocketAddress& addr) { + dest_ = addr; + BufferInput(true); + return BufferedReadAdapter::Connect(proxy_); +} + +SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const { + return dest_; +} + +void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket * socket) { + SendHello(); +} + +void AsyncSocksProxySocket::ProcessInput(char * data, size_t& len) { + assert(state_ < SS_TUNNEL); + + ByteBuffer response(data, len); + + if (state_ == SS_HELLO) { + uint8 ver, method; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(method)) + return; + + if (ver != 5) { + Error(0); + return; + } + + if (method == 0) { + SendConnect(); + } else if (method == 2) { + SendAuth(); + } else { + Error(0); + return; + } + } else if (state_ == SS_AUTH) { + uint8 ver, status; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(status)) + return; + + if ((ver != 1) || (status != 0)) { + Error(EACCES); + return; + } + + SendConnect(); + } else if (state_ == SS_CONNECT) { + uint8 ver, rep, rsv, atyp; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(rep) || + !response.ReadUInt8(rsv) || + !response.ReadUInt8(atyp)) + return; + + if ((ver != 5) || (rep != 0)) { + Error(0); + return; + } + + uint16 port; + if (atyp == 1) { + uint32 addr; + if (!response.ReadUInt32(addr) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 3) { + uint8 len; + std::string addr; + if (!response.ReadUInt8(len) || + !response.ReadString(addr, len) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 4) { + std::string addr; + if (!response.ReadString(addr, 16) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on :" << port; + } else { + Error(0); + return; + } + + state_ = SS_TUNNEL; + } + + // Consume parsed data + len = response.Length(); + memcpy(data, response.Data(), len); + + if (state_ != SS_TUNNEL) + return; + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncSocksProxySocket::SendHello() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + if (user_.empty()) { + request.WriteUInt8(1); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + } else { + request.WriteUInt8(2); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + request.WriteUInt8(2); // Username/Password + } + DirectSend(request.Data(), request.Length()); + state_ = SS_HELLO; +} + +void AsyncSocksProxySocket::SendAuth() { + ByteBuffer request; + request.WriteUInt8(1); // Negotiation Version + request.WriteUInt8(static_cast(user_.size())); + request.WriteString(user_); // Username + request.WriteUInt8(static_cast(pass_.GetLength())); + size_t len = pass_.GetLength() + 1; + char * sensitive = new char[len]; + pass_.CopyTo(sensitive, true); + request.WriteString(sensitive); // Password + memset(sensitive, 0, len); + delete [] sensitive; + DirectSend(request.Data(), request.Length()); + state_ = SS_AUTH; +} + +void AsyncSocksProxySocket::SendConnect() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + request.WriteUInt8(1); // CONNECT + request.WriteUInt8(0); // Reserved + if (dest_.IsUnresolved()) { + std::string hostname = dest_.IPAsString(); + request.WriteUInt8(3); // DOMAINNAME + request.WriteUInt8(static_cast(hostname.size())); + request.WriteString(hostname); // Destination Hostname + } else { + request.WriteUInt8(1); // IPV4 + request.WriteUInt32(dest_.ip()); // Destination IP + } + request.WriteUInt16(dest_.port()); // Destination Port + DirectSend(request.Data(), request.Length()); + state_ = SS_CONNECT; +} + +void AsyncSocksProxySocket::Error(int error) { + state_ = SS_ERROR; + BufferInput(false); + Close(); + SetError(EACCES); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +LoggingAdapter::LoggingAdapter(AsyncSocket* socket, LoggingSeverity level, + const char * label) + : AsyncSocketAdapter(socket), level_(level) +{ + label_.append("["); + label_.append(label); + label_.append("]"); +} + +int +LoggingAdapter::Send(const void *pv, size_t cb) { + int res = AsyncSocketAdapter::Send(pv, cb); + if (res > 0) + LogMultiline(false, static_cast(pv), res); + return res; +} + +int +LoggingAdapter::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + int res = AsyncSocketAdapter::SendTo(pv, cb, addr); + if (res > 0) + LogMultiline(false, static_cast(pv), res); + return res; +} + +int +LoggingAdapter::Recv(void *pv, size_t cb) { + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res > 0) + LogMultiline(true, static_cast(pv), res); + return res; +} + +int +LoggingAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + if (res > 0) + LogMultiline(true, static_cast(pv), res); + return res; +} + +void +LoggingAdapter::OnConnectEvent(AsyncSocket * socket) { + LOG(level_) << label_ << " Connected"; + AsyncSocketAdapter::OnConnectEvent(socket); +} + +void +LoggingAdapter::OnCloseEvent(AsyncSocket * socket, int err) { + LOG(level_) << label_ << " Closed with error: " << err; + AsyncSocketAdapter::OnCloseEvent(socket, err); +} + +void +LoggingAdapter::LogMultiline(bool input, const char * data, size_t len) { + const char * direction = (input ? " << " : " >> "); + std::string str(data, len); + while (!str.empty()) { + std::string::size_type pos = str.find('\n'); + std::string substr = str; + if (pos == std::string::npos) { + substr = str; + str.clear(); + } else if ((pos > 0) && (str[pos-1] == '\r')) { + substr = str.substr(0, pos - 1); + str = str.substr(pos + 1); + } else { + substr = str.substr(0, pos); + str = str.substr(pos + 1); + } + + // Filter out any private data + std::string::size_type pos_private = substr.find("Email"); + if (pos_private == std::string::npos) { + pos_private = substr.find("Passwd"); + } + if (pos_private == std::string::npos) { + LOG(level_) << label_ << direction << substr; + } else { + LOG(level_) << label_ << direction << "## TEXT REMOVED ##"; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc deleted file mode 100644 index 30d43fc6..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc +++ /dev/null @@ -1,267 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/socketaddress.h" -#include "talk/base/byteorder.h" -#include "talk/base/logging.h" -#include -#include -#include - -#ifdef WIN32 -#undef SetPort -int inet_aton(const char * cp, struct in_addr * inp) { - inp->s_addr = inet_addr(cp); - return (inp->s_addr == INADDR_NONE) ? 0 : 1; -} -#endif // WIN32 - -#ifdef POSIX -#include -#include -#include -#include -#include -#endif - -#ifdef _DEBUG -#define DISABLE_DNS 0 -#else // !_DEBUG -#define DISABLE_DNS 0 -#endif // !_DEBUG - -namespace cricket { - -SocketAddress::SocketAddress() { - Zero(); -} - -SocketAddress::SocketAddress(const std::string& hostname, int port, bool use_dns) { - Zero(); - SetIP(hostname, use_dns); - SetPort(port); -} - -SocketAddress::SocketAddress(uint32 ip, int port) { - Zero(); - SetIP(ip); - SetPort(port); -} - -SocketAddress::SocketAddress(const SocketAddress& addr) { - Zero(); - this->operator=(addr); -} - -void SocketAddress::Zero() { - ip_ = 0; - port_ = 0; -} - -SocketAddress& SocketAddress::operator =(const SocketAddress& addr) { - hostname_ = addr.hostname_; - ip_ = addr.ip_; - port_ = addr.port_; - return *this; -} - -void SocketAddress::SetIP(uint32 ip) { - hostname_.clear(); - ip_ = ip; -} - -bool SocketAddress::SetIP(const std::string& hostname, bool use_dns) { - hostname_ = hostname; - ip_ = 0; - return Resolve(true, use_dns); -} - -void SocketAddress::SetResolvedIP(uint32 ip) { - ip_ = ip; -} - -void SocketAddress::SetPort(int port) { - assert((0 <= port) && (port < 65536)); - port_ = port; -} - -uint32 SocketAddress::ip() const { - return ip_; -} - -uint16 SocketAddress::port() const { - return port_; -} - -std::string SocketAddress::IPAsString() const { - if (!hostname_.empty()) - return hostname_; - return IPToString(ip_); -} - -std::string SocketAddress::PortAsString() const { - std::ostringstream ost; - ost << port_; - return ost.str(); -} - -std::string SocketAddress::ToString() const { - std::ostringstream ost; - ost << IPAsString(); - ost << ":"; - ost << port(); - return ost.str(); -} - -bool SocketAddress::IsAny() const { - return (ip_ == 0); -} - -bool SocketAddress::IsLocalIP() const { - return (ip_ >> 24) == 127; -} - -bool SocketAddress::IsPrivateIP() const { - return ((ip_ >> 24) == 127) || - ((ip_ >> 24) == 10) || - ((ip_ >> 20) == ((172 << 4) | 1)) || - ((ip_ >> 16) == ((192 << 8) | 168)); -} - -bool SocketAddress::IsUnresolved() const { - return IsAny() && !hostname_.empty(); -} - -bool SocketAddress::Resolve(bool force, bool use_dns) { - if (hostname_.empty()) { - // nothing to resolve - } else if (!force && !IsAny()) { - // already resolved - } else if (uint32 ip = StringToIP(hostname_, use_dns)) { - ip_ = ip; - } else { - return false; - } - return true; -} - -bool SocketAddress::operator ==(const SocketAddress& addr) const { - return EqualIPs(addr) && EqualPorts(addr); -} - -bool SocketAddress::operator <(const SocketAddress& addr) const { - if (ip_ < addr.ip_) - return true; - else if (addr.ip_ < ip_) - return false; - - // We only check hostnames if both IPs are zero. This matches EqualIPs() - if (addr.ip_ == 0) { - if (hostname_ < addr.hostname_) - return true; - else if (addr.hostname_ < hostname_) - return false; - } - - return port_ < addr.port_; -} - -bool SocketAddress::EqualIPs(const SocketAddress& addr) const { - return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_)); -} - -bool SocketAddress::EqualPorts(const SocketAddress& addr) const { - return (port_ == addr.port_); -} - -size_t SocketAddress::Hash() const { - size_t h = 0; - h ^= ip_; - h ^= port_ | (port_ << 16); - return h; -} - -size_t SocketAddress::Size_() const { - return sizeof(ip_) + sizeof(port_); -} - -void SocketAddress::Write_(char* buf, int len) const { - // TODO: Depending on how this is used, we may want/need to write hostname - assert((size_t)len >= Size_()); - reinterpret_cast(buf)[0] = ip_; - buf += sizeof(ip_); - reinterpret_cast(buf)[0] = port_; -} - -void SocketAddress::Read_(const char* buf, int len) { - assert((size_t)len >= Size_()); - ip_ = reinterpret_cast(buf)[0]; - buf += sizeof(ip_); - port_ = reinterpret_cast(buf)[0]; -} - -std::string SocketAddress::IPToString(uint32 ip) { - std::ostringstream ost; - ost << ((ip >> 24) & 0xff); - ost << '.'; - ost << ((ip >> 16) & 0xff); - ost << '.'; - ost << ((ip >> 8) & 0xff); - ost << '.'; - ost << ((ip >> 0) & 0xff); - return ost.str(); -} - -uint32 SocketAddress::StringToIP(const std::string& hostname, bool use_dns) { - uint32 ip = 0; - in_addr addr; - if (inet_aton(hostname.c_str(), &addr) != 0) { - ip = NetworkToHost32(addr.s_addr); - } else if (use_dns) { - // Note: this is here so we can spot spurious DNS resolutions for a while - LOG(INFO) << "=== DNS RESOLUTION (" << hostname << ") ==="; -#if DISABLE_DNS - LOG(WARNING) << "*** DNS DISABLED ***"; -#ifdef WIN32 - WSASetLastError(WSAHOST_NOT_FOUND); -#endif // WIN32 -#endif // DISABLE_DNS - if (hostent * pHost = gethostbyname(hostname.c_str())) { - ip = NetworkToHost32(*reinterpret_cast(pHost->h_addr_list[0])); - } else { -#ifdef WIN32 - LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError(); -#else - LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno); -#endif - } - LOG(INFO) << hostname << " resolved to " << IPToString(ip); - } - return ip; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cpp new file mode 100644 index 00000000..30d43fc6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cpp @@ -0,0 +1,267 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/socketaddress.h" +#include "talk/base/byteorder.h" +#include "talk/base/logging.h" +#include +#include +#include + +#ifdef WIN32 +#undef SetPort +int inet_aton(const char * cp, struct in_addr * inp) { + inp->s_addr = inet_addr(cp); + return (inp->s_addr == INADDR_NONE) ? 0 : 1; +} +#endif // WIN32 + +#ifdef POSIX +#include +#include +#include +#include +#include +#endif + +#ifdef _DEBUG +#define DISABLE_DNS 0 +#else // !_DEBUG +#define DISABLE_DNS 0 +#endif // !_DEBUG + +namespace cricket { + +SocketAddress::SocketAddress() { + Zero(); +} + +SocketAddress::SocketAddress(const std::string& hostname, int port, bool use_dns) { + Zero(); + SetIP(hostname, use_dns); + SetPort(port); +} + +SocketAddress::SocketAddress(uint32 ip, int port) { + Zero(); + SetIP(ip); + SetPort(port); +} + +SocketAddress::SocketAddress(const SocketAddress& addr) { + Zero(); + this->operator=(addr); +} + +void SocketAddress::Zero() { + ip_ = 0; + port_ = 0; +} + +SocketAddress& SocketAddress::operator =(const SocketAddress& addr) { + hostname_ = addr.hostname_; + ip_ = addr.ip_; + port_ = addr.port_; + return *this; +} + +void SocketAddress::SetIP(uint32 ip) { + hostname_.clear(); + ip_ = ip; +} + +bool SocketAddress::SetIP(const std::string& hostname, bool use_dns) { + hostname_ = hostname; + ip_ = 0; + return Resolve(true, use_dns); +} + +void SocketAddress::SetResolvedIP(uint32 ip) { + ip_ = ip; +} + +void SocketAddress::SetPort(int port) { + assert((0 <= port) && (port < 65536)); + port_ = port; +} + +uint32 SocketAddress::ip() const { + return ip_; +} + +uint16 SocketAddress::port() const { + return port_; +} + +std::string SocketAddress::IPAsString() const { + if (!hostname_.empty()) + return hostname_; + return IPToString(ip_); +} + +std::string SocketAddress::PortAsString() const { + std::ostringstream ost; + ost << port_; + return ost.str(); +} + +std::string SocketAddress::ToString() const { + std::ostringstream ost; + ost << IPAsString(); + ost << ":"; + ost << port(); + return ost.str(); +} + +bool SocketAddress::IsAny() const { + return (ip_ == 0); +} + +bool SocketAddress::IsLocalIP() const { + return (ip_ >> 24) == 127; +} + +bool SocketAddress::IsPrivateIP() const { + return ((ip_ >> 24) == 127) || + ((ip_ >> 24) == 10) || + ((ip_ >> 20) == ((172 << 4) | 1)) || + ((ip_ >> 16) == ((192 << 8) | 168)); +} + +bool SocketAddress::IsUnresolved() const { + return IsAny() && !hostname_.empty(); +} + +bool SocketAddress::Resolve(bool force, bool use_dns) { + if (hostname_.empty()) { + // nothing to resolve + } else if (!force && !IsAny()) { + // already resolved + } else if (uint32 ip = StringToIP(hostname_, use_dns)) { + ip_ = ip; + } else { + return false; + } + return true; +} + +bool SocketAddress::operator ==(const SocketAddress& addr) const { + return EqualIPs(addr) && EqualPorts(addr); +} + +bool SocketAddress::operator <(const SocketAddress& addr) const { + if (ip_ < addr.ip_) + return true; + else if (addr.ip_ < ip_) + return false; + + // We only check hostnames if both IPs are zero. This matches EqualIPs() + if (addr.ip_ == 0) { + if (hostname_ < addr.hostname_) + return true; + else if (addr.hostname_ < hostname_) + return false; + } + + return port_ < addr.port_; +} + +bool SocketAddress::EqualIPs(const SocketAddress& addr) const { + return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_)); +} + +bool SocketAddress::EqualPorts(const SocketAddress& addr) const { + return (port_ == addr.port_); +} + +size_t SocketAddress::Hash() const { + size_t h = 0; + h ^= ip_; + h ^= port_ | (port_ << 16); + return h; +} + +size_t SocketAddress::Size_() const { + return sizeof(ip_) + sizeof(port_); +} + +void SocketAddress::Write_(char* buf, int len) const { + // TODO: Depending on how this is used, we may want/need to write hostname + assert((size_t)len >= Size_()); + reinterpret_cast(buf)[0] = ip_; + buf += sizeof(ip_); + reinterpret_cast(buf)[0] = port_; +} + +void SocketAddress::Read_(const char* buf, int len) { + assert((size_t)len >= Size_()); + ip_ = reinterpret_cast(buf)[0]; + buf += sizeof(ip_); + port_ = reinterpret_cast(buf)[0]; +} + +std::string SocketAddress::IPToString(uint32 ip) { + std::ostringstream ost; + ost << ((ip >> 24) & 0xff); + ost << '.'; + ost << ((ip >> 16) & 0xff); + ost << '.'; + ost << ((ip >> 8) & 0xff); + ost << '.'; + ost << ((ip >> 0) & 0xff); + return ost.str(); +} + +uint32 SocketAddress::StringToIP(const std::string& hostname, bool use_dns) { + uint32 ip = 0; + in_addr addr; + if (inet_aton(hostname.c_str(), &addr) != 0) { + ip = NetworkToHost32(addr.s_addr); + } else if (use_dns) { + // Note: this is here so we can spot spurious DNS resolutions for a while + LOG(INFO) << "=== DNS RESOLUTION (" << hostname << ") ==="; +#if DISABLE_DNS + LOG(WARNING) << "*** DNS DISABLED ***"; +#ifdef WIN32 + WSASetLastError(WSAHOST_NOT_FOUND); +#endif // WIN32 +#endif // DISABLE_DNS + if (hostent * pHost = gethostbyname(hostname.c_str())) { + ip = NetworkToHost32(*reinterpret_cast(pHost->h_addr_list[0])); + } else { +#ifdef WIN32 + LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError(); +#else + LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno); +#endif + } + LOG(INFO) << hostname << " resolved to " << IPToString(ip); + } + return ip; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc deleted file mode 100644 index 2166be09..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc +++ /dev/null @@ -1,58 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/socketaddresspair.h" - -namespace cricket { - -SocketAddressPair::SocketAddressPair( - const SocketAddress& src, const SocketAddress& dest) - : src_(src), dest_(dest) { -} - - -bool SocketAddressPair::operator ==(const SocketAddressPair& p) const { - return (src_ == p.src_) && (dest_ == p.dest_); -} - -bool SocketAddressPair::operator <(const SocketAddressPair& p) const { - if (src_ < p.src_) - return true; - if (p.src_ < src_) - return false; - if (dest_ < p.dest_) - return true; - if (p.dest_ < dest_) - return false; - return false; -} - -size_t SocketAddressPair::Hash() const { - return src_.Hash() ^ dest_.Hash(); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cpp new file mode 100644 index 00000000..2166be09 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cpp @@ -0,0 +1,58 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/socketaddresspair.h" + +namespace cricket { + +SocketAddressPair::SocketAddressPair( + const SocketAddress& src, const SocketAddress& dest) + : src_(src), dest_(dest) { +} + + +bool SocketAddressPair::operator ==(const SocketAddressPair& p) const { + return (src_ == p.src_) && (dest_ == p.dest_); +} + +bool SocketAddressPair::operator <(const SocketAddressPair& p) const { + if (src_ < p.src_) + return true; + if (p.src_ < src_) + return false; + if (dest_ < p.dest_) + return true; + if (p.dest_ < dest_) + return false; + return false; +} + +size_t SocketAddressPair::Hash() const { + return src_.Hash() ^ dest_.Hash(); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc deleted file mode 100644 index a5a94941..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc +++ /dev/null @@ -1,238 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "task.h" -#include "taskrunner.h" - -#include - -namespace buzz { - -Task::Task(Task * parent) : - state_(STATE_INIT), - parent_(parent), - blocked_(false), - done_(false), - aborted_(false), - busy_(false), - error_(false), - child_error_(false), - start_time_(0) { - runner_ = ((parent == NULL) ? (TaskRunner *)this : parent->GetRunner()); - if (parent_ != NULL) { - parent_->AddChild(this); - } -} - -unsigned long long -Task::CurrentTime() { - return runner_->CurrentTime(); -} - -unsigned long long -Task::ElapsedTime() { - return CurrentTime() - start_time_; -} - -void -Task::Start() { - if (state_ != STATE_INIT) - return; - GetRunner()->StartTask(this); - start_time_ = CurrentTime(); -} - -void -Task::Step() { - if (done_) { -#ifdef DEBUG - // we do not know how !blocked_ happens when done_ - should be impossible. - // But it causes problems, so in retail build, we force blocked_, and - // under debug we assert. - assert(blocked_); -#else - blocked_ = true; -#endif - return; - } - - // Async Error() was called - if (error_) { - done_ = true; - state_ = STATE_ERROR; - blocked_ = true; -// obsolete - an errored task is not considered done now -// SignalDone(); - Stop(); - return; - } - - busy_ = true; - int new_state = Process(state_); - busy_ = false; - - if (aborted_) { - Abort(true); // no need to wake because we're awake - return; - } - - if (new_state == STATE_BLOCKED) { - blocked_ = true; - } - else { - state_ = new_state; - blocked_ = false; - } - - if (new_state == STATE_DONE) { - done_ = true; - } - else if (new_state == STATE_ERROR) { - done_ = true; - error_ = true; - } - - if (done_) { -// obsolete - call this yourself -// SignalDone(); - Stop(); - blocked_ = true; - } -} - -void -Task::Abort(bool nowake) { - if (aborted_ || done_) - return; - aborted_ = true; - if (!busy_) { - done_ = true; - blocked_ = true; - error_ = true; - Stop(); - if (!nowake) - Wake(); // to self-delete - } -} - -void -Task::Wake() { - if (done_) - return; - if (blocked_) { - blocked_ = false; - GetRunner()->WakeTasks(); - } -} - -void -Task::Error() { - if (error_ || done_) - return; - error_ = true; - Wake(); -} - -std::string -Task::GetStateName(int state) const { - static const std::string STR_BLOCKED("BLOCKED"); - static const std::string STR_INIT("INIT"); - static const std::string STR_START("START"); - static const std::string STR_DONE("DONE"); - static const std::string STR_ERROR("ERROR"); - static const std::string STR_RESPONSE("RESPONSE"); - static const std::string STR_HUH("??"); - switch (state) { - case STATE_BLOCKED: return STR_BLOCKED; - case STATE_INIT: return STR_INIT; - case STATE_START: return STR_START; - case STATE_DONE: return STR_DONE; - case STATE_ERROR: return STR_ERROR; - case STATE_RESPONSE: return STR_RESPONSE; - } - return STR_HUH; -} - -int Task::Process(int state) { - switch (state) { - case STATE_INIT: - return STATE_START; - case STATE_START: - return ProcessStart(); - case STATE_RESPONSE: - return ProcessResponse(); - case STATE_DONE: - case STATE_ERROR: - return STATE_BLOCKED; - } - return STATE_ERROR; -} - -void -Task::AddChild(Task * child) { - children_.insert(child); -} - -bool -Task::AllChildrenDone() { - for (ChildSet::iterator it = children_.begin(); it != children_.end(); ++it) { - if (!(*it)->IsDone()) - return false; - } - return true; -} - -bool -Task::AnyChildError() { - return child_error_; -} - -void -Task::AbortAllChildren() { - if (children_.size() > 0) { - ChildSet copy = children_; - for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) { - (*it)->Abort(true); // Note we do not wake - } - } -} - -void -Task::Stop() { - AbortAllChildren(); // No need to wake because we're either awake or in abort - parent_->OnChildStopped(this); -} - -void -Task::OnChildStopped(Task * child) { - if (child->HasError()) - child_error_ = true; - children_.erase(child); -} - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cpp new file mode 100644 index 00000000..a5a94941 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cpp @@ -0,0 +1,238 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "task.h" +#include "taskrunner.h" + +#include + +namespace buzz { + +Task::Task(Task * parent) : + state_(STATE_INIT), + parent_(parent), + blocked_(false), + done_(false), + aborted_(false), + busy_(false), + error_(false), + child_error_(false), + start_time_(0) { + runner_ = ((parent == NULL) ? (TaskRunner *)this : parent->GetRunner()); + if (parent_ != NULL) { + parent_->AddChild(this); + } +} + +unsigned long long +Task::CurrentTime() { + return runner_->CurrentTime(); +} + +unsigned long long +Task::ElapsedTime() { + return CurrentTime() - start_time_; +} + +void +Task::Start() { + if (state_ != STATE_INIT) + return; + GetRunner()->StartTask(this); + start_time_ = CurrentTime(); +} + +void +Task::Step() { + if (done_) { +#ifdef DEBUG + // we do not know how !blocked_ happens when done_ - should be impossible. + // But it causes problems, so in retail build, we force blocked_, and + // under debug we assert. + assert(blocked_); +#else + blocked_ = true; +#endif + return; + } + + // Async Error() was called + if (error_) { + done_ = true; + state_ = STATE_ERROR; + blocked_ = true; +// obsolete - an errored task is not considered done now +// SignalDone(); + Stop(); + return; + } + + busy_ = true; + int new_state = Process(state_); + busy_ = false; + + if (aborted_) { + Abort(true); // no need to wake because we're awake + return; + } + + if (new_state == STATE_BLOCKED) { + blocked_ = true; + } + else { + state_ = new_state; + blocked_ = false; + } + + if (new_state == STATE_DONE) { + done_ = true; + } + else if (new_state == STATE_ERROR) { + done_ = true; + error_ = true; + } + + if (done_) { +// obsolete - call this yourself +// SignalDone(); + Stop(); + blocked_ = true; + } +} + +void +Task::Abort(bool nowake) { + if (aborted_ || done_) + return; + aborted_ = true; + if (!busy_) { + done_ = true; + blocked_ = true; + error_ = true; + Stop(); + if (!nowake) + Wake(); // to self-delete + } +} + +void +Task::Wake() { + if (done_) + return; + if (blocked_) { + blocked_ = false; + GetRunner()->WakeTasks(); + } +} + +void +Task::Error() { + if (error_ || done_) + return; + error_ = true; + Wake(); +} + +std::string +Task::GetStateName(int state) const { + static const std::string STR_BLOCKED("BLOCKED"); + static const std::string STR_INIT("INIT"); + static const std::string STR_START("START"); + static const std::string STR_DONE("DONE"); + static const std::string STR_ERROR("ERROR"); + static const std::string STR_RESPONSE("RESPONSE"); + static const std::string STR_HUH("??"); + switch (state) { + case STATE_BLOCKED: return STR_BLOCKED; + case STATE_INIT: return STR_INIT; + case STATE_START: return STR_START; + case STATE_DONE: return STR_DONE; + case STATE_ERROR: return STR_ERROR; + case STATE_RESPONSE: return STR_RESPONSE; + } + return STR_HUH; +} + +int Task::Process(int state) { + switch (state) { + case STATE_INIT: + return STATE_START; + case STATE_START: + return ProcessStart(); + case STATE_RESPONSE: + return ProcessResponse(); + case STATE_DONE: + case STATE_ERROR: + return STATE_BLOCKED; + } + return STATE_ERROR; +} + +void +Task::AddChild(Task * child) { + children_.insert(child); +} + +bool +Task::AllChildrenDone() { + for (ChildSet::iterator it = children_.begin(); it != children_.end(); ++it) { + if (!(*it)->IsDone()) + return false; + } + return true; +} + +bool +Task::AnyChildError() { + return child_error_; +} + +void +Task::AbortAllChildren() { + if (children_.size() > 0) { + ChildSet copy = children_; + for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) { + (*it)->Abort(true); // Note we do not wake + } + } +} + +void +Task::Stop() { + AbortAllChildren(); // No need to wake because we're either awake or in abort + parent_->OnChildStopped(this); +} + +void +Task::OnChildStopped(Task * child) { + if (child->HasError()) + child_error_ = true; + children_.erase(child); +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc deleted file mode 100644 index b5ecc55e..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "taskrunner.h" -#include "task.h" -#include - - -namespace buzz { - -TaskRunner::~TaskRunner() { - // this kills and deletes children silently! - AbortAllChildren(); - RunTasks(); -} - -void -TaskRunner::StartTask(Task * task) { - tasks_.push_back(task); - WakeTasks(); -} - -void -TaskRunner::RunTasks() { - // Running continues until all tasks are Blocked (ok for a small # of tasks) - if (tasks_running_) { - return; // don't reenter - } - - tasks_running_ = true; - - int did_run = true; - while (did_run) { - did_run = false; - // use indexing instead of iterators because tasks_ may grow - for (size_t i = 0; i < tasks_.size(); ++i) { - while (!tasks_[i]->Blocked()) { - tasks_[i]->Step(); - did_run = true; - } - } - } - // Tasks are deleted when running has paused - for (size_t i = 0; i < tasks_.size(); ++i) { - if (tasks_[i]->IsDone()) { - Task* task = tasks_[i]; - delete task; - tasks_[i] = NULL; - } - } - // Finally, remove nulls - tasks_.erase(std::remove(tasks_.begin(), tasks_.end(), (Task *)NULL), tasks_.end()); - - tasks_running_ = false; -} - -void -TaskRunner::PollTasks() { - // every task gets hit once with a poll - they wake if needed - for (size_t i = 0; i < tasks_.size(); ++i) { - if (!tasks_[i]->IsDone()) { - tasks_[i]->Poll(); - } - } -} - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cpp new file mode 100644 index 00000000..b5ecc55e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cpp @@ -0,0 +1,92 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "taskrunner.h" +#include "task.h" +#include + + +namespace buzz { + +TaskRunner::~TaskRunner() { + // this kills and deletes children silently! + AbortAllChildren(); + RunTasks(); +} + +void +TaskRunner::StartTask(Task * task) { + tasks_.push_back(task); + WakeTasks(); +} + +void +TaskRunner::RunTasks() { + // Running continues until all tasks are Blocked (ok for a small # of tasks) + if (tasks_running_) { + return; // don't reenter + } + + tasks_running_ = true; + + int did_run = true; + while (did_run) { + did_run = false; + // use indexing instead of iterators because tasks_ may grow + for (size_t i = 0; i < tasks_.size(); ++i) { + while (!tasks_[i]->Blocked()) { + tasks_[i]->Step(); + did_run = true; + } + } + } + // Tasks are deleted when running has paused + for (size_t i = 0; i < tasks_.size(); ++i) { + if (tasks_[i]->IsDone()) { + Task* task = tasks_[i]; + delete task; + tasks_[i] = NULL; + } + } + // Finally, remove nulls + tasks_.erase(std::remove(tasks_.begin(), tasks_.end(), (Task *)NULL), tasks_.end()); + + tasks_running_ = false; +} + +void +TaskRunner::PollTasks() { + // every task gets hit once with a poll - they wake if needed + for (size_t i = 0; i < tasks_.size(); ++i) { + if (!tasks_[i]->IsDone()) { + tasks_[i]->Poll(); + } + } +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc deleted file mode 100644 index b189e621..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc +++ /dev/null @@ -1,274 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#ifdef POSIX -extern "C" { -#include -} -#endif - -#include "talk/base/common.h" -#include "talk/base/logging.h" -#include "talk/base/thread.h" -#include "talk/base/jtime.h" - -namespace cricket { - -ThreadManager g_thmgr; - -#ifdef POSIX -pthread_key_t ThreadManager::key_; - -ThreadManager::ThreadManager() { - pthread_key_create(&key_, NULL); - main_thread_ = new Thread(); - SetCurrent(main_thread_); -} - -ThreadManager::~ThreadManager() { - pthread_key_delete(key_); - delete main_thread_; -} - -Thread *ThreadManager::CurrentThread() { - return (Thread *)pthread_getspecific(key_); -} - -void ThreadManager::SetCurrent(Thread *thread) { - pthread_setspecific(key_, thread); -} -#endif - -#ifdef WIN32 -DWORD ThreadManager::key_; - -ThreadManager::ThreadManager() { - key_ = TlsAlloc(); - main_thread_ = new Thread(); - SetCurrent(main_thread_); -} - -ThreadManager::~ThreadManager() { - TlsFree(key_); - delete main_thread_; -} - -Thread *ThreadManager::CurrentThread() { - return (Thread *)TlsGetValue(key_); -} - -void ThreadManager::SetCurrent(Thread *thread) { - TlsSetValue(key_, thread); -} -#endif - -void ThreadManager::Add(Thread *thread) { - CritScope cs(&crit_); - threads_.push_back(thread); -} - -void ThreadManager::Remove(Thread *thread) { - CritScope cs(&crit_); - threads_.erase(std::remove(threads_.begin(), threads_.end(), thread), threads_.end()); -} - -Thread::Thread(SocketServer* ss) : MessageQueue(ss) { - g_thmgr.Add(this); - started_ = false; - has_sends_ = false; -} - -Thread::~Thread() { - Stop(); - Clear(NULL); - g_thmgr.Remove(this); -} - -#ifdef POSIX -void Thread::Start() { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_create(&thread_, &attr, PreLoop, this); - pthread_attr_destroy(&attr); - started_ = true; -} - -void Thread::Join() { - if (started_) { - void *pv; - pthread_join(thread_, &pv); - } -} -#endif - -#ifdef WIN32 -void Thread::Start() { - thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreLoop, this, 0, NULL); - started_ = true; -} - -void Thread::Join() { - if (started_) { - WaitForSingleObject(thread_, INFINITE); - CloseHandle(thread_); - started_ = false; - } -} -#endif - -void *Thread::PreLoop(void *pv) { - Thread *thread = (Thread *)pv; - ThreadManager::SetCurrent(thread); - thread->Loop(); - return NULL; -} - -void Thread::Loop(int cmsLoop) { - uint32 msEnd; - if (cmsLoop != -1) - msEnd = GetMillisecondCount() + cmsLoop; - int cmsNext = cmsLoop; - - while (true) { - Message msg; - if (!Get(&msg, cmsNext)) - return; - Dispatch(&msg); - - if (cmsLoop != -1) { - uint32 msCur = GetMillisecondCount(); - if (msCur >= msEnd) - return; - cmsNext = msEnd - msCur; - } - } -} - -void Thread::Stop() { - MessageQueue::Stop(); - Join(); -} - -void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { - // Sent messages are sent to the MessageHandler directly, in the context - // of "thread", like Win32 SendMessage. If in the right context, - // call the handler directly. - - Message msg; - msg.phandler = phandler; - msg.message_id = id; - msg.pdata = pdata; - if (IsCurrent()) { - phandler->OnMessage(&msg); - return; - } - - AutoThread thread; - Thread *current_thread = Thread::Current(); - ASSERT(current_thread != NULL); // AutoThread ensures this - - crit_.Enter(); - bool ready = false; - _SendMessage smsg; - smsg.thread = current_thread; - smsg.msg = msg; - smsg.ready = &ready; - sendlist_.push_back(smsg); - has_sends_ = true; - crit_.Leave(); - - // Wait for a reply - - ss_->WakeUp(); - while (!ready) { - current_thread->ReceiveSends(); - current_thread->socketserver()->Wait(-1, false); - } -} - -void Thread::ReceiveSends() { - // Before entering critical section, check boolean. - - if (!has_sends_) - return; - - // Receive a sent message. Cleanup scenarios: - // - thread sending exits: We don't allow this, since thread can exit - // only via Join, so Send must complete. - // - thread receiving exits: Wakeup/set ready in Thread::Clear() - // - object target cleared: Wakeup/set ready in Thread::Clear() - crit_.Enter(); - while (!sendlist_.empty()) { - _SendMessage smsg = sendlist_.front(); - sendlist_.pop_front(); - crit_.Leave(); - smsg.msg.phandler->OnMessage(&smsg.msg); - crit_.Enter(); - *smsg.ready = true; - smsg.thread->socketserver()->WakeUp(); - } - has_sends_ = false; - crit_.Leave(); -} - -void Thread::Clear(MessageHandler *phandler, uint32 id) { - CritScope cs(&crit_); - - // Remove messages on sendlist_ with phandler - // Object target cleared: remove from send list, wakeup/set ready - // if sender not NULL. - - std::list<_SendMessage>::iterator iter = sendlist_.begin(); - while (iter != sendlist_.end()) { - _SendMessage smsg = *iter; - if (phandler == NULL || smsg.msg.phandler == phandler) { - if (id == (uint32)-1 || smsg.msg.message_id == id) { - iter = sendlist_.erase(iter); - *smsg.ready = true; - smsg.thread->socketserver()->WakeUp(); - continue; - } - } - ++iter; - } - - MessageQueue::Clear(phandler, id); -} - -AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { - if (!ThreadManager::CurrentThread()) { - ThreadManager::SetCurrent(this); - } -} - -AutoThread::~AutoThread() { - if (ThreadManager::CurrentThread() == this) { - ThreadManager::SetCurrent(NULL); - } -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cpp new file mode 100644 index 00000000..b189e621 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cpp @@ -0,0 +1,274 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#ifdef POSIX +extern "C" { +#include +} +#endif + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#include "talk/base/jtime.h" + +namespace cricket { + +ThreadManager g_thmgr; + +#ifdef POSIX +pthread_key_t ThreadManager::key_; + +ThreadManager::ThreadManager() { + pthread_key_create(&key_, NULL); + main_thread_ = new Thread(); + SetCurrent(main_thread_); +} + +ThreadManager::~ThreadManager() { + pthread_key_delete(key_); + delete main_thread_; +} + +Thread *ThreadManager::CurrentThread() { + return (Thread *)pthread_getspecific(key_); +} + +void ThreadManager::SetCurrent(Thread *thread) { + pthread_setspecific(key_, thread); +} +#endif + +#ifdef WIN32 +DWORD ThreadManager::key_; + +ThreadManager::ThreadManager() { + key_ = TlsAlloc(); + main_thread_ = new Thread(); + SetCurrent(main_thread_); +} + +ThreadManager::~ThreadManager() { + TlsFree(key_); + delete main_thread_; +} + +Thread *ThreadManager::CurrentThread() { + return (Thread *)TlsGetValue(key_); +} + +void ThreadManager::SetCurrent(Thread *thread) { + TlsSetValue(key_, thread); +} +#endif + +void ThreadManager::Add(Thread *thread) { + CritScope cs(&crit_); + threads_.push_back(thread); +} + +void ThreadManager::Remove(Thread *thread) { + CritScope cs(&crit_); + threads_.erase(std::remove(threads_.begin(), threads_.end(), thread), threads_.end()); +} + +Thread::Thread(SocketServer* ss) : MessageQueue(ss) { + g_thmgr.Add(this); + started_ = false; + has_sends_ = false; +} + +Thread::~Thread() { + Stop(); + Clear(NULL); + g_thmgr.Remove(this); +} + +#ifdef POSIX +void Thread::Start() { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_create(&thread_, &attr, PreLoop, this); + pthread_attr_destroy(&attr); + started_ = true; +} + +void Thread::Join() { + if (started_) { + void *pv; + pthread_join(thread_, &pv); + } +} +#endif + +#ifdef WIN32 +void Thread::Start() { + thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreLoop, this, 0, NULL); + started_ = true; +} + +void Thread::Join() { + if (started_) { + WaitForSingleObject(thread_, INFINITE); + CloseHandle(thread_); + started_ = false; + } +} +#endif + +void *Thread::PreLoop(void *pv) { + Thread *thread = (Thread *)pv; + ThreadManager::SetCurrent(thread); + thread->Loop(); + return NULL; +} + +void Thread::Loop(int cmsLoop) { + uint32 msEnd; + if (cmsLoop != -1) + msEnd = GetMillisecondCount() + cmsLoop; + int cmsNext = cmsLoop; + + while (true) { + Message msg; + if (!Get(&msg, cmsNext)) + return; + Dispatch(&msg); + + if (cmsLoop != -1) { + uint32 msCur = GetMillisecondCount(); + if (msCur >= msEnd) + return; + cmsNext = msEnd - msCur; + } + } +} + +void Thread::Stop() { + MessageQueue::Stop(); + Join(); +} + +void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { + // Sent messages are sent to the MessageHandler directly, in the context + // of "thread", like Win32 SendMessage. If in the right context, + // call the handler directly. + + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + if (IsCurrent()) { + phandler->OnMessage(&msg); + return; + } + + AutoThread thread; + Thread *current_thread = Thread::Current(); + ASSERT(current_thread != NULL); // AutoThread ensures this + + crit_.Enter(); + bool ready = false; + _SendMessage smsg; + smsg.thread = current_thread; + smsg.msg = msg; + smsg.ready = &ready; + sendlist_.push_back(smsg); + has_sends_ = true; + crit_.Leave(); + + // Wait for a reply + + ss_->WakeUp(); + while (!ready) { + current_thread->ReceiveSends(); + current_thread->socketserver()->Wait(-1, false); + } +} + +void Thread::ReceiveSends() { + // Before entering critical section, check boolean. + + if (!has_sends_) + return; + + // Receive a sent message. Cleanup scenarios: + // - thread sending exits: We don't allow this, since thread can exit + // only via Join, so Send must complete. + // - thread receiving exits: Wakeup/set ready in Thread::Clear() + // - object target cleared: Wakeup/set ready in Thread::Clear() + crit_.Enter(); + while (!sendlist_.empty()) { + _SendMessage smsg = sendlist_.front(); + sendlist_.pop_front(); + crit_.Leave(); + smsg.msg.phandler->OnMessage(&smsg.msg); + crit_.Enter(); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + } + has_sends_ = false; + crit_.Leave(); +} + +void Thread::Clear(MessageHandler *phandler, uint32 id) { + CritScope cs(&crit_); + + // Remove messages on sendlist_ with phandler + // Object target cleared: remove from send list, wakeup/set ready + // if sender not NULL. + + std::list<_SendMessage>::iterator iter = sendlist_.begin(); + while (iter != sendlist_.end()) { + _SendMessage smsg = *iter; + if (phandler == NULL || smsg.msg.phandler == phandler) { + if (id == (uint32)-1 || smsg.msg.message_id == id) { + iter = sendlist_.erase(iter); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + continue; + } + } + ++iter; + } + + MessageQueue::Clear(phandler, id); +} + +AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { + if (!ThreadManager::CurrentThread()) { + ThreadManager::SetCurrent(this); + } +} + +AutoThread::~AutoThread() { + if (ThreadManager::CurrentThread() == this) { + ThreadManager::SetCurrent(NULL); + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am index 64e09526..9464e252 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS = call call_CXXFLAGS = $(AM_CXXFLAGS) -call_SOURCES = call_main.cc callclient.cc console.cc presencepushtask.cc presenceouttask.cc +call_SOURCES = call_main.cpp callclient.cpp console.cpp presencepushtask.cpp presenceouttask.cpp noinst_HEADERS = callclient.h console.h presenceouttask.h presencepushtask.h status.h call_LDADD = \ $(srcdir)/../../../talk/examples/login/libcricketexampleslogin.la \ @@ -14,4 +14,4 @@ call_LDADD = \ $(srcdir)/../../../talk/third_party/ortp/libortp.la \ $(EXPAT_LIBS) -lpthread $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) -lasound AM_CPPFLAGS = -DPOSIX -DEFAULT_INCLUDES = -I$(srcdir)/../../.. \ No newline at end of file +DEFAULT_INCLUDES = -I$(srcdir)/../../.. diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro index ccf0638b..ee1538e0 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro @@ -6,14 +6,14 @@ include(../../../../../conf.pri) # Input SOURCES += \ - call_main.cc \ - callclient.cc \ - console.cc \ - presenceouttask.cc \ - presencepushtask.cc \ - ../login/xmppauth.cc \ - ../login/xmpppump.cc \ - ../login/xmppsocket.cc \ - ../login/xmppthread.cc + call_main.cpp \ + callclient.cpp \ + console.cpp \ + presenceouttask.cpp \ + presencepushtask.cpp \ + ../login/xmppauth.cpp \ + ../login/xmpppump.cpp \ + ../login/xmppsocket.cpp \ + ../login/xmppthread.cpp LIBS += ../../../liblibjingle.a diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc deleted file mode 100644 index 661f2849..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Jingle call example - * Copyright 2004--2005, Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "talk/xmpp/xmppclientsettings.h" -#include "talk/examples/login/xmppthread.h" -#include "talk/examples/login/xmppauth.h" -#include "talk/examples/call/callclient.h" -#include "talk/examples/call/console.h" - -void GetString(const char* desc, char* out) { - printf("%s: ", desc); - fflush(stdout); - scanf("%s", out); -} - -int main(int argc, char **argv) { - // TODO: Make this into a console task - char username[256], auth_cookie[256]; - GetString("Username", username); - GetString("Auth Cookie", auth_cookie); - - printf("Logging in as %s@gmail.com\n", username); - - // We will run the console and the XMPP client on the main thread. The - // CallClient maintains a separate worker thread for voice. - - cricket::PhysicalSocketServer ss; - cricket::Thread main_thread(&ss); - cricket::ThreadManager::SetCurrent(&main_thread); - - InitConsole(&ss); - XmppPump pump; - CallClient client(pump.client()); - - buzz::XmppClientSettings xcs; - xcs.set_user(username); - xcs.set_host("gmail.com"); - xcs.set_use_tls(false); - xcs.set_auth_cookie(auth_cookie); - xcs.set_server(cricket::SocketAddress("talk.google.com", 5222)); - pump.DoLogin(xcs, new XmppSocket(false), new XmppAuth()); - - main_thread.Loop(); - - return 0; -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cpp new file mode 100644 index 00000000..661f2849 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cpp @@ -0,0 +1,62 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include "talk/examples/login/xmppauth.h" +#include "talk/examples/call/callclient.h" +#include "talk/examples/call/console.h" + +void GetString(const char* desc, char* out) { + printf("%s: ", desc); + fflush(stdout); + scanf("%s", out); +} + +int main(int argc, char **argv) { + // TODO: Make this into a console task + char username[256], auth_cookie[256]; + GetString("Username", username); + GetString("Auth Cookie", auth_cookie); + + printf("Logging in as %s@gmail.com\n", username); + + // We will run the console and the XMPP client on the main thread. The + // CallClient maintains a separate worker thread for voice. + + cricket::PhysicalSocketServer ss; + cricket::Thread main_thread(&ss); + cricket::ThreadManager::SetCurrent(&main_thread); + + InitConsole(&ss); + XmppPump pump; + CallClient client(pump.client()); + + buzz::XmppClientSettings xcs; + xcs.set_user(username); + xcs.set_host("gmail.com"); + xcs.set_use_tls(false); + xcs.set_auth_cookie(auth_cookie); + xcs.set_server(cricket::SocketAddress("talk.google.com", 5222)); + pump.DoLogin(xcs, new XmppSocket(false), new XmppAuth()); + + main_thread.Loop(); + + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc deleted file mode 100644 index 6d818932..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Jingle call example - * Copyright 2004--2005, Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include - -#include "talk/xmpp/constants.h" -#include "talk/base/thread.h" -#include "talk/base/network.h" -#include "talk/base/socketaddress.h" -#include "talk/p2p/base/sessionmanager.h" -#include "talk/p2p/base/helpers.h" -#include "talk/p2p/client/basicportallocator.h" -#include "talk/session/receiver.h" -#include "talk/session/sessionsendtask.h" -#include "talk/session/phone/phonesessionclient.h" -#include "talk/examples/call/callclient.h" -#include "talk/examples/call/console.h" -#include "talk/examples/call/presencepushtask.h" -#include "talk/examples/call/presenceouttask.h" - -namespace { - -const char* CALL_COMMANDS = -"Available commands:\n" -"\n" -" hangup Ends the call.\n" -" mute Stops sending voice.\n" -" unmute Re-starts sending voice.\n" -""; - -class CallTask: public ConsoleTask, public sigslot::has_slots<> { -public: - CallTask(CallClient* call_client, const buzz::Jid& jid, cricket::Call* call) - : call_client_(call_client), jid_(jid), call_(call) { - } - - virtual ~CallTask() {} - - virtual void Start() { - call_client_->phone_client()->SignalCallDestroy.connect( - this, &CallTask::OnCallDestroy); - if (!call_) { - call_ = call_client_->phone_client()->CreateCall(); - call_->SignalSessionState.connect(this, &CallTask::OnSessionState); - session_ = call_->InitiateSession(jid_); - } - call_client_->phone_client()->SetFocus(call_); - } - - virtual std::string GetPrompt() { return jid_.node(); } - - virtual void ProcessLine(const std::string& line) { - std::vector words; - ParseLine(line, &words); - - if ((words.size() == 1) && (words[0] == "hangup")) { - call_->Terminate(); - SignalDone(this); - } else if ((words.size() == 1) && (words[0] == "mute")) { - call_->Mute(true); - } else if ((words.size() == 1) && (words[0] == "unmute")) { - call_->Mute(false); - } else { - console()->Print(CALL_COMMANDS); - } - } - -private: - CallClient* call_client_; - buzz::Jid jid_; - cricket::Call* call_; - cricket::Session* session_; - - void OnCallDestroy(cricket::Call* call) { - if (call == call_) { - console()->Print("call destroyed"); - SignalDone(this); - } - } - - void OnSessionState(cricket::Call* call, - cricket::Session* session, - cricket::Session::State state) { - if (state == cricket::Session::STATE_SENTINITIATE) { - console()->Print("calling..."); - } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) { - console()->Print("call answered"); - } else if (state == cricket::Session::STATE_RECEIVEDREJECT) { - console()->Print("call not answered"); - SignalDone(this); - } else if (state == cricket::Session::STATE_INPROGRESS) { - console()->Print("call in progress"); - } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) { - console()->Print("other side hung up"); - SignalDone(this); - } - } -}; - -const char* RECEIVE_COMMANDS = -"Available commands:\n" -"\n" -" accept Accepts the incoming call and switches to it.\n" -" reject Rejects the incoming call and stays with the current call.\n" -""; - -class ReceiveTask: public ConsoleTask { -public: - ReceiveTask(CallClient* call_client, - const buzz::Jid& jid, - cricket::Call* call) - : call_client_(call_client), jid_(jid), call_(call) { - } - - virtual std::string GetPrompt() { return jid_.node(); } - - virtual void ProcessLine(const std::string& line) { - std::vector words; - ParseLine(line, &words); - - if ((words.size() == 1) && (words[0] == "accept")) { - assert(call_->sessions().size() == 1); - call_->AcceptSession(call_->sessions()[0]); - Console()->Push(new CallTask(call_client_, jid_, call_)); - SignalDone(this); - } else if ((words.size() == 1) && (words[0] == "reject")) { - call_->RejectSession(call_->sessions()[0]); - SignalDone(this); - } else { - console()->Print(RECEIVE_COMMANDS); - } - } - -private: - CallClient* call_client_; - buzz::Jid jid_; - cricket::Call* call_; -}; - -const char* CONSOLE_COMMANDS = -"Available commands:\n" -"\n" -" roster Prints the online friends from your roster.\n" -" call Initiates a call to the friend with the given name.\n" -" quit Quits the application.\n" -""; - -class CallConsoleTask: public ConsoleTask { -public: - CallConsoleTask(CallClient* call_client) : call_client_(call_client) {} - virtual ~CallConsoleTask() {} - - virtual std::string GetPrompt() { return "console"; } - - virtual void ProcessLine(const std::string& line) { - std::vector words; - ParseLine(line, &words); - - if ((words.size() == 1) && (words[0] == "quit")) { - SignalDone(this); - } else if ((words.size() == 1) && (words[0] == "roster")) { - call_client_->PrintRoster(); - } else if ((words.size() == 2) && (words[0] == "call")) { - call_client_->MakeCallTo(words[1]); - } else { - console()->Print(CONSOLE_COMMANDS); - } - } - -private: - CallClient* call_client_; -}; - -const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) { - switch (show) { - case buzz::Status::SHOW_XA: return desc.c_str(); - case buzz::Status::SHOW_ONLINE: return "online"; - case buzz::Status::SHOW_AWAY: return "away"; - case buzz::Status::SHOW_DND: return "do not disturb"; - case buzz::Status::SHOW_CHAT: return "ready to chat"; - delault: return "offline"; - } -} - -} // namespace - -CallClient::CallClient(buzz::XmppClient* xmpp_client) - : xmpp_client_(xmpp_client), roster_(new RosterMap) { - xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); - Console()->Push(new CallConsoleTask(this)); -} - -CallClient::~CallClient() { - delete roster_; -} - -const std::string CallClient::strerror(buzz::XmppEngine::Error err) { - switch (err) { - case buzz::XmppEngine::ERROR_NONE: - return ""; - case buzz::XmppEngine::ERROR_XML: - return "Malformed XML or encoding error"; - case buzz::XmppEngine::ERROR_STREAM: - return "XMPP stream error"; - case buzz::XmppEngine::ERROR_VERSION: - return "XMPP version error"; - case buzz::XmppEngine::ERROR_UNAUTHORIZED: - return "User is not authorized (Confirm your GX cookie at mail.google.com)"; - case buzz::XmppEngine::ERROR_TLS: - return "TLS could not be negotiated"; - case buzz::XmppEngine::ERROR_AUTH: - return "Authentication could not be negotiated"; - case buzz::XmppEngine::ERROR_BIND: - return "Resource or session binding could not be negotiated"; - case buzz::XmppEngine::ERROR_CONNECTION_CLOSED: - return "Connection closed by output handler."; - case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED: - return "Closed by "; - case buzz::XmppEngine::ERROR_SOCKET: - return "Socket error"; - } -} - -void CallClient::OnStateChange(buzz::XmppEngine::State state) { - switch (state) { - case buzz::XmppEngine::STATE_START: - Console()->Print("connecting..."); - break; - - case buzz::XmppEngine::STATE_OPENING: - Console()->Print("logging in..."); - break; - - case buzz::XmppEngine::STATE_OPEN: - Console()->Print("logged in..."); - InitPhone(); - InitPresence(); - break; - - case buzz::XmppEngine::STATE_CLOSED: - buzz::XmppEngine::Error error = xmpp_client_->GetError(); - Console()->Print("logged out..." + strerror(error)); - exit(0); - } -} - -void CallClient::InitPhone() { - std::string client_unique = xmpp_client_->jid().Str(); - cricket::InitRandom(client_unique.c_str(), client_unique.size()); - - worker_thread_ = new cricket::Thread(); - - network_manager_ = new cricket::NetworkManager(); - - cricket::SocketAddress *stun_addr = new cricket::SocketAddress("64.233.167.126", 19302); - port_allocator_ = new cricket::BasicPortAllocator(network_manager_, stun_addr, NULL); - - session_manager_ = new cricket::SessionManager( - port_allocator_, worker_thread_); - session_manager_->SignalRequestSignaling.connect( - this, &CallClient::OnRequestSignaling); - session_manager_->OnSignalingReady(); - - phone_client_ = new cricket::PhoneSessionClient( - xmpp_client_->jid(),session_manager_); - phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); - phone_client_->SignalSendStanza.connect(this, &CallClient::OnSendStanza); - - receiver_ = new cricket::Receiver(xmpp_client_, phone_client_); - receiver_->Start(); - - worker_thread_->Start(); -} - -void CallClient::OnRequestSignaling() { - session_manager_->OnSignalingReady(); -} - -void CallClient::OnCallCreate(cricket::Call* call) { - call->SignalSessionState.connect(this, &CallClient::OnSessionState); -} - -void CallClient::OnSessionState(cricket::Call* call, - cricket::Session* session, - cricket::Session::State state) { - if (state == cricket::Session::STATE_RECEIVEDINITIATE) { - buzz::Jid jid(session->remote_address()); - Console()->Printf("Incoming call from '%s'", jid.Str().c_str()); - Console()->Push(new ReceiveTask(this, jid, call)); - } -} - -void CallClient::OnSendStanza(cricket::SessionClient *client, const buzz::XmlElement* stanza) { - cricket::SessionSendTask* sender = - new cricket::SessionSendTask(xmpp_client_, phone_client_); - sender->Send(stanza); - sender->Start(); -} - -void CallClient::InitPresence() { - presence_push_ = new buzz::PresencePushTask(xmpp_client_); - presence_push_->SignalStatusUpdate.connect( - this, &CallClient::OnStatusUpdate); - presence_push_->Start(); - - buzz::Status my_status; - my_status.set_jid(xmpp_client_->jid()); - my_status.set_available(true); - my_status.set_invisible(false); - my_status.set_show(buzz::Status::SHOW_ONLINE); - my_status.set_priority(0); - my_status.set_know_capabilities(true); - my_status.set_phone_capability(true); - my_status.set_is_google_client(true); - my_status.set_version("1.0.0.66"); - - buzz::PresenceOutTask* presence_out_ = - new buzz::PresenceOutTask(xmpp_client_); - presence_out_->Send(my_status); - presence_out_->Start(); -} - -void CallClient::OnStatusUpdate(const buzz::Status& status) { - RosterItem item; - item.jid = status.jid(); - item.show = status.show(); - item.status = status.status(); - - std::string key = item.jid.Str(); - - if (status.available() && status.phone_capability()) { - Console()->Printf("Adding to roster: %s", key.c_str()); - (*roster_)[key] = item; - } else { - Console()->Printf("Removing from roster: %s", key.c_str()); - RosterMap::iterator iter = roster_->find(key); - if (iter != roster_->end()) - roster_->erase(iter); - } -} - -void CallClient::PrintRoster() { - Console()->Printf("Roster contains %d callable", roster_->size()); - RosterMap::iterator iter = roster_->begin(); - while (iter != roster_->end()) { - Console()->Printf("%s - %s", - iter->second.jid.BareJid().Str().c_str(), - DescribeStatus(iter->second.show, iter->second.status)); - iter++; - } -} - -void CallClient::MakeCallTo(const std::string& name) { - bool found = false; - buzz::Jid found_jid; - - RosterMap::iterator iter = roster_->begin(); - while (iter != roster_->end()) { - if (iter->second.jid.node() == name) { - found = true; - found_jid = iter->second.jid; - break; - } - ++iter; - } - - if (found) { - Console()->Printf("Found online friend '%s'", found_jid.Str().c_str()); - Console()->Push(new CallTask(this, found_jid, NULL)); - } else { - Console()->Printf("Could not find online friend '%s'", name.c_str()); - } -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cpp new file mode 100644 index 00000000..6d818932 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cpp @@ -0,0 +1,390 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "talk/xmpp/constants.h" +#include "talk/base/thread.h" +#include "talk/base/network.h" +#include "talk/base/socketaddress.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/helpers.h" +#include "talk/p2p/client/basicportallocator.h" +#include "talk/session/receiver.h" +#include "talk/session/sessionsendtask.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/examples/call/callclient.h" +#include "talk/examples/call/console.h" +#include "talk/examples/call/presencepushtask.h" +#include "talk/examples/call/presenceouttask.h" + +namespace { + +const char* CALL_COMMANDS = +"Available commands:\n" +"\n" +" hangup Ends the call.\n" +" mute Stops sending voice.\n" +" unmute Re-starts sending voice.\n" +""; + +class CallTask: public ConsoleTask, public sigslot::has_slots<> { +public: + CallTask(CallClient* call_client, const buzz::Jid& jid, cricket::Call* call) + : call_client_(call_client), jid_(jid), call_(call) { + } + + virtual ~CallTask() {} + + virtual void Start() { + call_client_->phone_client()->SignalCallDestroy.connect( + this, &CallTask::OnCallDestroy); + if (!call_) { + call_ = call_client_->phone_client()->CreateCall(); + call_->SignalSessionState.connect(this, &CallTask::OnSessionState); + session_ = call_->InitiateSession(jid_); + } + call_client_->phone_client()->SetFocus(call_); + } + + virtual std::string GetPrompt() { return jid_.node(); } + + virtual void ProcessLine(const std::string& line) { + std::vector words; + ParseLine(line, &words); + + if ((words.size() == 1) && (words[0] == "hangup")) { + call_->Terminate(); + SignalDone(this); + } else if ((words.size() == 1) && (words[0] == "mute")) { + call_->Mute(true); + } else if ((words.size() == 1) && (words[0] == "unmute")) { + call_->Mute(false); + } else { + console()->Print(CALL_COMMANDS); + } + } + +private: + CallClient* call_client_; + buzz::Jid jid_; + cricket::Call* call_; + cricket::Session* session_; + + void OnCallDestroy(cricket::Call* call) { + if (call == call_) { + console()->Print("call destroyed"); + SignalDone(this); + } + } + + void OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state) { + if (state == cricket::Session::STATE_SENTINITIATE) { + console()->Print("calling..."); + } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) { + console()->Print("call answered"); + } else if (state == cricket::Session::STATE_RECEIVEDREJECT) { + console()->Print("call not answered"); + SignalDone(this); + } else if (state == cricket::Session::STATE_INPROGRESS) { + console()->Print("call in progress"); + } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) { + console()->Print("other side hung up"); + SignalDone(this); + } + } +}; + +const char* RECEIVE_COMMANDS = +"Available commands:\n" +"\n" +" accept Accepts the incoming call and switches to it.\n" +" reject Rejects the incoming call and stays with the current call.\n" +""; + +class ReceiveTask: public ConsoleTask { +public: + ReceiveTask(CallClient* call_client, + const buzz::Jid& jid, + cricket::Call* call) + : call_client_(call_client), jid_(jid), call_(call) { + } + + virtual std::string GetPrompt() { return jid_.node(); } + + virtual void ProcessLine(const std::string& line) { + std::vector words; + ParseLine(line, &words); + + if ((words.size() == 1) && (words[0] == "accept")) { + assert(call_->sessions().size() == 1); + call_->AcceptSession(call_->sessions()[0]); + Console()->Push(new CallTask(call_client_, jid_, call_)); + SignalDone(this); + } else if ((words.size() == 1) && (words[0] == "reject")) { + call_->RejectSession(call_->sessions()[0]); + SignalDone(this); + } else { + console()->Print(RECEIVE_COMMANDS); + } + } + +private: + CallClient* call_client_; + buzz::Jid jid_; + cricket::Call* call_; +}; + +const char* CONSOLE_COMMANDS = +"Available commands:\n" +"\n" +" roster Prints the online friends from your roster.\n" +" call Initiates a call to the friend with the given name.\n" +" quit Quits the application.\n" +""; + +class CallConsoleTask: public ConsoleTask { +public: + CallConsoleTask(CallClient* call_client) : call_client_(call_client) {} + virtual ~CallConsoleTask() {} + + virtual std::string GetPrompt() { return "console"; } + + virtual void ProcessLine(const std::string& line) { + std::vector words; + ParseLine(line, &words); + + if ((words.size() == 1) && (words[0] == "quit")) { + SignalDone(this); + } else if ((words.size() == 1) && (words[0] == "roster")) { + call_client_->PrintRoster(); + } else if ((words.size() == 2) && (words[0] == "call")) { + call_client_->MakeCallTo(words[1]); + } else { + console()->Print(CONSOLE_COMMANDS); + } + } + +private: + CallClient* call_client_; +}; + +const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) { + switch (show) { + case buzz::Status::SHOW_XA: return desc.c_str(); + case buzz::Status::SHOW_ONLINE: return "online"; + case buzz::Status::SHOW_AWAY: return "away"; + case buzz::Status::SHOW_DND: return "do not disturb"; + case buzz::Status::SHOW_CHAT: return "ready to chat"; + delault: return "offline"; + } +} + +} // namespace + +CallClient::CallClient(buzz::XmppClient* xmpp_client) + : xmpp_client_(xmpp_client), roster_(new RosterMap) { + xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); + Console()->Push(new CallConsoleTask(this)); +} + +CallClient::~CallClient() { + delete roster_; +} + +const std::string CallClient::strerror(buzz::XmppEngine::Error err) { + switch (err) { + case buzz::XmppEngine::ERROR_NONE: + return ""; + case buzz::XmppEngine::ERROR_XML: + return "Malformed XML or encoding error"; + case buzz::XmppEngine::ERROR_STREAM: + return "XMPP stream error"; + case buzz::XmppEngine::ERROR_VERSION: + return "XMPP version error"; + case buzz::XmppEngine::ERROR_UNAUTHORIZED: + return "User is not authorized (Confirm your GX cookie at mail.google.com)"; + case buzz::XmppEngine::ERROR_TLS: + return "TLS could not be negotiated"; + case buzz::XmppEngine::ERROR_AUTH: + return "Authentication could not be negotiated"; + case buzz::XmppEngine::ERROR_BIND: + return "Resource or session binding could not be negotiated"; + case buzz::XmppEngine::ERROR_CONNECTION_CLOSED: + return "Connection closed by output handler."; + case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED: + return "Closed by "; + case buzz::XmppEngine::ERROR_SOCKET: + return "Socket error"; + } +} + +void CallClient::OnStateChange(buzz::XmppEngine::State state) { + switch (state) { + case buzz::XmppEngine::STATE_START: + Console()->Print("connecting..."); + break; + + case buzz::XmppEngine::STATE_OPENING: + Console()->Print("logging in..."); + break; + + case buzz::XmppEngine::STATE_OPEN: + Console()->Print("logged in..."); + InitPhone(); + InitPresence(); + break; + + case buzz::XmppEngine::STATE_CLOSED: + buzz::XmppEngine::Error error = xmpp_client_->GetError(); + Console()->Print("logged out..." + strerror(error)); + exit(0); + } +} + +void CallClient::InitPhone() { + std::string client_unique = xmpp_client_->jid().Str(); + cricket::InitRandom(client_unique.c_str(), client_unique.size()); + + worker_thread_ = new cricket::Thread(); + + network_manager_ = new cricket::NetworkManager(); + + cricket::SocketAddress *stun_addr = new cricket::SocketAddress("64.233.167.126", 19302); + port_allocator_ = new cricket::BasicPortAllocator(network_manager_, stun_addr, NULL); + + session_manager_ = new cricket::SessionManager( + port_allocator_, worker_thread_); + session_manager_->SignalRequestSignaling.connect( + this, &CallClient::OnRequestSignaling); + session_manager_->OnSignalingReady(); + + phone_client_ = new cricket::PhoneSessionClient( + xmpp_client_->jid(),session_manager_); + phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); + phone_client_->SignalSendStanza.connect(this, &CallClient::OnSendStanza); + + receiver_ = new cricket::Receiver(xmpp_client_, phone_client_); + receiver_->Start(); + + worker_thread_->Start(); +} + +void CallClient::OnRequestSignaling() { + session_manager_->OnSignalingReady(); +} + +void CallClient::OnCallCreate(cricket::Call* call) { + call->SignalSessionState.connect(this, &CallClient::OnSessionState); +} + +void CallClient::OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state) { + if (state == cricket::Session::STATE_RECEIVEDINITIATE) { + buzz::Jid jid(session->remote_address()); + Console()->Printf("Incoming call from '%s'", jid.Str().c_str()); + Console()->Push(new ReceiveTask(this, jid, call)); + } +} + +void CallClient::OnSendStanza(cricket::SessionClient *client, const buzz::XmlElement* stanza) { + cricket::SessionSendTask* sender = + new cricket::SessionSendTask(xmpp_client_, phone_client_); + sender->Send(stanza); + sender->Start(); +} + +void CallClient::InitPresence() { + presence_push_ = new buzz::PresencePushTask(xmpp_client_); + presence_push_->SignalStatusUpdate.connect( + this, &CallClient::OnStatusUpdate); + presence_push_->Start(); + + buzz::Status my_status; + my_status.set_jid(xmpp_client_->jid()); + my_status.set_available(true); + my_status.set_invisible(false); + my_status.set_show(buzz::Status::SHOW_ONLINE); + my_status.set_priority(0); + my_status.set_know_capabilities(true); + my_status.set_phone_capability(true); + my_status.set_is_google_client(true); + my_status.set_version("1.0.0.66"); + + buzz::PresenceOutTask* presence_out_ = + new buzz::PresenceOutTask(xmpp_client_); + presence_out_->Send(my_status); + presence_out_->Start(); +} + +void CallClient::OnStatusUpdate(const buzz::Status& status) { + RosterItem item; + item.jid = status.jid(); + item.show = status.show(); + item.status = status.status(); + + std::string key = item.jid.Str(); + + if (status.available() && status.phone_capability()) { + Console()->Printf("Adding to roster: %s", key.c_str()); + (*roster_)[key] = item; + } else { + Console()->Printf("Removing from roster: %s", key.c_str()); + RosterMap::iterator iter = roster_->find(key); + if (iter != roster_->end()) + roster_->erase(iter); + } +} + +void CallClient::PrintRoster() { + Console()->Printf("Roster contains %d callable", roster_->size()); + RosterMap::iterator iter = roster_->begin(); + while (iter != roster_->end()) { + Console()->Printf("%s - %s", + iter->second.jid.BareJid().Str().c_str(), + DescribeStatus(iter->second.show, iter->second.status)); + iter++; + } +} + +void CallClient::MakeCallTo(const std::string& name) { + bool found = false; + buzz::Jid found_jid; + + RosterMap::iterator iter = roster_->begin(); + while (iter != roster_->end()) { + if (iter->second.jid.node() == name) { + found = true; + found_jid = iter->second.jid; + break; + } + ++iter; + } + + if (found) { + Console()->Printf("Found online friend '%s'", found_jid.Str().c_str()); + Console()->Push(new CallTask(this, found_jid, NULL)); + } else { + Console()->Printf("Could not find online friend '%s'", name.c_str()); + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc deleted file mode 100644 index 96f5dc96..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Jingle call example - * Copyright 2004--2005, Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -extern "C" { -#include -#include -#include -#include -#include -} -#include -#include -#include -#include - -#include "talk/examples/call/console.h" - -namespace { - -void PError(const char* desc) { - perror(desc); - exit(1); -} - -CConsole* gConsole = NULL; - -const uint32 MSG_UPDATE = 1; - -} // namespace - -void InitConsole(cricket::PhysicalSocketServer* ss) { - assert(gConsole == NULL); - assert(ss); - gConsole = new CConsole(ss); -} - -CConsole* Console() { - assert(gConsole); - return gConsole; -} - -CConsole::CConsole(cricket::PhysicalSocketServer* ss) - : prompting_(false), prompt_dirty_(false) { - stdin_ = ss->CreateFile(0); - stdin_->SignalReadEvent.connect(this, &CConsole::OnReadInput); - - tasks_ = new std::vector; -} - -CConsole::~CConsole() { - delete stdin_; - delete tasks_; -} - -void CConsole::Push(ConsoleTask* task) { - task->set_console(this); - task->SignalDone.connect(this, &CConsole::OnTaskDone); - tasks_->push_back(task); - task->Start(); - UpdatePrompt(); -} - -void CConsole::Remove(ConsoleTask* task) { - int index = -1; - for (size_t i = 0; i < tasks_->size(); ++i) { - if ((*tasks_)[i] == task) - index = i; - } - - assert(index >= 0); - tasks_->erase(tasks_->begin() + index); - if (static_cast(tasks_->size()) == index) - UpdatePrompt(); - - delete task; - - if (tasks_->size() == 0) - exit(0); -} - -void CConsole::Print(const char* str) { - if (prompting_) - printf("\r"); - printf("%s\n", str); - prompting_ = false; - UpdatePrompt(); -} - -void CConsole::Print(const std::string& str) { - Print(str.c_str()); -} - -void CConsole::Printf(const char* format, ...) { - va_list ap; - va_start(ap, format); - - char buf[4096]; - int size = vsnprintf(buf, sizeof(buf), format, ap); - assert(size >= 0); - assert(size < static_cast(sizeof(buf))); - buf[size] = '\0'; - Print(buf); - - va_end(ap); -} - -void CConsole::OnTaskDone(ConsoleTask* task) { - Remove(task); -} - -void CConsole::OnReadInput(cricket::AsyncFile* file) { - assert(file == stdin_); - - char buf[4096]; - int size = read(0, buf, sizeof(buf)); - if (size < 0) - PError("read"); - - prompting_ = (buf[size-1] != '\n'); - - int start = 0; - for (int i = 0; i < size; ++i) { - if (buf[i] == '\n') { - std::string line = input_; - line.append(buf + start, i + 1 - start); - input_.clear(); - - assert(tasks_->size() > 0); - tasks_->back()->ProcessLine(line); - - start = i + 1; - } - } - - input_.append(buf + start, size - start); -} - -void CConsole::OnMessage(cricket::Message* pmsg) { - assert(pmsg->message_id == MSG_UPDATE); - assert(tasks_->size() > 0); - if (prompting_) - printf("\n"); - printf("%s: %s", tasks_->back()->GetPrompt().c_str(), input_.c_str()); - fflush(stdout); - prompting_ = true; - prompt_dirty_ = false; -} - -void CConsole::UpdatePrompt() { - if (!prompt_dirty_) { - prompt_dirty_ = true; - cricket::Thread::Current()->Post(this, MSG_UPDATE); - } -} - -void ConsoleTask::ParseLine(const std::string& line, - std::vector* words) { - assert(line.size() > 0); - assert(line[line.size() - 1] == '\n'); - - int start = -1; - int state = 0; - for (int index = 0; index <= static_cast(line.size()); ++index) { - if (state == 0) { - if (!isspace(line[index])) { - start = index; - state = 1; - } - } else { - assert(state == 1); - assert(start >= 0); - if (isspace(line[index])) { - std::string word(line, start, index - start); - words->push_back(word); - start = -1; - state = 0; - } - } - } -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cpp new file mode 100644 index 00000000..96f5dc96 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cpp @@ -0,0 +1,196 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +extern "C" { +#include +#include +#include +#include +#include +} +#include +#include +#include +#include + +#include "talk/examples/call/console.h" + +namespace { + +void PError(const char* desc) { + perror(desc); + exit(1); +} + +CConsole* gConsole = NULL; + +const uint32 MSG_UPDATE = 1; + +} // namespace + +void InitConsole(cricket::PhysicalSocketServer* ss) { + assert(gConsole == NULL); + assert(ss); + gConsole = new CConsole(ss); +} + +CConsole* Console() { + assert(gConsole); + return gConsole; +} + +CConsole::CConsole(cricket::PhysicalSocketServer* ss) + : prompting_(false), prompt_dirty_(false) { + stdin_ = ss->CreateFile(0); + stdin_->SignalReadEvent.connect(this, &CConsole::OnReadInput); + + tasks_ = new std::vector; +} + +CConsole::~CConsole() { + delete stdin_; + delete tasks_; +} + +void CConsole::Push(ConsoleTask* task) { + task->set_console(this); + task->SignalDone.connect(this, &CConsole::OnTaskDone); + tasks_->push_back(task); + task->Start(); + UpdatePrompt(); +} + +void CConsole::Remove(ConsoleTask* task) { + int index = -1; + for (size_t i = 0; i < tasks_->size(); ++i) { + if ((*tasks_)[i] == task) + index = i; + } + + assert(index >= 0); + tasks_->erase(tasks_->begin() + index); + if (static_cast(tasks_->size()) == index) + UpdatePrompt(); + + delete task; + + if (tasks_->size() == 0) + exit(0); +} + +void CConsole::Print(const char* str) { + if (prompting_) + printf("\r"); + printf("%s\n", str); + prompting_ = false; + UpdatePrompt(); +} + +void CConsole::Print(const std::string& str) { + Print(str.c_str()); +} + +void CConsole::Printf(const char* format, ...) { + va_list ap; + va_start(ap, format); + + char buf[4096]; + int size = vsnprintf(buf, sizeof(buf), format, ap); + assert(size >= 0); + assert(size < static_cast(sizeof(buf))); + buf[size] = '\0'; + Print(buf); + + va_end(ap); +} + +void CConsole::OnTaskDone(ConsoleTask* task) { + Remove(task); +} + +void CConsole::OnReadInput(cricket::AsyncFile* file) { + assert(file == stdin_); + + char buf[4096]; + int size = read(0, buf, sizeof(buf)); + if (size < 0) + PError("read"); + + prompting_ = (buf[size-1] != '\n'); + + int start = 0; + for (int i = 0; i < size; ++i) { + if (buf[i] == '\n') { + std::string line = input_; + line.append(buf + start, i + 1 - start); + input_.clear(); + + assert(tasks_->size() > 0); + tasks_->back()->ProcessLine(line); + + start = i + 1; + } + } + + input_.append(buf + start, size - start); +} + +void CConsole::OnMessage(cricket::Message* pmsg) { + assert(pmsg->message_id == MSG_UPDATE); + assert(tasks_->size() > 0); + if (prompting_) + printf("\n"); + printf("%s: %s", tasks_->back()->GetPrompt().c_str(), input_.c_str()); + fflush(stdout); + prompting_ = true; + prompt_dirty_ = false; +} + +void CConsole::UpdatePrompt() { + if (!prompt_dirty_) { + prompt_dirty_ = true; + cricket::Thread::Current()->Post(this, MSG_UPDATE); + } +} + +void ConsoleTask::ParseLine(const std::string& line, + std::vector* words) { + assert(line.size() > 0); + assert(line[line.size() - 1] == '\n'); + + int start = -1; + int state = 0; + for (int index = 0; index <= static_cast(line.size()); ++index) { + if (state == 0) { + if (!isspace(line[index])) { + start = index; + state = 1; + } + } else { + assert(state == 1); + assert(start >= 0); + if (isspace(line[index])) { + std::string word(line, start, index - start); + words->push_back(word); + start = -1; + state = 0; + } + } + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc deleted file mode 100644 index a15a6c83..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Jingle call example - * Copyright 2004--2005, Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "talk/xmpp/constants.h" -#include "talk/xmpp/xmppclient.h" -#include "talk/examples/call/presenceouttask.h" - -namespace buzz { - -// string helper functions ----------------------------------------------------- -template static -bool FromString(const std::string& s, - T * t) { - std::istringstream iss(s); - return !(iss>>*t).fail(); -} - -template static -bool ToString(const T &t, - std::string* s) { - std::ostringstream oss; - oss << t; - *s = oss.str(); - return !oss.fail(); -} - -XmppReturnStatus -PresenceOutTask::Send(const Status & s) { - if (GetState() != STATE_INIT) - return XMPP_RETURN_BADSTATE; - - stanza_.reset(TranslateStatus(s)); - return XMPP_RETURN_OK; -} - -XmppReturnStatus -PresenceOutTask::SendDirected(const Jid & j, const Status & s) { - if (GetState() != STATE_INIT) - return XMPP_RETURN_BADSTATE; - - XmlElement * presence = TranslateStatus(s); - presence->AddAttr(TQN_TO, j.Str()); - stanza_.reset(presence); - return XMPP_RETURN_OK; -} - -XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) { - if (GetState() != STATE_INIT) - return XMPP_RETURN_BADSTATE; - - XmlElement * presence = new XmlElement(TQN_PRESENCE); - presence->AddAttr(TQN_TO, jid.Str()); - presence->AddAttr(TQN_TYPE, "probe"); - - stanza_.reset(presence); - return XMPP_RETURN_OK; -} - -int -PresenceOutTask::ProcessStart() { - if (SendStanza(stanza_.get()) != XMPP_RETURN_OK) - return STATE_ERROR; - return STATE_DONE; -} - -XmlElement * -PresenceOutTask::TranslateStatus(const Status & s) { - XmlElement * result = new XmlElement(TQN_PRESENCE); - if (!s.available()) { - result->AddAttr(TQN_TYPE, STR_UNAVAILABLE); - } - else { - if (s.invisible()) { - result->AddAttr(TQN_TYPE, STR_INVISIBLE); - } - - if (s.show() != Status::SHOW_ONLINE && s.show() != Status::SHOW_OFFLINE) { - result->AddElement(new XmlElement(TQN_SHOW)); - switch (s.show()) { - default: - result->AddText(STR_SHOW_AWAY, 1); - break; - case Status::SHOW_XA: - result->AddText(STR_SHOW_XA, 1); - break; - case Status::SHOW_DND: - result->AddText(STR_SHOW_DND, 1); - break; - case Status::SHOW_CHAT: - result->AddText(STR_SHOW_CHAT, 1); - break; - } - } - - result->AddElement(new XmlElement(TQN_STATUS)); - result->AddText(s.status(), 1); - - std::string pri; - ToString(s.priority(), &pri); - - result->AddElement(new XmlElement(TQN_PRIORITY)); - result->AddText(pri, 1); - - if (s.know_capabilities() && s.is_google_client()) { - result->AddElement(new XmlElement(TQN_CAPS_C, true)); - result->AddAttr(TQN_NODE, GOOGLE_CLIENT_NODE, 1); - result->AddAttr(TQN_VER, s.version(), 1); - result->AddAttr(TQN_EXT, s.phone_capability() ? "voice-v1" : "", 1); - } - - // Put the delay mark on the presence according to JEP-0091 - { - result->AddElement(new XmlElement(kQnDelayX, true)); - - // This here is why we *love* the C runtime - time_t current_time_seconds; - time(¤t_time_seconds); - struct tm* current_time = gmtime(¤t_time_seconds); - char output[256]; - strftime(output, ARRAY_SIZE(output), "%Y%m%dT%H:%M:%S", current_time); - result->AddAttr(kQnStamp, output, 1); - } - - } - - return result; -} - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cpp new file mode 100644 index 00000000..a15a6c83 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cpp @@ -0,0 +1,148 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "talk/xmpp/constants.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/examples/call/presenceouttask.h" + +namespace buzz { + +// string helper functions ----------------------------------------------------- +template static +bool FromString(const std::string& s, + T * t) { + std::istringstream iss(s); + return !(iss>>*t).fail(); +} + +template static +bool ToString(const T &t, + std::string* s) { + std::ostringstream oss; + oss << t; + *s = oss.str(); + return !oss.fail(); +} + +XmppReturnStatus +PresenceOutTask::Send(const Status & s) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + stanza_.reset(TranslateStatus(s)); + return XMPP_RETURN_OK; +} + +XmppReturnStatus +PresenceOutTask::SendDirected(const Jid & j, const Status & s) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + XmlElement * presence = TranslateStatus(s); + presence->AddAttr(TQN_TO, j.Str()); + stanza_.reset(presence); + return XMPP_RETURN_OK; +} + +XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + XmlElement * presence = new XmlElement(TQN_PRESENCE); + presence->AddAttr(TQN_TO, jid.Str()); + presence->AddAttr(TQN_TYPE, "probe"); + + stanza_.reset(presence); + return XMPP_RETURN_OK; +} + +int +PresenceOutTask::ProcessStart() { + if (SendStanza(stanza_.get()) != XMPP_RETURN_OK) + return STATE_ERROR; + return STATE_DONE; +} + +XmlElement * +PresenceOutTask::TranslateStatus(const Status & s) { + XmlElement * result = new XmlElement(TQN_PRESENCE); + if (!s.available()) { + result->AddAttr(TQN_TYPE, STR_UNAVAILABLE); + } + else { + if (s.invisible()) { + result->AddAttr(TQN_TYPE, STR_INVISIBLE); + } + + if (s.show() != Status::SHOW_ONLINE && s.show() != Status::SHOW_OFFLINE) { + result->AddElement(new XmlElement(TQN_SHOW)); + switch (s.show()) { + default: + result->AddText(STR_SHOW_AWAY, 1); + break; + case Status::SHOW_XA: + result->AddText(STR_SHOW_XA, 1); + break; + case Status::SHOW_DND: + result->AddText(STR_SHOW_DND, 1); + break; + case Status::SHOW_CHAT: + result->AddText(STR_SHOW_CHAT, 1); + break; + } + } + + result->AddElement(new XmlElement(TQN_STATUS)); + result->AddText(s.status(), 1); + + std::string pri; + ToString(s.priority(), &pri); + + result->AddElement(new XmlElement(TQN_PRIORITY)); + result->AddText(pri, 1); + + if (s.know_capabilities() && s.is_google_client()) { + result->AddElement(new XmlElement(TQN_CAPS_C, true)); + result->AddAttr(TQN_NODE, GOOGLE_CLIENT_NODE, 1); + result->AddAttr(TQN_VER, s.version(), 1); + result->AddAttr(TQN_EXT, s.phone_capability() ? "voice-v1" : "", 1); + } + + // Put the delay mark on the presence according to JEP-0091 + { + result->AddElement(new XmlElement(kQnDelayX, true)); + + // This here is why we *love* the C runtime + time_t current_time_seconds; + time(¤t_time_seconds); + struct tm* current_time = gmtime(¤t_time_seconds); + char output[256]; + strftime(output, ARRAY_SIZE(output), "%Y%m%dT%H:%M:%S", current_time); + result->AddAttr(kQnStamp, output, 1); + } + + } + + return result; +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc deleted file mode 100644 index 31d1ae77..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Jingle call example - * Copyright 2004--2005, Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "talk/examples/call/presencepushtask.h" -#include "talk/xmpp/constants.h" -#include - - -namespace buzz { - -// string helper functions ----------------------------------------------------- -template static -bool FromString(const std::string& s, - T * t) { - std::istringstream iss(s); - return !(iss>>*t).fail(); -} - -template static -bool ToString(const T &t, - std::string* s) { - std::ostringstream oss; - oss << t; - *s = oss.str(); - return !oss.fail(); -} - -static bool -IsXmlSpace(int ch) { - return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; -} - -static bool -ListContainsToken(const std::string & list, const std::string & token) { - size_t i = list.find(token); - if (i == std::string::npos || token.empty()) - return false; - bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1])); - bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()])); - return boundary_before && boundary_after; -} - - -bool -PresencePushTask::HandleStanza(const XmlElement * stanza) { - if (stanza->Name() != TQN_PRESENCE) - return false; - if (stanza->HasAttr(TQN_TYPE) && stanza->Attr(TQN_TYPE) != STR_UNAVAILABLE) - return false; - QueueStanza(stanza); - return true; -} - -static bool IsUtf8FirstByte(int c) { - return (((c)&0x80)==0) || // is single byte - ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte -} - -int -PresencePushTask::ProcessStart() { - const XmlElement * stanza = NextStanza(); - if (stanza == NULL) - return STATE_BLOCKED; - Status s; - - s.set_jid(Jid(stanza->Attr(TQN_FROM))); - - if (stanza->Attr(TQN_TYPE) == STR_UNAVAILABLE) { - s.set_available(false); - SignalStatusUpdate(s); - } - else { - s.set_available(true); - const XmlElement * status = stanza->FirstNamed(TQN_STATUS); - if (status != NULL) { - s.set_status(status->BodyText()); - - // Truncate status messages longer than 300 bytes - if (s.status().length() > 300) { - size_t len = 300; - - // Be careful not to split legal utf-8 chars in half - while (!IsUtf8FirstByte(s.status()[len]) && len > 0) { - len -= 1; - } - std::string truncated(s.status(), 0, len); - s.set_status(truncated); - } - } - - const XmlElement * priority = stanza->FirstNamed(TQN_PRIORITY); - if (priority != NULL) { - int pri; - if (FromString(priority->BodyText(), &pri)) { - s.set_priority(pri); - } - } - - const XmlElement * show = stanza->FirstNamed(TQN_SHOW); - if (show == NULL || show->FirstChild() == NULL) { - s.set_show(Status::SHOW_ONLINE); - } - else { - if (show->BodyText() == "away") { - s.set_show(Status::SHOW_AWAY); - } - else if (show->BodyText() == "xa") { - s.set_show(Status::SHOW_XA); - } - else if (show->BodyText() == "dnd") { - s.set_show(Status::SHOW_DND); - } - else if (show->BodyText() == "chat") { - s.set_show(Status::SHOW_CHAT); - } - else { - s.set_show(Status::SHOW_ONLINE); - } - } - - const XmlElement * caps = stanza->FirstNamed(TQN_CAPS_C); - if (caps != NULL) { - std::string node = caps->Attr(TQN_NODE); - std::string ver = caps->Attr(TQN_VER); - std::string exts = caps->Attr(TQN_EXT); - - s.set_know_capabilities(true); - - if (node == GOOGLE_CLIENT_NODE) { - s.set_is_google_client(true); - s.set_version(ver); - if (ListContainsToken(exts, "voice-v1")) { - s.set_phone_capability(true); - } - } - } - - const XmlElement* delay = stanza->FirstNamed(kQnDelayX); - if (delay != NULL) { - // Ideally we would parse this according to the Psuedo ISO-8601 rules - // that are laid out in JEP-0082: - // http://www.jabber.org/jeps/jep-0082.html - std::string stamp = delay->Attr(kQnStamp); - s.set_sent_time(stamp); - } - - SignalStatusUpdate(s); - } - - return STATE_START; -} - - -} - - diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cpp new file mode 100644 index 00000000..31d1ae77 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cpp @@ -0,0 +1,172 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "talk/examples/call/presencepushtask.h" +#include "talk/xmpp/constants.h" +#include + + +namespace buzz { + +// string helper functions ----------------------------------------------------- +template static +bool FromString(const std::string& s, + T * t) { + std::istringstream iss(s); + return !(iss>>*t).fail(); +} + +template static +bool ToString(const T &t, + std::string* s) { + std::ostringstream oss; + oss << t; + *s = oss.str(); + return !oss.fail(); +} + +static bool +IsXmlSpace(int ch) { + return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; +} + +static bool +ListContainsToken(const std::string & list, const std::string & token) { + size_t i = list.find(token); + if (i == std::string::npos || token.empty()) + return false; + bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1])); + bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()])); + return boundary_before && boundary_after; +} + + +bool +PresencePushTask::HandleStanza(const XmlElement * stanza) { + if (stanza->Name() != TQN_PRESENCE) + return false; + if (stanza->HasAttr(TQN_TYPE) && stanza->Attr(TQN_TYPE) != STR_UNAVAILABLE) + return false; + QueueStanza(stanza); + return true; +} + +static bool IsUtf8FirstByte(int c) { + return (((c)&0x80)==0) || // is single byte + ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte +} + +int +PresencePushTask::ProcessStart() { + const XmlElement * stanza = NextStanza(); + if (stanza == NULL) + return STATE_BLOCKED; + Status s; + + s.set_jid(Jid(stanza->Attr(TQN_FROM))); + + if (stanza->Attr(TQN_TYPE) == STR_UNAVAILABLE) { + s.set_available(false); + SignalStatusUpdate(s); + } + else { + s.set_available(true); + const XmlElement * status = stanza->FirstNamed(TQN_STATUS); + if (status != NULL) { + s.set_status(status->BodyText()); + + // Truncate status messages longer than 300 bytes + if (s.status().length() > 300) { + size_t len = 300; + + // Be careful not to split legal utf-8 chars in half + while (!IsUtf8FirstByte(s.status()[len]) && len > 0) { + len -= 1; + } + std::string truncated(s.status(), 0, len); + s.set_status(truncated); + } + } + + const XmlElement * priority = stanza->FirstNamed(TQN_PRIORITY); + if (priority != NULL) { + int pri; + if (FromString(priority->BodyText(), &pri)) { + s.set_priority(pri); + } + } + + const XmlElement * show = stanza->FirstNamed(TQN_SHOW); + if (show == NULL || show->FirstChild() == NULL) { + s.set_show(Status::SHOW_ONLINE); + } + else { + if (show->BodyText() == "away") { + s.set_show(Status::SHOW_AWAY); + } + else if (show->BodyText() == "xa") { + s.set_show(Status::SHOW_XA); + } + else if (show->BodyText() == "dnd") { + s.set_show(Status::SHOW_DND); + } + else if (show->BodyText() == "chat") { + s.set_show(Status::SHOW_CHAT); + } + else { + s.set_show(Status::SHOW_ONLINE); + } + } + + const XmlElement * caps = stanza->FirstNamed(TQN_CAPS_C); + if (caps != NULL) { + std::string node = caps->Attr(TQN_NODE); + std::string ver = caps->Attr(TQN_VER); + std::string exts = caps->Attr(TQN_EXT); + + s.set_know_capabilities(true); + + if (node == GOOGLE_CLIENT_NODE) { + s.set_is_google_client(true); + s.set_version(ver); + if (ListContainsToken(exts, "voice-v1")) { + s.set_phone_capability(true); + } + } + } + + const XmlElement* delay = stanza->FirstNamed(kQnDelayX); + if (delay != NULL) { + // Ideally we would parse this according to the Psuedo ISO-8601 rules + // that are laid out in JEP-0082: + // http://www.jabber.org/jeps/jep-0082.html + std::string stamp = delay->Attr(kQnStamp); + s.set_sent_time(stamp); + } + + SignalStatusUpdate(s); + } + + return STATE_START; +} + + +} + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am index 16164fb7..78851923 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am @@ -1,12 +1,12 @@ noinst_LTLIBRARIES= libcricketexampleslogin.la -libcricketexampleslogin_la_SOURCES = xmppsocket.cc \ - xmppauth.cc \ - xmppthread.cc \ - xmpppump.cc +libcricketexampleslogin_la_SOURCES = xmppsocket.cpp \ + xmppauth.cpp \ + xmppthread.cpp \ + xmpppump.cpp noinst_HEADERS = xmppauth.h xmpppump.h xmppsocket.h xmppthread.h bin_PROGRAMS = login login_CXXFLAGS = $(AM_CXXFLAGS) -login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc +login_SOURCES = login_main.cpp xmppsocket.cpp xmppthread.cpp xmpppump.cpp xmppauth.cpp login_LDADD = $(srcdir)/../../../talk/xmpp/libcricketxmpp.la \ $(srcdir)/../../../talk/xmllite/libcricketxmllite.la \ $(srcdir)/../../../talk/base/libcricketbase.la \ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc deleted file mode 100644 index 2939c79f..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/thread.h" -#include "talk/xmpp/xmppclientsettings.h" -#include "talk/examples/login/xmppthread.h" -#include - -int main(int argc, char **argv) { - printf("Auth Cookie: "); - fflush(stdout); - - char auth_cookie[256]; - scanf("%s", auth_cookie); - - char username[256]; - scanf("%s", username); - - // Start xmpp on a different thread - XmppThread thread; - thread.Start(); - - buzz::XmppClientSettings xcs; - xcs.set_user(username); - xcs.set_host("gmail.com"); - xcs.set_use_tls(false); - xcs.set_auth_cookie(auth_cookie); - xcs.set_server(cricket::SocketAddress("talk.google.com", 5222)); - thread.Login(xcs); - - // Use main thread for console input - std::string line; - while (std::getline(std::cin, line)) { - if (line == "quit") - break; - } - return 0; -} - diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cpp new file mode 100644 index 00000000..2939c79f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cpp @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/thread.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include + +int main(int argc, char **argv) { + printf("Auth Cookie: "); + fflush(stdout); + + char auth_cookie[256]; + scanf("%s", auth_cookie); + + char username[256]; + scanf("%s", username); + + // Start xmpp on a different thread + XmppThread thread; + thread.Start(); + + buzz::XmppClientSettings xcs; + xcs.set_user(username); + xcs.set_host("gmail.com"); + xcs.set_use_tls(false); + xcs.set_auth_cookie(auth_cookie); + xcs.set_server(cricket::SocketAddress("talk.google.com", 5222)); + thread.Login(xcs); + + // Use main thread for console input + std::string line; + while (std::getline(std::cin, line)) { + if (line == "quit") + break; + } + return 0; +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc deleted file mode 100644 index 66191f12..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc +++ /dev/null @@ -1,93 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include "talk/examples/login/xmppauth.h" -#include "talk/xmpp/saslcookiemechanism.h" -#include "talk/xmpp/saslplainmechanism.h" - -XmppAuth::XmppAuth() : done_(false), error_(false) { -} - -XmppAuth::~XmppAuth() { -} - -void XmppAuth::StartPreXmppAuth(const buzz::Jid & jid, - const cricket::SocketAddress & server, - const buzz::XmppPassword & pass, - const std::string & auth_cookie) { - jid_ = jid; - passwd_ = pass; - auth_cookie_ = auth_cookie; - error_ = auth_cookie.empty(); - done_ = true; - - SignalAuthDone(); -} - -std::string XmppAuth::ChooseBestSaslMechanism( - const std::vector & mechanisms, - bool encrypted) { - std::vector::const_iterator it; - - // a token is the weakest auth - 15s, service-limited, so prefer it. - it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-TOKEN"); - if (it != mechanisms.end()) - return "X-GOOGLE-TOKEN"; - - // a cookie is the next weakest - 14 days - it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-COOKIE"); - if (it != mechanisms.end()) - return "X-GOOGLE-COOKIE"; - - // never pass @google.com passwords without encryption!! - if (!encrypted && (jid_.domain() == "google.com")) - return ""; - - // as a last resort, use plain authentication - if (jid_.domain() != "google.com") { - it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN"); - if (it != mechanisms.end()) - return "PLAIN"; - } - - // No good mechanism found - return ""; -} - -buzz::SaslMechanism* XmppAuth::CreateSaslMechanism( - const std::string & mechanism) { - if (mechanism == "X-GOOGLE-TOKEN") { - return new buzz::SaslCookieMechanism(mechanism, jid_.Str(), auth_cookie_); - //} else if (mechanism == "X-GOOGLE-COOKIE") { - // return new buzz::SaslCookieMechanism(mechanism, jid.Str(), sid_); - } else if (mechanism == "PLAIN") { - return new buzz::SaslPlainMechanism(jid_, passwd_); - } else { - return NULL; - } -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cpp new file mode 100644 index 00000000..66191f12 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cpp @@ -0,0 +1,93 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include "talk/examples/login/xmppauth.h" +#include "talk/xmpp/saslcookiemechanism.h" +#include "talk/xmpp/saslplainmechanism.h" + +XmppAuth::XmppAuth() : done_(false), error_(false) { +} + +XmppAuth::~XmppAuth() { +} + +void XmppAuth::StartPreXmppAuth(const buzz::Jid & jid, + const cricket::SocketAddress & server, + const buzz::XmppPassword & pass, + const std::string & auth_cookie) { + jid_ = jid; + passwd_ = pass; + auth_cookie_ = auth_cookie; + error_ = auth_cookie.empty(); + done_ = true; + + SignalAuthDone(); +} + +std::string XmppAuth::ChooseBestSaslMechanism( + const std::vector & mechanisms, + bool encrypted) { + std::vector::const_iterator it; + + // a token is the weakest auth - 15s, service-limited, so prefer it. + it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-TOKEN"); + if (it != mechanisms.end()) + return "X-GOOGLE-TOKEN"; + + // a cookie is the next weakest - 14 days + it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-COOKIE"); + if (it != mechanisms.end()) + return "X-GOOGLE-COOKIE"; + + // never pass @google.com passwords without encryption!! + if (!encrypted && (jid_.domain() == "google.com")) + return ""; + + // as a last resort, use plain authentication + if (jid_.domain() != "google.com") { + it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN"); + if (it != mechanisms.end()) + return "PLAIN"; + } + + // No good mechanism found + return ""; +} + +buzz::SaslMechanism* XmppAuth::CreateSaslMechanism( + const std::string & mechanism) { + if (mechanism == "X-GOOGLE-TOKEN") { + return new buzz::SaslCookieMechanism(mechanism, jid_.Str(), auth_cookie_); + //} else if (mechanism == "X-GOOGLE-COOKIE") { + // return new buzz::SaslCookieMechanism(mechanism, jid.Str(), sid_); + } else if (mechanism == "PLAIN") { + return new buzz::SaslPlainMechanism(jid_, passwd_); + } else { + return NULL; + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc deleted file mode 100644 index 7d966fb3..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc +++ /dev/null @@ -1,73 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/examples/login/xmpppump.h" -#include "talk/examples/login/xmppauth.h" - -XmppPump::XmppPump(XmppPumpNotify * notify) { - state_ = buzz::XmppEngine::STATE_NONE; - notify_ = notify; - client_ = new buzz::XmppClient(this); // NOTE: deleted by TaskRunner -} - -void XmppPump::DoLogin(const buzz::XmppClientSettings & xcs, - buzz::AsyncSocket* socket, - buzz::PreXmppAuth* auth) { - OnStateChange(buzz::XmppEngine::STATE_START); - client_->SignalStateChange.connect(this, &XmppPump::OnStateChange); - client_->Connect(xcs, socket, auth); - client_->Start(); -} - -void XmppPump::DoDisconnect() { - client_->Disconnect(); - OnStateChange(buzz::XmppEngine::STATE_CLOSED); -} - -void XmppPump::OnStateChange(buzz::XmppEngine::State state) { - if (state_ == state) - return; - state_ = state; - if (notify_ != NULL) - notify_->OnStateChange(state); -} - -void XmppPump::WakeTasks() { - cricket::Thread::Current()->Post(this); -} - -unsigned long long XmppPump::CurrentTime() { - return cricket::Time(); -} - -void XmppPump::OnMessage(cricket::Message *pmsg) { - RunTasks(); -} - -buzz::XmppReturnStatus XmppPump::SendStanza(const buzz::XmlElement *stanza) { - return client_->SendStanza(stanza); -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cpp new file mode 100644 index 00000000..7d966fb3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cpp @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/examples/login/xmpppump.h" +#include "talk/examples/login/xmppauth.h" + +XmppPump::XmppPump(XmppPumpNotify * notify) { + state_ = buzz::XmppEngine::STATE_NONE; + notify_ = notify; + client_ = new buzz::XmppClient(this); // NOTE: deleted by TaskRunner +} + +void XmppPump::DoLogin(const buzz::XmppClientSettings & xcs, + buzz::AsyncSocket* socket, + buzz::PreXmppAuth* auth) { + OnStateChange(buzz::XmppEngine::STATE_START); + client_->SignalStateChange.connect(this, &XmppPump::OnStateChange); + client_->Connect(xcs, socket, auth); + client_->Start(); +} + +void XmppPump::DoDisconnect() { + client_->Disconnect(); + OnStateChange(buzz::XmppEngine::STATE_CLOSED); +} + +void XmppPump::OnStateChange(buzz::XmppEngine::State state) { + if (state_ == state) + return; + state_ = state; + if (notify_ != NULL) + notify_->OnStateChange(state); +} + +void XmppPump::WakeTasks() { + cricket::Thread::Current()->Post(this); +} + +unsigned long long XmppPump::CurrentTime() { + return cricket::Time(); +} + +void XmppPump::OnMessage(cricket::Message *pmsg) { + RunTasks(); +} + +buzz::XmppReturnStatus XmppPump::SendStanza(const buzz::XmlElement *stanza) { + return client_->SendStanza(stanza); +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc deleted file mode 100644 index 33aabf3e..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include "talk/base/basicdefs.h" -#include "talk/base/logging.h" -#include "talk/base/thread.h" -#ifdef FEATURE_ENABLE_SSL -#include "talk/base/schanneladapter.h" -#endif -#include "xmppsocket.h" - -XmppSocket::XmppSocket(bool tls) : tls_(tls) { - cricket::Thread* pth = cricket::Thread::Current(); - cricket::AsyncSocket* socket = - pth->socketserver()->CreateAsyncSocket(SOCK_STREAM); -#ifdef FEATURE_ENABLE_SSL - if (tls_) - socket = new cricket::SChannelAdapter(socket); -#endif - cricket_socket_ = socket; - cricket_socket_->SignalReadEvent.connect(this, &XmppSocket::OnReadEvent); - cricket_socket_->SignalWriteEvent.connect(this, &XmppSocket::OnWriteEvent); - cricket_socket_->SignalConnectEvent.connect(this, - &XmppSocket::OnConnectEvent); - state_ = buzz::AsyncSocket::STATE_CLOSED; -} - -XmppSocket::~XmppSocket() { - Close(); - delete cricket_socket_; -} - -void XmppSocket::OnReadEvent(cricket::AsyncSocket * socket) { - SignalRead(); -} - -void XmppSocket::OnWriteEvent(cricket::AsyncSocket * socket) { - // Write bytes if there are any - while (buffer_.Length() != 0) { - int written = cricket_socket_->Send(buffer_.Data(), buffer_.Length()); - if (written > 0) { - buffer_.Shift(written); - continue; - } - if (!cricket_socket_->IsBlocking()) - LOG(LS_ERROR) << "Send error: " << cricket_socket_->GetError(); - return; - } -} - -void XmppSocket::OnConnectEvent(cricket::AsyncSocket * socket) { -#if defined(FEATURE_ENABLE_SSL) - if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) { - state_ = buzz::AsyncSocket::STATE_TLS_OPEN; - SignalSSLConnected(); - OnWriteEvent(cricket_socket_); - return; - } -#endif // !defined(FEATURE_ENABLE_SSL) - state_ = buzz::AsyncSocket::STATE_OPEN; - SignalConnected(); -} - -buzz::AsyncSocket::State XmppSocket::state() { - return state_; -} - -buzz::AsyncSocket::Error XmppSocket::error() { - return buzz::AsyncSocket::ERROR_NONE; -} - -bool XmppSocket::Connect(const cricket::SocketAddress& addr) { - if (cricket_socket_->Connect(addr) < 0) { - return cricket_socket_->IsBlocking(); - } - return true; -} - -bool XmppSocket::Read(char * data, size_t len, size_t* len_read) { - int read = cricket_socket_->Recv(data, len); - if (read > 0) { - *len_read = (size_t)read; - return true; - } - return false; -} - -bool XmppSocket::Write(const char * data, size_t len) { - buffer_.WriteBytes(data, len); - OnWriteEvent(cricket_socket_); - return true; -} - -bool XmppSocket::Close() { - if (state_ != buzz::AsyncSocket::STATE_OPEN) - return false; - if (cricket_socket_->Close() == 0) { - state_ = buzz::AsyncSocket::STATE_CLOSED; - SignalClosed(); - return true; - } - return false; -} - -bool XmppSocket::StartTls(const std::string & domainname) { -#if defined(FEATURE_ENABLE_SSL) - if (!tls_) - return false; - cricket::SChannelAdapter * ssl = - static_cast(cricket_socket_); - ssl->set_ignore_bad_cert(true); - if (ssl->StartSSL(domainname.c_str(), false) != 0) - return false; - state_ = buzz::AsyncSocket::STATE_TLS_CONNECTING; - return true; -#else // !defined(FEATURE_ENABLE_SSL) - return false; -#endif // !defined(FEATURE_ENABLE_SSL) -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cpp new file mode 100644 index 00000000..33aabf3e --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cpp @@ -0,0 +1,144 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include "talk/base/basicdefs.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#ifdef FEATURE_ENABLE_SSL +#include "talk/base/schanneladapter.h" +#endif +#include "xmppsocket.h" + +XmppSocket::XmppSocket(bool tls) : tls_(tls) { + cricket::Thread* pth = cricket::Thread::Current(); + cricket::AsyncSocket* socket = + pth->socketserver()->CreateAsyncSocket(SOCK_STREAM); +#ifdef FEATURE_ENABLE_SSL + if (tls_) + socket = new cricket::SChannelAdapter(socket); +#endif + cricket_socket_ = socket; + cricket_socket_->SignalReadEvent.connect(this, &XmppSocket::OnReadEvent); + cricket_socket_->SignalWriteEvent.connect(this, &XmppSocket::OnWriteEvent); + cricket_socket_->SignalConnectEvent.connect(this, + &XmppSocket::OnConnectEvent); + state_ = buzz::AsyncSocket::STATE_CLOSED; +} + +XmppSocket::~XmppSocket() { + Close(); + delete cricket_socket_; +} + +void XmppSocket::OnReadEvent(cricket::AsyncSocket * socket) { + SignalRead(); +} + +void XmppSocket::OnWriteEvent(cricket::AsyncSocket * socket) { + // Write bytes if there are any + while (buffer_.Length() != 0) { + int written = cricket_socket_->Send(buffer_.Data(), buffer_.Length()); + if (written > 0) { + buffer_.Shift(written); + continue; + } + if (!cricket_socket_->IsBlocking()) + LOG(LS_ERROR) << "Send error: " << cricket_socket_->GetError(); + return; + } +} + +void XmppSocket::OnConnectEvent(cricket::AsyncSocket * socket) { +#if defined(FEATURE_ENABLE_SSL) + if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) { + state_ = buzz::AsyncSocket::STATE_TLS_OPEN; + SignalSSLConnected(); + OnWriteEvent(cricket_socket_); + return; + } +#endif // !defined(FEATURE_ENABLE_SSL) + state_ = buzz::AsyncSocket::STATE_OPEN; + SignalConnected(); +} + +buzz::AsyncSocket::State XmppSocket::state() { + return state_; +} + +buzz::AsyncSocket::Error XmppSocket::error() { + return buzz::AsyncSocket::ERROR_NONE; +} + +bool XmppSocket::Connect(const cricket::SocketAddress& addr) { + if (cricket_socket_->Connect(addr) < 0) { + return cricket_socket_->IsBlocking(); + } + return true; +} + +bool XmppSocket::Read(char * data, size_t len, size_t* len_read) { + int read = cricket_socket_->Recv(data, len); + if (read > 0) { + *len_read = (size_t)read; + return true; + } + return false; +} + +bool XmppSocket::Write(const char * data, size_t len) { + buffer_.WriteBytes(data, len); + OnWriteEvent(cricket_socket_); + return true; +} + +bool XmppSocket::Close() { + if (state_ != buzz::AsyncSocket::STATE_OPEN) + return false; + if (cricket_socket_->Close() == 0) { + state_ = buzz::AsyncSocket::STATE_CLOSED; + SignalClosed(); + return true; + } + return false; +} + +bool XmppSocket::StartTls(const std::string & domainname) { +#if defined(FEATURE_ENABLE_SSL) + if (!tls_) + return false; + cricket::SChannelAdapter * ssl = + static_cast(cricket_socket_); + ssl->set_ignore_bad_cert(true); + if (ssl->StartSSL(domainname.c_str(), false) != 0) + return false; + state_ = buzz::AsyncSocket::STATE_TLS_CONNECTING; + return true; +#else // !defined(FEATURE_ENABLE_SSL) + return false; +#endif // !defined(FEATURE_ENABLE_SSL) +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc deleted file mode 100644 index 7a1b2a13..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/xmpp/xmppclientsettings.h" -#include "talk/examples/login/xmppthread.h" -#include "talk/examples/login/xmppauth.h" - -namespace { - -const uint32 MSG_LOGIN = 1; -const uint32 MSG_DISCONNECT = 2; - -struct LoginData: public cricket::MessageData { - LoginData(const buzz::XmppClientSettings& s) : xcs(s) {} - virtual ~LoginData() {} - - buzz::XmppClientSettings xcs; -}; - -} // namespace - -XmppThread::XmppThread() { - pump_ = new XmppPump(this); -} - -XmppThread::~XmppThread() { - delete pump_; -} - -void XmppThread::Loop(int cms) { - Thread::Loop(cms); -} - -void XmppThread::Login(const buzz::XmppClientSettings& xcs) { - Post(this, MSG_LOGIN, new LoginData(xcs)); -} - -void XmppThread::Disconnect() { - Post(this, MSG_DISCONNECT); -} - -void XmppThread::OnStateChange(buzz::XmppEngine::State state) { -} - -void XmppThread::OnMessage(cricket::Message* pmsg) { - if (pmsg->message_id == MSG_LOGIN) { - assert(pmsg->pdata); - LoginData* data = reinterpret_cast(pmsg->pdata); - pump_->DoLogin(data->xcs, new XmppSocket(false), new XmppAuth()); - delete data; - } else if (pmsg->message_id == MSG_DISCONNECT) { - pump_->DoDisconnect(); - } else { - assert(false); - } -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cpp new file mode 100644 index 00000000..7a1b2a13 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cpp @@ -0,0 +1,80 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include "talk/examples/login/xmppauth.h" + +namespace { + +const uint32 MSG_LOGIN = 1; +const uint32 MSG_DISCONNECT = 2; + +struct LoginData: public cricket::MessageData { + LoginData(const buzz::XmppClientSettings& s) : xcs(s) {} + virtual ~LoginData() {} + + buzz::XmppClientSettings xcs; +}; + +} // namespace + +XmppThread::XmppThread() { + pump_ = new XmppPump(this); +} + +XmppThread::~XmppThread() { + delete pump_; +} + +void XmppThread::Loop(int cms) { + Thread::Loop(cms); +} + +void XmppThread::Login(const buzz::XmppClientSettings& xcs) { + Post(this, MSG_LOGIN, new LoginData(xcs)); +} + +void XmppThread::Disconnect() { + Post(this, MSG_DISCONNECT); +} + +void XmppThread::OnStateChange(buzz::XmppEngine::State state) { +} + +void XmppThread::OnMessage(cricket::Message* pmsg) { + if (pmsg->message_id == MSG_LOGIN) { + assert(pmsg->pdata); + LoginData* data = reinterpret_cast(pmsg->pdata); + pump_->DoLogin(data->xcs, new XmppSocket(false), new XmppAuth()); + delete data; + } else if (pmsg->message_id == MSG_DISCONNECT) { + pump_->DoDisconnect(); + } else { + assert(false); + } +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt index ba05a061..c96bedee 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/CMakeLists.txt @@ -24,9 +24,9 @@ include_directories( tde_add_library( cricketp2pbase STATIC_PIC SOURCES - stun.cc port.cc udpport.cc tcpport.cc helpers.cc sessionmanager.cc - session.cc p2psocket.cc relayport.cc stunrequest.cc stunport.cc - socketmanager.cc + stun.cpp port.cpp udpport.cpp tcpport.cpp helpers.cpp sessionmanager.cpp + session.cpp p2psocket.cpp relayport.cpp stunrequest.cpp stunport.cpp + socketmanager.cpp ) @@ -34,7 +34,7 @@ tde_add_library( cricketp2pbase STATIC_PIC tde_add_executable( relayserver SOURCES - relayserver.cc relayserver_main.cc + relayserver.cpp relayserver_main.cpp LINK cricketbase-static cricketp2pbase-static pthread DESTINATION ${BIN_INSTALL_DIR} @@ -45,7 +45,7 @@ tde_add_executable( relayserver tde_add_executable( stunserver SOURCES - stunserver.cc stunserver_main.cc + stunserver.cpp stunserver_main.cpp LINK cricketbase-static cricketp2pbase-static pthread DESTINATION ${BIN_INSTALL_DIR} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am index 6cc30f15..3c8e3ab7 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am @@ -1,18 +1,18 @@ ## Does not compile with final KDE_OPTIONS = nofinal -libcricketp2pbase_la_SOURCES = stun.cc \ - port.cc \ - udpport.cc \ - tcpport.cc \ - helpers.cc \ - sessionmanager.cc \ - session.cc \ - p2psocket.cc \ - relayport.cc \ - stunrequest.cc \ - stunport.cc \ - socketmanager.cc +libcricketp2pbase_la_SOURCES = stun.cpp \ + port.cpp \ + udpport.cpp \ + tcpport.cpp \ + helpers.cpp \ + sessionmanager.cpp \ + session.cpp \ + p2psocket.cpp \ + relayport.cpp \ + stunrequest.cpp \ + stunport.cpp \ + socketmanager.cpp noinst_HEADERS = candidate.h \ portallocator.h \ @@ -37,9 +37,9 @@ noinst_HEADERS = candidate.h \ AM_CPPFLAGS = -DPOSIX $(all_includes) -I$(srcdir)/../../.. bin_PROGRAMS = relayserver stunserver -relayserver_SOURCES = relayserver.cc relayserver_main.cc +relayserver_SOURCES = relayserver.cpp relayserver_main.cpp relayserver_LDADD = ../../base/libcricketbase.la libcricketp2pbase.la -lpthread -stunserver_SOURCES = stunserver.cc stunserver_main.cc +stunserver_SOURCES = stunserver.cpp stunserver_main.cpp stunserver_LDADD = ../../base/libcricketbase.la libcricketp2pbase.la -lpthread noinst_LTLIBRARIES = libcricketp2pbase.la diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc deleted file mode 100644 index 83e02a27..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/p2p/base/helpers.h" -#include "talk/base/jtime.h" -#include -#include - -// TODO: Change this implementation to use OpenSSL's RAND_bytes. That will -// give cryptographically random values on all platforms. - -#ifdef WIN32 -#include -#include -#endif - -namespace cricket { - -static long g_seed = 1L; - -int GetRandom() { - return ((g_seed = g_seed * 214013L + 2531011L) >> 16) & 0x7fff; -} - -void SetRandomSeed(unsigned long seed) -{ - g_seed = (long)seed; -} - -static bool s_initrandom; - -void InitRandom(const char *client_unique, size_t len) { - s_initrandom = true; - - // Hash this string - unique per client - - uint32 hash = 0; - if (client_unique != NULL) { - for (int i = 0; i < (int)len; i++) - hash = ((hash << 2) + hash) + client_unique[i]; - } - - // Now initialize the seed against a high resolution - // counter - -#ifdef WIN32 - LARGE_INTEGER big; - QueryPerformanceCounter(&big); - SetRandomSeed(big.LowPart ^ hash); -#else - SetRandomSeed(Time() ^ hash); -#endif -} - -const char BASE64[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' -}; - -// Generates a random string of the given length. We generate base64 values so -// that they will be printable, though that's not necessary. - -std::string CreateRandomString(int len) { - // Random number generator should of been initialized! - assert(s_initrandom); - if (!s_initrandom) - InitRandom(0, 0); - - std::string str; - for (int i = 0; i < len; i++) -#if defined(_MSC_VER) && _MSC_VER < 1300 - str.insert(str.end(), BASE64[GetRandom() & 63]); -#else - str.push_back(BASE64[GetRandom() & 63]); -#endif - return str; -} - -uint32 CreateRandomId() { - uint8 b1 = (uint8)(GetRandom() & 255); - uint8 b2 = (uint8)(GetRandom() & 255); - uint8 b3 = (uint8)(GetRandom() & 255); - uint8 b4 = (uint8)(GetRandom() & 255); - return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); -} - -bool IsBase64Char(char ch) { - return (('A' <= ch) && (ch <= 'Z')) || - (('a' <= ch) && (ch <= 'z')) || - (('0' <= ch) && (ch <= '9')) || - (ch == '+') || (ch == '/'); -} - -bool IsBase64Encoded(const std::string& str) { - for (size_t i = 0; i < str.size(); ++i) { - if (!IsBase64Char(str.at(i))) - return false; - } - return true; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cpp new file mode 100644 index 00000000..83e02a27 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cpp @@ -0,0 +1,129 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/p2p/base/helpers.h" +#include "talk/base/jtime.h" +#include +#include + +// TODO: Change this implementation to use OpenSSL's RAND_bytes. That will +// give cryptographically random values on all platforms. + +#ifdef WIN32 +#include +#include +#endif + +namespace cricket { + +static long g_seed = 1L; + +int GetRandom() { + return ((g_seed = g_seed * 214013L + 2531011L) >> 16) & 0x7fff; +} + +void SetRandomSeed(unsigned long seed) +{ + g_seed = (long)seed; +} + +static bool s_initrandom; + +void InitRandom(const char *client_unique, size_t len) { + s_initrandom = true; + + // Hash this string - unique per client + + uint32 hash = 0; + if (client_unique != NULL) { + for (int i = 0; i < (int)len; i++) + hash = ((hash << 2) + hash) + client_unique[i]; + } + + // Now initialize the seed against a high resolution + // counter + +#ifdef WIN32 + LARGE_INTEGER big; + QueryPerformanceCounter(&big); + SetRandomSeed(big.LowPart ^ hash); +#else + SetRandomSeed(Time() ^ hash); +#endif +} + +const char BASE64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +// Generates a random string of the given length. We generate base64 values so +// that they will be printable, though that's not necessary. + +std::string CreateRandomString(int len) { + // Random number generator should of been initialized! + assert(s_initrandom); + if (!s_initrandom) + InitRandom(0, 0); + + std::string str; + for (int i = 0; i < len; i++) +#if defined(_MSC_VER) && _MSC_VER < 1300 + str.insert(str.end(), BASE64[GetRandom() & 63]); +#else + str.push_back(BASE64[GetRandom() & 63]); +#endif + return str; +} + +uint32 CreateRandomId() { + uint8 b1 = (uint8)(GetRandom() & 255); + uint8 b2 = (uint8)(GetRandom() & 255); + uint8 b3 = (uint8)(GetRandom() & 255); + uint8 b4 = (uint8)(GetRandom() & 255); + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); +} + +bool IsBase64Char(char ch) { + return (('A' <= ch) && (ch <= 'Z')) || + (('a' <= ch) && (ch <= 'z')) || + (('0' <= ch) && (ch <= '9')) || + (ch == '+') || (ch == '/'); +} + +bool IsBase64Encoded(const std::string& str) { + for (size_t i = 0; i < str.size(); ++i) { + if (!IsBase64Char(str.at(i))) + return false; + } + return true; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc deleted file mode 100644 index eb53efeb..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc +++ /dev/null @@ -1,910 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -// Description of the P2PSocket class in P2PSocket.h -// -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include -#include -#include "talk/base/logging.h" -#include "talk/p2p/base/p2psocket.h" -#include -namespace { - -// messages for queuing up work for ourselves -const uint32 MSG_SORT = 1; -const uint32 MSG_PING = 2; -const uint32 MSG_ALLOCATE = 3; - -// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) -// for pinging. When the socket is writable, we will use only 1 Kbps because -// we don't want to degrade the quality on a modem. These numbers should work -// well on a 28.8K modem, which is the slowest connection on which the voice -// quality is reasonable at all. -static const uint32 PING_PACKET_SIZE = 60 * 8; -static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms -static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000;// 50ms - -// If there is a current writable connection, then we will also try hard to -// make sure it is pinged at this rate. -static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit - -// The minimum improvement in MOS that justifies a switch. -static const double kMinImprovement = 10; - -// Amount of time that we wait when *losing* writability before we try doing -// another allocation. -static const int kAllocateDelay = 1 * 1000; // 1 second - -// We will try creating a new allocator from scratch after a delay of this -// length without becoming writable (or timing out). -static const int kAllocatePeriod = 20 * 1000; // 20 seconds - -cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port, - cricket::Port* origin_port) { - if (!origin_port) - return cricket::Port::ORIGIN_MESSAGE; - else if (port == origin_port) - return cricket::Port::ORIGIN_THIS_PORT; - else - return cricket::Port::ORIGIN_OTHER_PORT; -} - -// Compares two connections based only on static information about them. -int CompareConnectionCandidates(cricket::Connection* a, - cricket::Connection* b) { - // Combine local and remote preferences - assert(a->local_candidate().preference() == a->port()->preference()); - assert(b->local_candidate().preference() == b->port()->preference()); - double a_pref = a->local_candidate().preference() - * a->remote_candidate().preference(); - double b_pref = b->local_candidate().preference() - * b->remote_candidate().preference(); - - // Now check combined preferences. Lower values get sorted last. - if (a_pref > b_pref) - return 1; - if (a_pref < b_pref) - return -1; - - return 0; -} - -// Compare two connections based on their writability and static preferences. -int CompareConnections(cricket::Connection *a, cricket::Connection *b) { - // Sort based on write-state. Better states have lower values. - if (a->write_state() < b->write_state()) - return 1; - if (a->write_state() > b->write_state()) - return -1; - - // Compare the candidate information. - return CompareConnectionCandidates(a, b); -} - -// Wraps the comparison connection into a less than operator that puts higher -// priority writable connections first. -class ConnectionCompare { -public: - bool operator()(const cricket::Connection *ca, - const cricket::Connection *cb) { - cricket::Connection* a = const_cast(ca); - cricket::Connection* b = const_cast(cb); - - // Compare first on writability and static preferences. - int cmp = CompareConnections(a, b); - if (cmp > 0) - return true; - if (cmp < 0) - return false; - - // Otherwise, sort based on latency estimate. - return a->rtt() < b->rtt(); - - // Should we bother checking for the last connection that last received - // data? It would help rendezvous on the connection that is also receiving - // packets. - // - // TODO: Yes we should definitely do this. The TCP protocol gains - // efficiency by being used bidirectionally, as opposed to two separate - // unidirectional streams. This test should probably occur before - // comparison of local prefs (assuming combined prefs are the same). We - // need to be careful though, not to bounce back and forth with both sides - // trying to rendevous with the other. - } -}; - -// Determines whether we should switch between two connections, based first on -// static preferences and then (if those are equal) on latency estimates. -bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { - if (a_conn == b_conn) - return false; - - if ((a_conn == NULL) || (b_conn == NULL)) // don't think the latter should happen - return true; - - int prefs_cmp = CompareConnections(a_conn, b_conn); - if (prefs_cmp < 0) - return true; - if (prefs_cmp > 0) - return false; - - return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; -} - -} // unnamed namespace - -namespace cricket { - -P2PSocket::P2PSocket(const std::string &name, PortAllocator *allocator) -: worker_thread_(Thread::Current()), name_(name), allocator_(allocator), - error_(0), state_(STATE_CONNECTING), waiting_for_signaling_(false), - best_connection_(NULL), pinging_started_(false), sort_dirty_(false), - was_writable_(false), was_timed_out_(true) { -} - -P2PSocket::~P2PSocket() { - assert(worker_thread_ == Thread::Current()); - - thread()->Clear(this); - - for (uint32 i = 0; i < allocator_sessions_.size(); ++i) - delete allocator_sessions_[i]; -} - -// Add the allocator session to our list so that we know which sessions -// are still active. -void P2PSocket::AddAllocatorSession(PortAllocatorSession* session) { - session->set_generation(static_cast(allocator_sessions_.size())); - allocator_sessions_.push_back(session); - - // We now only want to apply new candidates that we receive to the ports - // created by this new session because these are replacing those of the - // previous sessions. - ports_.clear(); - - session->SignalPortReady.connect(this, &P2PSocket::OnPortReady); - session->SignalCandidatesReady.connect(this, &P2PSocket::OnCandidatesReady); - session->GetInitialPorts(); - if (pinging_started_) - session->StartGetAllPorts(); -} - -// Go into the state of processing candidates, and running in general -void P2PSocket::StartProcessingCandidates() { - assert(worker_thread_ == Thread::Current()); - - // Kick off an allocator session - OnAllocate(); - - // Start pinging as the ports come in. - thread()->Post(this, MSG_PING); -} - -// Reset the socket, clear up any previous allocations and start over -void P2PSocket::Reset() { - assert(worker_thread_ == Thread::Current()); - - // Get rid of all the old allocators. This should clean up everything. - for (uint32 i = 0; i < allocator_sessions_.size(); ++i) - delete allocator_sessions_[i]; - - allocator_sessions_.clear(); - ports_.clear(); - connections_.clear(); - best_connection_ = NULL; - - // Forget about all of the candidates we got before. - remote_candidates_.clear(); - - // Revert to the connecting state. - set_state(STATE_CONNECTING); - - // Reinitialize the rest of our state. - waiting_for_signaling_ = false; - pinging_started_ = false; - sort_dirty_ = false; - was_writable_ = false; - was_timed_out_ = true; - - // Start a new allocator. - OnAllocate(); - - // Start pinging as the ports come in. - thread()->Clear(this); - thread()->Post(this, MSG_PING); -} - -// A new port is available, attempt to make connections for it -void P2PSocket::OnPortReady(PortAllocatorSession *session, Port* port) { - assert(worker_thread_ == Thread::Current()); - - // Set in-effect options on the new port - for (OptionMap::const_iterator it = options_.begin(); it != options_.end(); ++it) { - int val = port->SetOption(it->first, it->second); - if (val < 0) { - LOG(WARNING) << "SetOption(" << it->first << ", " << it->second << ") failed: " << port->GetError(); - } - } - - // Remember the ports and candidates, and signal that candidates are ready. - // The session will handle this, and send an initiate/accept/modify message - // if one is pending. - - ports_.push_back(port); - port->SignalUnknownAddress.connect(this, &P2PSocket::OnUnknownAddress); - port->SignalDestroyed.connect(this, &P2PSocket::OnPortDestroyed); - - // Attempt to create a connection from this new port to all of the remote - // candidates that we were given so far. - - std::vector::iterator iter; - for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); - ++iter) - CreateConnection(port, *iter, iter->origin_port(), false); - - SortConnections(); -} - -// A new candidate is available, let listeners know -void P2PSocket::OnCandidatesReady(PortAllocatorSession *session, - const std::vector& candidates) { - SignalCandidatesReady(this, candidates); -} - -// Handle stun packets -void P2PSocket::OnUnknownAddress(Port *port, - const SocketAddress &address, - StunMessage *stun_msg, - const std::string &remote_username) { - assert(worker_thread_ == Thread::Current()); - - // Port has received a valid stun packet from an address that no Connection - // is currently available for. See if the remote user name is in the remote - // candidate list. If it isn't return error to the stun request. - - const Candidate *candidate = NULL; - std::vector::iterator it; - for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) { - if ((*it).username() == remote_username) { - candidate = &(*it); - break; - } - } - if (candidate == NULL) { - // Don't know about this username, the request is bogus - // This sometimes happens if a binding response comes in before the ACCEPT - // message. It is totally valid; the retry state machine will try again. - - port->SendBindingErrorResponse(stun_msg, address, - STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS); - delete stun_msg; - return; - } - - // Check for connectivity to this address. Create connections - // to this address across all local ports. First, add this as a new remote - // address - - Candidate new_remote_candidate = *candidate; - new_remote_candidate.set_address(address); - //new_remote_candidate.set_protocol(port->protocol()); - - // This remote username exists. Now create connections using this candidate, - // and resort - - if (CreateConnections(new_remote_candidate, port, true)) { - // Send the pinger a successful stun response. - port->SendBindingResponse(stun_msg, address); - - // Update the list of connections since we just added another. We do this - // after sending the response since it could (in principle) delete the - // connection in question. - SortConnections(); - } else { - // Hopefully this won't occur, because changing a destination address - // shouldn't cause a new connection to fail - assert(false); - port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, - STUN_ERROR_REASON_SERVER_ERROR); - } - - delete stun_msg; -} - -// We received a candidate from the other side, make connections so we -// can try to use these remote candidates with our local candidates. -void P2PSocket::AddRemoteCandidates( - const std::vector &remote_candidates) { - assert(worker_thread_ == Thread::Current()); - - // The remote candidates have come in. Remember them and start to establish - // connections - - std::vector::const_iterator it; - for (it = remote_candidates.begin(); it != remote_candidates.end(); ++it) - CreateConnections(*it, NULL, false); - - // Resort the connections - - SortConnections(); -} - -// Creates connections from all of the ports that we care about to the given -// remote candidate. The return value is true iff we created a connection from -// the origin port. -bool P2PSocket::CreateConnections(const Candidate &remote_candidate, - Port* origin_port, - bool readable) { - assert(worker_thread_ == Thread::Current()); - - // Add a new connection for this candidate to every port that allows such a - // connection (i.e., if they have compatible protocols) and that does not - // already have a connection to an equivalent candidate. We must be careful - // to make sure that the origin port is included, even if it was pruned, - // since that may be the only port that can create this connection. - - bool created = false; - - std::vector::reverse_iterator it; - for (it = ports_.rbegin(); it != ports_.rend(); ++it) { - if (CreateConnection(*it, remote_candidate, origin_port, readable)) { - if (*it == origin_port) - created = true; - } - } - - if ((origin_port != NULL) && - find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { - if (CreateConnection(origin_port, remote_candidate, origin_port, readable)) - created = true; - } - - // Remember this remote candidate so that we can add it to future ports. - RememberRemoteCandidate(remote_candidate, origin_port); - - return created; -} - -// Setup a connection object for the local and remote candidate combination. -// And then listen to connection object for changes. -bool P2PSocket::CreateConnection(Port* port, - const Candidate& remote_candidate, - Port* origin_port, - bool readable) { - // Look for an existing connection with this remote address. If one is not - // found, then we can create a new connection for this address. - Connection* connection = port->GetConnection(remote_candidate.address()); - if (connection != NULL) { - // It is not legal to try to change any of the parameters of an existing - // connection; however, the other side can send a duplicate candidate. - if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { - LOG(INFO) << "Attempt to change a remote candidate"; - return false; - } - } else { - Port::CandidateOrigin origin = GetOrigin(port, origin_port); - connection = port->CreateConnection(remote_candidate, origin); - if (!connection) - return false; - - connections_.push_back(connection); - connection->SignalReadPacket.connect(this, &P2PSocket::OnReadPacket); - connection->SignalStateChange.connect(this, &P2PSocket::OnConnectionStateChange); - connection->SignalDestroyed.connect(this, &P2PSocket::OnConnectionDestroyed); - } - - // If we are readable, it is because we are creating this in response to a - // ping from the other side. This will cause the state to become readable. - if (readable) - connection->ReceivedPing(); - - return true; -} - -// Maintain our remote candidate list, adding this new remote one. -void P2PSocket::RememberRemoteCandidate(const Candidate& remote_candidate, - Port* origin_port) { - // Remove any candidates whose generation is older than this one. The - // presence of a new generation indicates that the old ones are not useful. - uint32 i = 0; - while (i < remote_candidates_.size()) { - if (remote_candidates_[i].generation() < remote_candidate.generation()) { - remote_candidates_.erase(remote_candidates_.begin() + i); - LOG(INFO) << "Pruning candidate from old generation: " - << remote_candidates_[i].address().ToString(); - - } else { - i += 1; - } - } - - // Make sure this candidate is not a duplicate. - for (uint32 i = 0; i < remote_candidates_.size(); ++i) { - if (remote_candidates_[i].IsEquivalent(remote_candidate)) { - LOG(INFO) << "Duplicate candidate: " - << remote_candidate.address().ToString(); - return; - } - } - - // Try this candidate for all future ports. - remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port)); - - // We have some candidates from the other side, we are now serious about - // this connection. Let's do the StartGetAllPorts thing. - if (!pinging_started_) { - pinging_started_ = true; - for (size_t i = 0; i < allocator_sessions_.size(); ++i) { - if (!allocator_sessions_[i]->IsGettingAllPorts()) - allocator_sessions_[i]->StartGetAllPorts(); - } - } -} - -// Send data to the other side, using our best connection -int P2PSocket::Send(const char *data, size_t len) { - // This can get called on any thread that is convenient to write from! - if (best_connection_ == NULL) { - error_ = EWOULDBLOCK; - return SOCKET_ERROR; - } - int sent = best_connection_->Send(data, len); - if (sent <= 0) { - assert(sent < 0); - error_ = best_connection_->GetError(); - } - return sent; -} - -// Monitor connection states -void P2PSocket::UpdateConnectionStates() { - uint32 now = Time(); - - // We need to copy the list of connections since some may delete themselves - // when we call UpdateState. - for (uint32 i = 0; i < connections_.size(); ++i) - connections_[i]->UpdateState(now); -} - -// Prepare for best candidate sorting -void P2PSocket::RequestSort() { - if (!sort_dirty_) { - worker_thread_->Post(this, MSG_SORT); - sort_dirty_ = true; - } -} - -// Sort the available connections to find the best one. We also monitor -// the number of available connections and the current state so that we -// can possibly kick off more allocators (for more connections). -void P2PSocket::SortConnections() { - assert(worker_thread_ == Thread::Current()); - - // Make sure the connection states are up-to-date since this affects how they - // will be sorted. - UpdateConnectionStates(); - - // Any changes after this point will require a re-sort. - sort_dirty_ = false; - - // Get a list of the networks that we are using. - std::set networks; - for (uint32 i = 0; i < connections_.size(); ++i) - networks.insert(connections_[i]->port()->network()); - - // Find the best alternative connection by sorting. It is important to note - // that amongst equal preference, writable connections, this will choose the - // one whose estimated latency is lowest. So it is the only one that we - // need to consider switching to. - - ConnectionCompare cmp; - std::stable_sort(connections_.begin(), connections_.end(), cmp); - Connection* top_connection = NULL; - if (connections_.size() > 0) - top_connection = connections_[0]; - - // If necessary, switch to the new choice. - if (ShouldSwitch(best_connection_, top_connection)) - SwitchBestConnectionTo(top_connection); - - // We can prune any connection for which there is a writable connection on - // the same network with better or equal prefences. We leave those with - // better preference just in case they become writable later (at which point, - // we would prune out the current best connection). We leave connections on - // other networks because they may not be using the same resources and they - // may represent very distinct paths over which we can switch. - std::set::iterator network; - for (network = networks.begin(); network != networks.end(); ++network) { - Connection* primier = GetBestConnectionOnNetwork(*network); - if (!primier || (primier->write_state() != Connection::STATE_WRITABLE)) - continue; - - for (uint32 i = 0; i < connections_.size(); ++i) { - if ((connections_[i] != primier) && - (connections_[i]->port()->network() == *network) && - (CompareConnectionCandidates(primier, connections_[i]) >= 0)) { - connections_[i]->Prune(); - } - } - } - - // Count the number of connections in the various states. - - int writable = 0; - int write_connect = 0; - int write_timeout = 0; - - for (uint32 i = 0; i < connections_.size(); ++i) { - switch (connections_[i]->write_state()) { - case Connection::STATE_WRITABLE: - ++writable; - break; - case Connection::STATE_WRITE_CONNECT: - ++write_connect; - break; - case Connection::STATE_WRITE_TIMEOUT: - ++write_timeout; - break; - default: - assert(false); - } - } - - if (writable > 0) { - HandleWritable(); - } else if (write_connect > 0) { - HandleNotWritable(); - } else { - HandleAllTimedOut(); - } - - // Notify of connection state change - SignalConnectionMonitor(this); -} - -// Track the best connection, and let listeners know -void P2PSocket::SwitchBestConnectionTo(Connection* conn) { - best_connection_ = conn; - if (best_connection_) - SignalConnectionChanged(this, - best_connection_->remote_candidate().address()); -} - -// We checked the status of our connections and we had at least one that -// was writable, go into the writable state. -void P2PSocket::HandleWritable() { - // - // One or more connections writable! - // - if (state_ != STATE_WRITABLE) { - for (uint32 i = 0; i < allocator_sessions_.size(); ++i) { - if (allocator_sessions_[i]->IsGettingAllPorts()) { - allocator_sessions_[i]->StopGetAllPorts(); - } - } - - // Stop further allocations. - thread()->Clear(this, MSG_ALLOCATE); - } - - // We're writable, obviously we aren't timed out - was_writable_ = true; - was_timed_out_ = false; - set_state(STATE_WRITABLE); -} - -// We checked the status of our connections and we didn't have any that -// were writable, go into the connecting state (kick off a new allocator -// session). -void P2PSocket::HandleNotWritable() { - // - // No connections are writable but not timed out! - // - if (was_writable_) { - // If we were writable, let's kick off an allocator session immediately - was_writable_ = false; - OnAllocate(); - } - - // We were connecting, obviously not ALL timed out. - was_timed_out_ = false; - set_state(STATE_CONNECTING); -} - -// We checked the status of our connections and not only weren't they writable -// but they were also timed out, we really need a new allocator. -void P2PSocket::HandleAllTimedOut() { - // - // No connections... all are timed out! - // - if (!was_timed_out_) { - // We weren't timed out before, so kick off an allocator now (we'll still - // be in the fully timed out state until the allocator actually gives back - // new ports) - OnAllocate(); - } - - // NOTE: we start was_timed_out_ in the true state so that we don't get - // another allocator created WHILE we are in the process of building up - // our first allocator. - was_timed_out_ = true; - was_writable_ = false; - set_state(STATE_CONNECTING); -} - -// If we have a best connection, return it, otherwise return top one in the -// list (later we will mark it best). -Connection* P2PSocket::GetBestConnectionOnNetwork(Network* network) { - // If the best connection is on this network, then it wins. - if (best_connection_ && (best_connection_->port()->network() == network)) - return best_connection_; - - // Otherwise, we return the top-most in sorted order. - for (uint32 i = 0; i < connections_.size(); ++i) { - if (connections_[i]->port()->network() == network) - return connections_[i]; - } - - return NULL; -} - -// Handle any queued up requests -void P2PSocket::OnMessage(Message *pmsg) { - if (pmsg->message_id == MSG_SORT) - OnSort(); - else if (pmsg->message_id == MSG_PING) - OnPing(); - else if (pmsg->message_id == MSG_ALLOCATE) - OnAllocate(); - else - assert(false); -} - -// Handle queued up sort request -void P2PSocket::OnSort() { - // Resort the connections based on the new statistics. - SortConnections(); -} - -// Handle queued up ping request -void P2PSocket::OnPing() { - // Make sure the states of the connections are up-to-date (since this affects - // which ones are pingable). - UpdateConnectionStates(); - - // Find the oldest pingable connection and have it do a ping. - Connection* conn = FindNextPingableConnection(); - if (conn) - conn->Ping(Time()); - - // Post ourselves a message to perform the next ping. - uint32 delay = (state_ == STATE_WRITABLE) ? WRITABLE_DELAY : UNWRITABLE_DELAY; - thread()->PostDelayed(delay, this, MSG_PING); -} - -// Is the connection in a state for us to even consider pinging the other side? -bool P2PSocket::IsPingable(Connection* conn) { - // An unconnected connection cannot be written to at all, so pinging is out - // of the question. - if (!conn->connected()) - return false; - - if (state_ == STATE_WRITABLE) { - // If we are writable, then we only want to ping connections that could be - // better than this one, i.e., the ones that were not pruned. - return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT); - } else { - // If we are not writable, then we need to try everything that might work. - // This includes both connections that do not have write timeout as well as - // ones that do not have read timeout. A connection could be readable but - // be in write-timeout if we pruned it before. Since the other side is - // still pinging it, it very well might still work. - return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) || - (conn->read_state() != Connection::STATE_READ_TIMEOUT); - } -} - -// Returns the next pingable connection to ping. This will be the oldest -// pingable connection unless we have a writable connection that is past the -// maximum acceptable ping delay. -Connection* P2PSocket::FindNextPingableConnection() { - uint32 now = Time(); - if (best_connection_ && - (best_connection_->write_state() == Connection::STATE_WRITABLE) && - (best_connection_->last_ping_sent() - + MAX_CURRENT_WRITABLE_DELAY <= now)) { - return best_connection_; - } - - Connection* oldest_conn = NULL; - uint32 oldest_time = 0xFFFFFFFF; - for (uint32 i = 0; i < connections_.size(); ++i) { - if (IsPingable(connections_[i])) { - if (connections_[i]->last_ping_sent() < oldest_time) { - oldest_time = connections_[i]->last_ping_sent(); - oldest_conn = connections_[i]; - } - } - } - return oldest_conn; -} - -// return the number of "pingable" connections -uint32 P2PSocket::NumPingableConnections() { - uint32 count = 0; - for (uint32 i = 0; i < connections_.size(); ++i) { - if (IsPingable(connections_[i])) - count += 1; - } - return count; -} - -// When a connection's state changes, we need to figure out who to use as -// the best connection again. It could have become usable, or become unusable. -void P2PSocket::OnConnectionStateChange(Connection *connection) { - assert(worker_thread_ == Thread::Current()); - - // We have to unroll the stack before doing this because we may be changing - // the state of connections while sorting. - RequestSort(); -} - -// When a connection is removed, edit it out, and then update our best -// connection. -void P2PSocket::OnConnectionDestroyed(Connection *connection) { - assert(worker_thread_ == Thread::Current()); - - // Remove this connection from the list. - std::vector::iterator iter = - find(connections_.begin(), connections_.end(), connection); - assert(iter != connections_.end()); - connections_.erase(iter); - - LOG(INFO) << "Removed connection from p2p socket: " - << static_cast(connections_.size()) << " remaining"; - - // If this is currently the best connection, then we need to pick a new one. - // The call to SortConnections will pick a new one. It looks at the current - // best connection in order to avoid switching between fairly similar ones. - // Since this connection is no longer an option, we can just set best to NULL - // and re-choose a best assuming that there was no best connection. - if (best_connection_ == connection) { - SwitchBestConnectionTo(NULL); - RequestSort(); - } -} - -// When a port is destroyed remove it from our list of ports to use for -// connection attempts. -void P2PSocket::OnPortDestroyed(Port* port) { - assert(worker_thread_ == Thread::Current()); - - // Remove this port from the list (if we didn't drop it already). - std::vector::iterator iter = find(ports_.begin(), ports_.end(), port); - if (iter != ports_.end()) - ports_.erase(iter); - - LOG(INFO) << "Removed port from p2p socket: " - << static_cast(ports_.size()) << " remaining"; -} - -// We data is available, let listeners know -void P2PSocket::OnReadPacket(Connection *connection, - const char *data, size_t len) { - assert(worker_thread_ == Thread::Current()); - - // Let the client know of an incoming packet - - SignalReadPacket(this, data, len); -} - -// return socket name -const std::string &P2PSocket::name() const { - return name_; -} - -// return socket error value -int P2PSocket::GetError() { - return error_; -} - -// return a reference to the list of connections -const std::vector& P2PSocket::connections() { - return connections_; -} - -// Set options on ourselves is simply setting options on all of our available -// port objects. -int P2PSocket::SetOption(Socket::Option opt, int value) { - OptionMap::iterator it = options_.find(opt); - if (it == options_.end()) { - options_.insert(std::make_pair(opt, value)); - } else if (it->second == value) { - return 0; - } else { - it->second = value; - } - - for (uint32 i = 0; i < ports_.size(); ++i) { - int val = ports_[i]->SetOption(opt, value); - if (val < 0) { - // Because this also occurs deferred, probably no point in reporting an error - LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: " << ports_[i]->GetError(); - } - } - return 0; -} - -// returns the current state -P2PSocket::State P2PSocket::state() { - return state_; -} - -// Set the current state, and let listeners know when it changes -void P2PSocket::set_state(P2PSocket::State state) { - assert(worker_thread_ == Thread::Current()); - if (state != state_) { - state_ = state; - SignalState(this, state); - } -} - -// Time for a new allocator, lets make sure we have a signalling channel -// to communicate candidates through first. -void P2PSocket::OnAllocate() { - // Allocation timer went off - waiting_for_signaling_ = true; - SignalRequestSignaling(); -} - -// When the signalling channel is ready, we can really kick off the allocator -void P2PSocket::OnSignalingReady() { - if (waiting_for_signaling_) { - waiting_for_signaling_ = false; - AddAllocatorSession(allocator_->CreateSession(name_)); - thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE); - } -} - -// return the current best connection writable state. -bool P2PSocket::writable() { - assert(worker_thread_ == Thread::Current()); - - if (best_connection_ == NULL) - return false; - return best_connection_->write_state() == Connection::STATE_WRITABLE; -} - -// return the worker thread -Thread *P2PSocket::thread() { - return worker_thread_; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cpp new file mode 100644 index 00000000..eb53efeb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cpp @@ -0,0 +1,910 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +// Description of the P2PSocket class in P2PSocket.h +// +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include +#include +#include "talk/base/logging.h" +#include "talk/p2p/base/p2psocket.h" +#include +namespace { + +// messages for queuing up work for ourselves +const uint32 MSG_SORT = 1; +const uint32 MSG_PING = 2; +const uint32 MSG_ALLOCATE = 3; + +// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) +// for pinging. When the socket is writable, we will use only 1 Kbps because +// we don't want to degrade the quality on a modem. These numbers should work +// well on a 28.8K modem, which is the slowest connection on which the voice +// quality is reasonable at all. +static const uint32 PING_PACKET_SIZE = 60 * 8; +static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms +static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000;// 50ms + +// If there is a current writable connection, then we will also try hard to +// make sure it is pinged at this rate. +static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit + +// The minimum improvement in MOS that justifies a switch. +static const double kMinImprovement = 10; + +// Amount of time that we wait when *losing* writability before we try doing +// another allocation. +static const int kAllocateDelay = 1 * 1000; // 1 second + +// We will try creating a new allocator from scratch after a delay of this +// length without becoming writable (or timing out). +static const int kAllocatePeriod = 20 * 1000; // 20 seconds + +cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port, + cricket::Port* origin_port) { + if (!origin_port) + return cricket::Port::ORIGIN_MESSAGE; + else if (port == origin_port) + return cricket::Port::ORIGIN_THIS_PORT; + else + return cricket::Port::ORIGIN_OTHER_PORT; +} + +// Compares two connections based only on static information about them. +int CompareConnectionCandidates(cricket::Connection* a, + cricket::Connection* b) { + // Combine local and remote preferences + assert(a->local_candidate().preference() == a->port()->preference()); + assert(b->local_candidate().preference() == b->port()->preference()); + double a_pref = a->local_candidate().preference() + * a->remote_candidate().preference(); + double b_pref = b->local_candidate().preference() + * b->remote_candidate().preference(); + + // Now check combined preferences. Lower values get sorted last. + if (a_pref > b_pref) + return 1; + if (a_pref < b_pref) + return -1; + + return 0; +} + +// Compare two connections based on their writability and static preferences. +int CompareConnections(cricket::Connection *a, cricket::Connection *b) { + // Sort based on write-state. Better states have lower values. + if (a->write_state() < b->write_state()) + return 1; + if (a->write_state() > b->write_state()) + return -1; + + // Compare the candidate information. + return CompareConnectionCandidates(a, b); +} + +// Wraps the comparison connection into a less than operator that puts higher +// priority writable connections first. +class ConnectionCompare { +public: + bool operator()(const cricket::Connection *ca, + const cricket::Connection *cb) { + cricket::Connection* a = const_cast(ca); + cricket::Connection* b = const_cast(cb); + + // Compare first on writability and static preferences. + int cmp = CompareConnections(a, b); + if (cmp > 0) + return true; + if (cmp < 0) + return false; + + // Otherwise, sort based on latency estimate. + return a->rtt() < b->rtt(); + + // Should we bother checking for the last connection that last received + // data? It would help rendezvous on the connection that is also receiving + // packets. + // + // TODO: Yes we should definitely do this. The TCP protocol gains + // efficiency by being used bidirectionally, as opposed to two separate + // unidirectional streams. This test should probably occur before + // comparison of local prefs (assuming combined prefs are the same). We + // need to be careful though, not to bounce back and forth with both sides + // trying to rendevous with the other. + } +}; + +// Determines whether we should switch between two connections, based first on +// static preferences and then (if those are equal) on latency estimates. +bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { + if (a_conn == b_conn) + return false; + + if ((a_conn == NULL) || (b_conn == NULL)) // don't think the latter should happen + return true; + + int prefs_cmp = CompareConnections(a_conn, b_conn); + if (prefs_cmp < 0) + return true; + if (prefs_cmp > 0) + return false; + + return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; +} + +} // unnamed namespace + +namespace cricket { + +P2PSocket::P2PSocket(const std::string &name, PortAllocator *allocator) +: worker_thread_(Thread::Current()), name_(name), allocator_(allocator), + error_(0), state_(STATE_CONNECTING), waiting_for_signaling_(false), + best_connection_(NULL), pinging_started_(false), sort_dirty_(false), + was_writable_(false), was_timed_out_(true) { +} + +P2PSocket::~P2PSocket() { + assert(worker_thread_ == Thread::Current()); + + thread()->Clear(this); + + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) + delete allocator_sessions_[i]; +} + +// Add the allocator session to our list so that we know which sessions +// are still active. +void P2PSocket::AddAllocatorSession(PortAllocatorSession* session) { + session->set_generation(static_cast(allocator_sessions_.size())); + allocator_sessions_.push_back(session); + + // We now only want to apply new candidates that we receive to the ports + // created by this new session because these are replacing those of the + // previous sessions. + ports_.clear(); + + session->SignalPortReady.connect(this, &P2PSocket::OnPortReady); + session->SignalCandidatesReady.connect(this, &P2PSocket::OnCandidatesReady); + session->GetInitialPorts(); + if (pinging_started_) + session->StartGetAllPorts(); +} + +// Go into the state of processing candidates, and running in general +void P2PSocket::StartProcessingCandidates() { + assert(worker_thread_ == Thread::Current()); + + // Kick off an allocator session + OnAllocate(); + + // Start pinging as the ports come in. + thread()->Post(this, MSG_PING); +} + +// Reset the socket, clear up any previous allocations and start over +void P2PSocket::Reset() { + assert(worker_thread_ == Thread::Current()); + + // Get rid of all the old allocators. This should clean up everything. + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) + delete allocator_sessions_[i]; + + allocator_sessions_.clear(); + ports_.clear(); + connections_.clear(); + best_connection_ = NULL; + + // Forget about all of the candidates we got before. + remote_candidates_.clear(); + + // Revert to the connecting state. + set_state(STATE_CONNECTING); + + // Reinitialize the rest of our state. + waiting_for_signaling_ = false; + pinging_started_ = false; + sort_dirty_ = false; + was_writable_ = false; + was_timed_out_ = true; + + // Start a new allocator. + OnAllocate(); + + // Start pinging as the ports come in. + thread()->Clear(this); + thread()->Post(this, MSG_PING); +} + +// A new port is available, attempt to make connections for it +void P2PSocket::OnPortReady(PortAllocatorSession *session, Port* port) { + assert(worker_thread_ == Thread::Current()); + + // Set in-effect options on the new port + for (OptionMap::const_iterator it = options_.begin(); it != options_.end(); ++it) { + int val = port->SetOption(it->first, it->second); + if (val < 0) { + LOG(WARNING) << "SetOption(" << it->first << ", " << it->second << ") failed: " << port->GetError(); + } + } + + // Remember the ports and candidates, and signal that candidates are ready. + // The session will handle this, and send an initiate/accept/modify message + // if one is pending. + + ports_.push_back(port); + port->SignalUnknownAddress.connect(this, &P2PSocket::OnUnknownAddress); + port->SignalDestroyed.connect(this, &P2PSocket::OnPortDestroyed); + + // Attempt to create a connection from this new port to all of the remote + // candidates that we were given so far. + + std::vector::iterator iter; + for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); + ++iter) + CreateConnection(port, *iter, iter->origin_port(), false); + + SortConnections(); +} + +// A new candidate is available, let listeners know +void P2PSocket::OnCandidatesReady(PortAllocatorSession *session, + const std::vector& candidates) { + SignalCandidatesReady(this, candidates); +} + +// Handle stun packets +void P2PSocket::OnUnknownAddress(Port *port, + const SocketAddress &address, + StunMessage *stun_msg, + const std::string &remote_username) { + assert(worker_thread_ == Thread::Current()); + + // Port has received a valid stun packet from an address that no Connection + // is currently available for. See if the remote user name is in the remote + // candidate list. If it isn't return error to the stun request. + + const Candidate *candidate = NULL; + std::vector::iterator it; + for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) { + if ((*it).username() == remote_username) { + candidate = &(*it); + break; + } + } + if (candidate == NULL) { + // Don't know about this username, the request is bogus + // This sometimes happens if a binding response comes in before the ACCEPT + // message. It is totally valid; the retry state machine will try again. + + port->SendBindingErrorResponse(stun_msg, address, + STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS); + delete stun_msg; + return; + } + + // Check for connectivity to this address. Create connections + // to this address across all local ports. First, add this as a new remote + // address + + Candidate new_remote_candidate = *candidate; + new_remote_candidate.set_address(address); + //new_remote_candidate.set_protocol(port->protocol()); + + // This remote username exists. Now create connections using this candidate, + // and resort + + if (CreateConnections(new_remote_candidate, port, true)) { + // Send the pinger a successful stun response. + port->SendBindingResponse(stun_msg, address); + + // Update the list of connections since we just added another. We do this + // after sending the response since it could (in principle) delete the + // connection in question. + SortConnections(); + } else { + // Hopefully this won't occur, because changing a destination address + // shouldn't cause a new connection to fail + assert(false); + port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, + STUN_ERROR_REASON_SERVER_ERROR); + } + + delete stun_msg; +} + +// We received a candidate from the other side, make connections so we +// can try to use these remote candidates with our local candidates. +void P2PSocket::AddRemoteCandidates( + const std::vector &remote_candidates) { + assert(worker_thread_ == Thread::Current()); + + // The remote candidates have come in. Remember them and start to establish + // connections + + std::vector::const_iterator it; + for (it = remote_candidates.begin(); it != remote_candidates.end(); ++it) + CreateConnections(*it, NULL, false); + + // Resort the connections + + SortConnections(); +} + +// Creates connections from all of the ports that we care about to the given +// remote candidate. The return value is true iff we created a connection from +// the origin port. +bool P2PSocket::CreateConnections(const Candidate &remote_candidate, + Port* origin_port, + bool readable) { + assert(worker_thread_ == Thread::Current()); + + // Add a new connection for this candidate to every port that allows such a + // connection (i.e., if they have compatible protocols) and that does not + // already have a connection to an equivalent candidate. We must be careful + // to make sure that the origin port is included, even if it was pruned, + // since that may be the only port that can create this connection. + + bool created = false; + + std::vector::reverse_iterator it; + for (it = ports_.rbegin(); it != ports_.rend(); ++it) { + if (CreateConnection(*it, remote_candidate, origin_port, readable)) { + if (*it == origin_port) + created = true; + } + } + + if ((origin_port != NULL) && + find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { + if (CreateConnection(origin_port, remote_candidate, origin_port, readable)) + created = true; + } + + // Remember this remote candidate so that we can add it to future ports. + RememberRemoteCandidate(remote_candidate, origin_port); + + return created; +} + +// Setup a connection object for the local and remote candidate combination. +// And then listen to connection object for changes. +bool P2PSocket::CreateConnection(Port* port, + const Candidate& remote_candidate, + Port* origin_port, + bool readable) { + // Look for an existing connection with this remote address. If one is not + // found, then we can create a new connection for this address. + Connection* connection = port->GetConnection(remote_candidate.address()); + if (connection != NULL) { + // It is not legal to try to change any of the parameters of an existing + // connection; however, the other side can send a duplicate candidate. + if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { + LOG(INFO) << "Attempt to change a remote candidate"; + return false; + } + } else { + Port::CandidateOrigin origin = GetOrigin(port, origin_port); + connection = port->CreateConnection(remote_candidate, origin); + if (!connection) + return false; + + connections_.push_back(connection); + connection->SignalReadPacket.connect(this, &P2PSocket::OnReadPacket); + connection->SignalStateChange.connect(this, &P2PSocket::OnConnectionStateChange); + connection->SignalDestroyed.connect(this, &P2PSocket::OnConnectionDestroyed); + } + + // If we are readable, it is because we are creating this in response to a + // ping from the other side. This will cause the state to become readable. + if (readable) + connection->ReceivedPing(); + + return true; +} + +// Maintain our remote candidate list, adding this new remote one. +void P2PSocket::RememberRemoteCandidate(const Candidate& remote_candidate, + Port* origin_port) { + // Remove any candidates whose generation is older than this one. The + // presence of a new generation indicates that the old ones are not useful. + uint32 i = 0; + while (i < remote_candidates_.size()) { + if (remote_candidates_[i].generation() < remote_candidate.generation()) { + remote_candidates_.erase(remote_candidates_.begin() + i); + LOG(INFO) << "Pruning candidate from old generation: " + << remote_candidates_[i].address().ToString(); + + } else { + i += 1; + } + } + + // Make sure this candidate is not a duplicate. + for (uint32 i = 0; i < remote_candidates_.size(); ++i) { + if (remote_candidates_[i].IsEquivalent(remote_candidate)) { + LOG(INFO) << "Duplicate candidate: " + << remote_candidate.address().ToString(); + return; + } + } + + // Try this candidate for all future ports. + remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port)); + + // We have some candidates from the other side, we are now serious about + // this connection. Let's do the StartGetAllPorts thing. + if (!pinging_started_) { + pinging_started_ = true; + for (size_t i = 0; i < allocator_sessions_.size(); ++i) { + if (!allocator_sessions_[i]->IsGettingAllPorts()) + allocator_sessions_[i]->StartGetAllPorts(); + } + } +} + +// Send data to the other side, using our best connection +int P2PSocket::Send(const char *data, size_t len) { + // This can get called on any thread that is convenient to write from! + if (best_connection_ == NULL) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = best_connection_->Send(data, len); + if (sent <= 0) { + assert(sent < 0); + error_ = best_connection_->GetError(); + } + return sent; +} + +// Monitor connection states +void P2PSocket::UpdateConnectionStates() { + uint32 now = Time(); + + // We need to copy the list of connections since some may delete themselves + // when we call UpdateState. + for (uint32 i = 0; i < connections_.size(); ++i) + connections_[i]->UpdateState(now); +} + +// Prepare for best candidate sorting +void P2PSocket::RequestSort() { + if (!sort_dirty_) { + worker_thread_->Post(this, MSG_SORT); + sort_dirty_ = true; + } +} + +// Sort the available connections to find the best one. We also monitor +// the number of available connections and the current state so that we +// can possibly kick off more allocators (for more connections). +void P2PSocket::SortConnections() { + assert(worker_thread_ == Thread::Current()); + + // Make sure the connection states are up-to-date since this affects how they + // will be sorted. + UpdateConnectionStates(); + + // Any changes after this point will require a re-sort. + sort_dirty_ = false; + + // Get a list of the networks that we are using. + std::set networks; + for (uint32 i = 0; i < connections_.size(); ++i) + networks.insert(connections_[i]->port()->network()); + + // Find the best alternative connection by sorting. It is important to note + // that amongst equal preference, writable connections, this will choose the + // one whose estimated latency is lowest. So it is the only one that we + // need to consider switching to. + + ConnectionCompare cmp; + std::stable_sort(connections_.begin(), connections_.end(), cmp); + Connection* top_connection = NULL; + if (connections_.size() > 0) + top_connection = connections_[0]; + + // If necessary, switch to the new choice. + if (ShouldSwitch(best_connection_, top_connection)) + SwitchBestConnectionTo(top_connection); + + // We can prune any connection for which there is a writable connection on + // the same network with better or equal prefences. We leave those with + // better preference just in case they become writable later (at which point, + // we would prune out the current best connection). We leave connections on + // other networks because they may not be using the same resources and they + // may represent very distinct paths over which we can switch. + std::set::iterator network; + for (network = networks.begin(); network != networks.end(); ++network) { + Connection* primier = GetBestConnectionOnNetwork(*network); + if (!primier || (primier->write_state() != Connection::STATE_WRITABLE)) + continue; + + for (uint32 i = 0; i < connections_.size(); ++i) { + if ((connections_[i] != primier) && + (connections_[i]->port()->network() == *network) && + (CompareConnectionCandidates(primier, connections_[i]) >= 0)) { + connections_[i]->Prune(); + } + } + } + + // Count the number of connections in the various states. + + int writable = 0; + int write_connect = 0; + int write_timeout = 0; + + for (uint32 i = 0; i < connections_.size(); ++i) { + switch (connections_[i]->write_state()) { + case Connection::STATE_WRITABLE: + ++writable; + break; + case Connection::STATE_WRITE_CONNECT: + ++write_connect; + break; + case Connection::STATE_WRITE_TIMEOUT: + ++write_timeout; + break; + default: + assert(false); + } + } + + if (writable > 0) { + HandleWritable(); + } else if (write_connect > 0) { + HandleNotWritable(); + } else { + HandleAllTimedOut(); + } + + // Notify of connection state change + SignalConnectionMonitor(this); +} + +// Track the best connection, and let listeners know +void P2PSocket::SwitchBestConnectionTo(Connection* conn) { + best_connection_ = conn; + if (best_connection_) + SignalConnectionChanged(this, + best_connection_->remote_candidate().address()); +} + +// We checked the status of our connections and we had at least one that +// was writable, go into the writable state. +void P2PSocket::HandleWritable() { + // + // One or more connections writable! + // + if (state_ != STATE_WRITABLE) { + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) { + if (allocator_sessions_[i]->IsGettingAllPorts()) { + allocator_sessions_[i]->StopGetAllPorts(); + } + } + + // Stop further allocations. + thread()->Clear(this, MSG_ALLOCATE); + } + + // We're writable, obviously we aren't timed out + was_writable_ = true; + was_timed_out_ = false; + set_state(STATE_WRITABLE); +} + +// We checked the status of our connections and we didn't have any that +// were writable, go into the connecting state (kick off a new allocator +// session). +void P2PSocket::HandleNotWritable() { + // + // No connections are writable but not timed out! + // + if (was_writable_) { + // If we were writable, let's kick off an allocator session immediately + was_writable_ = false; + OnAllocate(); + } + + // We were connecting, obviously not ALL timed out. + was_timed_out_ = false; + set_state(STATE_CONNECTING); +} + +// We checked the status of our connections and not only weren't they writable +// but they were also timed out, we really need a new allocator. +void P2PSocket::HandleAllTimedOut() { + // + // No connections... all are timed out! + // + if (!was_timed_out_) { + // We weren't timed out before, so kick off an allocator now (we'll still + // be in the fully timed out state until the allocator actually gives back + // new ports) + OnAllocate(); + } + + // NOTE: we start was_timed_out_ in the true state so that we don't get + // another allocator created WHILE we are in the process of building up + // our first allocator. + was_timed_out_ = true; + was_writable_ = false; + set_state(STATE_CONNECTING); +} + +// If we have a best connection, return it, otherwise return top one in the +// list (later we will mark it best). +Connection* P2PSocket::GetBestConnectionOnNetwork(Network* network) { + // If the best connection is on this network, then it wins. + if (best_connection_ && (best_connection_->port()->network() == network)) + return best_connection_; + + // Otherwise, we return the top-most in sorted order. + for (uint32 i = 0; i < connections_.size(); ++i) { + if (connections_[i]->port()->network() == network) + return connections_[i]; + } + + return NULL; +} + +// Handle any queued up requests +void P2PSocket::OnMessage(Message *pmsg) { + if (pmsg->message_id == MSG_SORT) + OnSort(); + else if (pmsg->message_id == MSG_PING) + OnPing(); + else if (pmsg->message_id == MSG_ALLOCATE) + OnAllocate(); + else + assert(false); +} + +// Handle queued up sort request +void P2PSocket::OnSort() { + // Resort the connections based on the new statistics. + SortConnections(); +} + +// Handle queued up ping request +void P2PSocket::OnPing() { + // Make sure the states of the connections are up-to-date (since this affects + // which ones are pingable). + UpdateConnectionStates(); + + // Find the oldest pingable connection and have it do a ping. + Connection* conn = FindNextPingableConnection(); + if (conn) + conn->Ping(Time()); + + // Post ourselves a message to perform the next ping. + uint32 delay = (state_ == STATE_WRITABLE) ? WRITABLE_DELAY : UNWRITABLE_DELAY; + thread()->PostDelayed(delay, this, MSG_PING); +} + +// Is the connection in a state for us to even consider pinging the other side? +bool P2PSocket::IsPingable(Connection* conn) { + // An unconnected connection cannot be written to at all, so pinging is out + // of the question. + if (!conn->connected()) + return false; + + if (state_ == STATE_WRITABLE) { + // If we are writable, then we only want to ping connections that could be + // better than this one, i.e., the ones that were not pruned. + return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT); + } else { + // If we are not writable, then we need to try everything that might work. + // This includes both connections that do not have write timeout as well as + // ones that do not have read timeout. A connection could be readable but + // be in write-timeout if we pruned it before. Since the other side is + // still pinging it, it very well might still work. + return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) || + (conn->read_state() != Connection::STATE_READ_TIMEOUT); + } +} + +// Returns the next pingable connection to ping. This will be the oldest +// pingable connection unless we have a writable connection that is past the +// maximum acceptable ping delay. +Connection* P2PSocket::FindNextPingableConnection() { + uint32 now = Time(); + if (best_connection_ && + (best_connection_->write_state() == Connection::STATE_WRITABLE) && + (best_connection_->last_ping_sent() + + MAX_CURRENT_WRITABLE_DELAY <= now)) { + return best_connection_; + } + + Connection* oldest_conn = NULL; + uint32 oldest_time = 0xFFFFFFFF; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (IsPingable(connections_[i])) { + if (connections_[i]->last_ping_sent() < oldest_time) { + oldest_time = connections_[i]->last_ping_sent(); + oldest_conn = connections_[i]; + } + } + } + return oldest_conn; +} + +// return the number of "pingable" connections +uint32 P2PSocket::NumPingableConnections() { + uint32 count = 0; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (IsPingable(connections_[i])) + count += 1; + } + return count; +} + +// When a connection's state changes, we need to figure out who to use as +// the best connection again. It could have become usable, or become unusable. +void P2PSocket::OnConnectionStateChange(Connection *connection) { + assert(worker_thread_ == Thread::Current()); + + // We have to unroll the stack before doing this because we may be changing + // the state of connections while sorting. + RequestSort(); +} + +// When a connection is removed, edit it out, and then update our best +// connection. +void P2PSocket::OnConnectionDestroyed(Connection *connection) { + assert(worker_thread_ == Thread::Current()); + + // Remove this connection from the list. + std::vector::iterator iter = + find(connections_.begin(), connections_.end(), connection); + assert(iter != connections_.end()); + connections_.erase(iter); + + LOG(INFO) << "Removed connection from p2p socket: " + << static_cast(connections_.size()) << " remaining"; + + // If this is currently the best connection, then we need to pick a new one. + // The call to SortConnections will pick a new one. It looks at the current + // best connection in order to avoid switching between fairly similar ones. + // Since this connection is no longer an option, we can just set best to NULL + // and re-choose a best assuming that there was no best connection. + if (best_connection_ == connection) { + SwitchBestConnectionTo(NULL); + RequestSort(); + } +} + +// When a port is destroyed remove it from our list of ports to use for +// connection attempts. +void P2PSocket::OnPortDestroyed(Port* port) { + assert(worker_thread_ == Thread::Current()); + + // Remove this port from the list (if we didn't drop it already). + std::vector::iterator iter = find(ports_.begin(), ports_.end(), port); + if (iter != ports_.end()) + ports_.erase(iter); + + LOG(INFO) << "Removed port from p2p socket: " + << static_cast(ports_.size()) << " remaining"; +} + +// We data is available, let listeners know +void P2PSocket::OnReadPacket(Connection *connection, + const char *data, size_t len) { + assert(worker_thread_ == Thread::Current()); + + // Let the client know of an incoming packet + + SignalReadPacket(this, data, len); +} + +// return socket name +const std::string &P2PSocket::name() const { + return name_; +} + +// return socket error value +int P2PSocket::GetError() { + return error_; +} + +// return a reference to the list of connections +const std::vector& P2PSocket::connections() { + return connections_; +} + +// Set options on ourselves is simply setting options on all of our available +// port objects. +int P2PSocket::SetOption(Socket::Option opt, int value) { + OptionMap::iterator it = options_.find(opt); + if (it == options_.end()) { + options_.insert(std::make_pair(opt, value)); + } else if (it->second == value) { + return 0; + } else { + it->second = value; + } + + for (uint32 i = 0; i < ports_.size(); ++i) { + int val = ports_[i]->SetOption(opt, value); + if (val < 0) { + // Because this also occurs deferred, probably no point in reporting an error + LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: " << ports_[i]->GetError(); + } + } + return 0; +} + +// returns the current state +P2PSocket::State P2PSocket::state() { + return state_; +} + +// Set the current state, and let listeners know when it changes +void P2PSocket::set_state(P2PSocket::State state) { + assert(worker_thread_ == Thread::Current()); + if (state != state_) { + state_ = state; + SignalState(this, state); + } +} + +// Time for a new allocator, lets make sure we have a signalling channel +// to communicate candidates through first. +void P2PSocket::OnAllocate() { + // Allocation timer went off + waiting_for_signaling_ = true; + SignalRequestSignaling(); +} + +// When the signalling channel is ready, we can really kick off the allocator +void P2PSocket::OnSignalingReady() { + if (waiting_for_signaling_) { + waiting_for_signaling_ = false; + AddAllocatorSession(allocator_->CreateSession(name_)); + thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE); + } +} + +// return the current best connection writable state. +bool P2PSocket::writable() { + assert(worker_thread_ == Thread::Current()); + + if (best_connection_ == NULL) + return false; + return best_connection_->write_state() == Connection::STATE_WRITABLE; +} + +// return the worker thread +Thread *P2PSocket::thread() { + return worker_thread_; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc deleted file mode 100644 index 14549b5b..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc +++ /dev/null @@ -1,869 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/logging.h" -#include "talk/base/asyncudpsocket.h" -#include "talk/base/asynctcpsocket.h" -#include "talk/base/socketadapters.h" -#include "talk/p2p/base/port.h" -#include "talk/p2p/base/helpers.h" -#include "talk/base/scoped_ptr.h" -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::memcmp; -} -#endif - -namespace { - -// The length of time we wait before timing out readability on a connection. -const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds - -// The length of time we wait before timing out writability on a connection. -const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds - -// The length of time we wait before we become unwritable. -const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds - -// The number of pings that must fail to respond before we become unwritable. -const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5; - -// This is the length of time that we wait for a ping response to come back. -const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds - -// Determines whether we have seen at least the given maximum number of -// pings fail to have a response. -inline bool TooManyFailures( - const std::vector& pings_since_last_response, - uint32 maximum_failures, - uint32 rtt_estimate, - uint32 now) { - - // If we haven't sent that many pings, then we can't have failed that many. - if (pings_since_last_response.size() < maximum_failures) - return false; - - // Check if the window in which we would expect a response to the ping has - // already elapsed. - return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now; -} - -// Determines whether we have gone too long without seeing any response. -inline bool TooLongWithoutResponse( - const std::vector& pings_since_last_response, - uint32 maximum_time, - uint32 now) { - - if (pings_since_last_response.size() == 0) - return false; - - return pings_since_last_response[0] + maximum_time < now; -} - -// We will restrict RTT estimates (when used for determining state) to be -// within a reasonable range. -const uint32 MINIMUM_RTT = 100; // 0.1 seconds -const uint32 MAXIMUM_RTT = 3000; // 3 seconds - -// When we don't have any RTT data, we have to pick something reasonable. We -// use a large value just in case the connection is really slow. -const uint32 DEFAULT_RTT = MAXIMUM_RTT; - -// Computes our estimate of the RTT given the current estimate and the number -// of data points on which it is based. -inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) { - if (rtt_data_points == 0) - return DEFAULT_RTT; - else - return cricket::_max(MINIMUM_RTT, cricket::_min(MAXIMUM_RTT, 2 * rtt)); -} - -// Weighting of the old rtt value to new data. -const int RTT_RATIO = 3; // 3 : 1 - -// The delay before we begin checking if this port is useless. -const int kPortTimeoutDelay = 30 * 1000; // 30 seconds - -const uint32 MSG_CHECKTIMEOUT = 1; -const uint32 MSG_DELETE = 1; - -} - -namespace cricket { - -static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" }; - -const char * ProtoToString(ProtocolType proto) { - return PROTO_NAMES[proto]; -} - -bool StringToProto(const char * value, ProtocolType& proto) { - for (size_t i=0; i<=PROTO_LAST; ++i) { - if (strcmp(PROTO_NAMES[i], value) == 0) { - proto = static_cast(i); - return true; - } - } - return false; -} - -ProxyInfo Port::proxy_; - -Port::Port(Thread* thread, const std::string& type, SocketFactory* factory, - Network* network) - : thread_(thread), factory_(factory), type_(type), network_(network), - preference_(-1), lifetime_(LT_PRESTART) { - - if (factory_ == NULL) - factory_ = thread_->socketserver(); - - set_username_fragment(CreateRandomString(16)); - set_password(CreateRandomString(16)); -} - -Port::~Port() { - // Delete all of the remaining connections. We copy the list up front - // because each deletion will cause it to be modified. - - std::vector list; - - AddressMap::iterator iter = connections_.begin(); - while (iter != connections_.end()) { - list.push_back(iter->second); - ++iter; - } - - for (uint32 i = 0; i < list.size(); i++) - delete list[i]; -} - -Connection* Port::GetConnection(const SocketAddress& remote_addr) { - AddressMap::const_iterator iter = connections_.find(remote_addr); - if (iter != connections_.end()) - return iter->second; - else - return NULL; -} - -void Port::set_username_fragment(const std::string& username_fragment) { - username_frag_ = username_fragment; -} - -void Port::set_password(const std::string& password) { - password_ = password; -} - -void Port::add_address(const SocketAddress& address, const std::string& protocol, bool final) { - Candidate c; - c.set_name(name_); - c.set_type(type_); - c.set_protocol(protocol); - c.set_address(address); - c.set_preference(preference_); - c.set_username(username_frag_); - c.set_password(password_); - c.set_network_name(network_->name()); - c.set_generation(generation_); - candidates_.push_back(c); - - if (final) - SignalAddressReady(this); -} - -void Port::AddConnection(Connection* conn) { - connections_[conn->remote_candidate().address()] = conn; - conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); - SignalConnectionCreated(this, conn); -} - -void Port::OnReadPacket( - const char* data, size_t size, const SocketAddress& addr) { - - // If this is an authenticated STUN request, then signal unknown address and - // send back a proper binding response. - StunMessage* msg; - std::string remote_username; - if (!GetStunMessage(data, size, addr, msg, remote_username)) { - LOG(LERROR) << "Received non-STUN packet from unknown address: " - << addr.ToString(); - } else if (!msg) { - // STUN message handled already - } else if (msg->type() == STUN_BINDING_REQUEST) { - SignalUnknownAddress(this, addr, msg, remote_username); - } else { - LOG(LERROR) << "Received unexpected STUN message type (" << msg->type() - << ") from unknown address: " << addr.ToString(); - delete msg; - } -} - -void Port::SendBindingRequest(Connection* conn) { - - // Construct the request message. - - StunMessage request; - request.SetType(STUN_BINDING_REQUEST); - request.SetTransactionID(CreateRandomString(16)); - - StunByteStringAttribute* username_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - std::string username = conn->remote_candidate().username(); - username.append(username_frag_); - username_attr->CopyBytes(username.c_str(), (uint16)username.size()); - request.AddAttribute(username_attr); - - // Send the request message. - // NOTE: If we wanted to, this is where we would add the HMAC. - ByteBuffer buf; - request.Write(&buf); - SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false); -} - -bool Port::GetStunMessage(const char* data, size_t size, - const SocketAddress& addr, StunMessage *& msg, - std::string& remote_username) { - // NOTE: This could clearly be optimized to avoid allocating any memory. - // However, at the data rates we'll be looking at on the client side, - // this probably isn't worth worrying about. - - msg = 0; - - // Parse the request message. If the packet is not a complete and correct - // STUN message, then ignore it. - buzz::scoped_ptr stun_msg(new StunMessage()); - ByteBuffer buf(data, size); - if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { - return false; - } - - // The packet must include a username that either begins or ends with our - // fragment. It should begin with our fragment if it is a request and it - // should end with our fragment if it is a response. - const StunByteStringAttribute* username_attr = - stun_msg->GetByteString(STUN_ATTR_USERNAME); - - int remote_frag_len = (username_attr ? username_attr->length() : 0); - remote_frag_len -= static_cast(username_frag_.size()); - - if (stun_msg->type() == STUN_BINDING_REQUEST) { - if ((remote_frag_len < 0) - || (std::memcmp(username_attr->bytes(), - username_frag_.c_str(), username_frag_.size()) != 0)) { - LOG(LERROR) << "Received STUN request with bad username"; - SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, - STUN_ERROR_REASON_BAD_REQUEST); - return true; - } - - remote_username.assign(username_attr->bytes() + username_frag_.size(), - username_attr->bytes() + username_attr->length()); - } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) - || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { - if ((remote_frag_len < 0) - || (std::memcmp(username_attr->bytes() + remote_frag_len, - username_frag_.c_str(), username_frag_.size()) != 0)) { - LOG(LERROR) << "Received STUN response with bad username"; - // Do not send error response to a response - return true; - } - - remote_username.assign(username_attr->bytes(), - username_attr->bytes() + remote_frag_len); - - if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) { - if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) { - LOG(LERROR) << "Received STUN binding error:" - << " class=" << error_code->error_class() - << " number=" << error_code->number() - << " reason='" << error_code->reason() << "'"; - // Return message to allow error-specific processing - } else { - LOG(LERROR) << "Received STUN error response with no error code"; - // Drop corrupt message - return true; - } - } - } else { - LOG(LERROR) << "Received STUN packet with invalid type: " - << stun_msg->type(); - return true; - } - - // Return the STUN message found. - msg = stun_msg.release(); - return true; -} - -void Port::SendBindingResponse( - StunMessage* request, const SocketAddress& addr) { - - assert(request->type() == STUN_BINDING_REQUEST); - - // Retrieve the username from the request. - const StunByteStringAttribute* username_attr = - request->GetByteString(STUN_ATTR_USERNAME); - assert(username_attr); - - // Fill in the response message. - - StunMessage response; - response.SetType(STUN_BINDING_RESPONSE); - response.SetTransactionID(request->transaction_id()); - - StunByteStringAttribute* username2_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); - response.AddAttribute(username2_attr); - - StunAddressAttribute* addr_attr = - StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); - addr_attr->SetFamily(1); - addr_attr->SetPort(addr.port()); - addr_attr->SetIP(addr.ip()); - response.AddAttribute(addr_attr); - - // Send the response message. - // NOTE: If we wanted to, this is where we would add the HMAC. - ByteBuffer buf; - response.Write(&buf); - SendTo(buf.Data(), buf.Length(), addr, false); - - // The fact that we received a successful request means that this connection - // (if one exists) should now be readable. - Connection* conn = GetConnection(addr); - assert(conn); - if (conn) - conn->ReceivedPing(); -} - -void Port::SendBindingErrorResponse( - StunMessage* request, const SocketAddress& addr, int error_code, - const std::string& reason) { - - assert(request->type() == STUN_BINDING_REQUEST); - - // Retrieve the username from the request. If it didn't have one, we - // shouldn't be responding at all. - const StunByteStringAttribute* username_attr = - request->GetByteString(STUN_ATTR_USERNAME); - assert(username_attr); - - // Fill in the response message. - - StunMessage response; - response.SetType(STUN_BINDING_ERROR_RESPONSE); - response.SetTransactionID(request->transaction_id()); - - StunByteStringAttribute* username2_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); - response.AddAttribute(username2_attr); - - StunErrorCodeAttribute* error_attr = StunAttribute::CreateErrorCode(); - error_attr->SetErrorCode(error_code); - error_attr->SetReason(reason); - response.AddAttribute(error_attr); - - // Send the response message. - // NOTE: If we wanted to, this is where we would add the HMAC. - ByteBuffer buf; - response.Write(&buf); - SendTo(buf.Data(), buf.Length(), addr, false); -} - -AsyncPacketSocket * Port::CreatePacketSocket(ProtocolType proto) { - if (proto == PROTO_UDP) { - return new AsyncUDPSocket(factory_->CreateAsyncSocket(SOCK_DGRAM)); - } else if ((proto == PROTO_TCP) || (proto == PROTO_SSLTCP)) { - AsyncSocket * socket = factory_->CreateAsyncSocket(SOCK_STREAM); - switch (proxy().type) { - case PROXY_NONE: - break; - case PROXY_SOCKS5: - socket = new AsyncSocksProxySocket(socket, proxy().address, proxy().username, proxy().password); - break; - case PROXY_HTTPS: - default: - socket = new AsyncHttpsProxySocket(socket, proxy().address, proxy().username, proxy().password); - break; - } - if (proto == PROTO_SSLTCP) { - socket = new AsyncSSLSocket(socket); - } - return new AsyncTCPSocket(socket); - } else { - LOG(INFO) << "Unknown protocol: " << proto; - return 0; - } -} - -void Port::OnMessage(Message *pmsg) { - assert(pmsg->message_id == MSG_CHECKTIMEOUT); - assert(lifetime_ == LT_PRETIMEOUT); - lifetime_ = LT_POSTTIMEOUT; - CheckTimeout(); -} - -void Port::Start() { - // The port sticks around for a minimum lifetime, after which - // we destroy it when it drops to zero connections. - if (lifetime_ == LT_PRESTART) { - lifetime_ = LT_PRETIMEOUT; - thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT); - } else { - LOG(WARNING) << "Port restart attempted"; - } -} - -void Port::OnConnectionDestroyed(Connection* conn) { - AddressMap::iterator iter = connections_.find(conn->remote_candidate().address()); - assert(iter != connections_.end()); - connections_.erase(iter); - - CheckTimeout(); -} - -void Port::CheckTimeout() { - // If this port has no connections, then there's no reason to keep it around. - // When the connections time out (both read and write), they will delete - // themselves, so if we have any connections, they are either readable or - // writable (or still connecting). - if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) { - LOG(INFO) << "Destroying port: " << name_ << "-" << type_; - SignalDestroyed(this); - delete this; - } -} - -// A ConnectionRequest is a simple STUN ping used to determine writability. -class ConnectionRequest : public StunRequest { -public: - ConnectionRequest(Connection* connection) : connection_(connection) { - } - - virtual ~ConnectionRequest() { - } - - virtual void Prepare(StunMessage* request) { - request->SetType(STUN_BINDING_REQUEST); - StunByteStringAttribute* username_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - std::string username = connection_->remote_candidate().username(); - username.append(connection_->port()->username_fragment()); - username_attr->CopyBytes(username.c_str(), (uint16)username.size()); - request->AddAttribute(username_attr); - } - - virtual void OnResponse(StunMessage* response) { - connection_->OnConnectionRequestResponse(response, Elapsed()); - } - - virtual void OnErrorResponse(StunMessage* response) { - connection_->OnConnectionRequestErrorResponse(response, Elapsed()); - } - - virtual void OnTimeout() { - } - - virtual int GetNextDelay() { - // Each request is sent only once. After a single delay , the request will - // time out. - timeout_ = true; - return CONNECTION_RESPONSE_TIMEOUT; - } - -private: - Connection* connection_; -}; - -// -// Connection -// - -Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate) - : requests_(port->thread()), port_(port), local_candidate_index_(index), - remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT), - write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false), - rtt_(0), rtt_data_points_(0), last_ping_sent_(0), last_ping_received_(0), - recv_total_bytes_(0), recv_bytes_second_(0), - last_recv_bytes_second_time_((uint32)-1), last_recv_bytes_second_calc_(0), - sent_total_bytes_(0), sent_bytes_second_(0), - last_sent_bytes_second_time_((uint32)-1), last_sent_bytes_second_calc_(0) { - - // Wire up to send stun packets - requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket); -} - -Connection::~Connection() { -} - -const Candidate& Connection::local_candidate() const { - if (local_candidate_index_ < port_->candidates().size()) - return port_->candidates()[local_candidate_index_]; - assert(false); - static Candidate foo; - return foo; -} - -void Connection::set_read_state(ReadState value) { - ReadState old_value = read_state_; - read_state_ = value; - if (value != old_value) { - SignalStateChange(this); - CheckTimeout(); - } -} - -void Connection::set_write_state(WriteState value) { - WriteState old_value = write_state_; - write_state_ = value; - if (value != old_value) { - SignalStateChange(this); - CheckTimeout(); - } -} - -void Connection::set_connected(bool value) { - bool old_value = connected_; - connected_ = value; - - // When connectedness is turned off, this connection is done. - if (old_value && !value) - set_write_state(STATE_WRITE_TIMEOUT); -} - -void Connection::OnSendStunPacket(const void* data, size_t size) { - port_->SendTo(data, size, remote_candidate_.address(), false); -} - -void Connection::OnReadPacket(const char* data, size_t size) { - StunMessage* msg; - std::string remote_username; - const SocketAddress& addr(remote_candidate_.address()); - if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) { - // The packet did not parse as a valid STUN message - - // If this connection is readable, then pass along the packet. - if (read_state_ == STATE_READABLE) { - // readable means data from this address is acceptable - // Send it on! - - recv_total_bytes_ += size; - SignalReadPacket(this, data, size); - - // If timed out sending writability checks, start up again - if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) - set_write_state(STATE_WRITE_CONNECT); - } else { - // Not readable means the remote address hasn't send a valid - // binding request yet. - - LOG(WARNING) << "Received non-STUN packet from an unreadable connection."; - } - } else if (!msg) { - // The packet was STUN, but was already handled - } else if (remote_username != remote_candidate_.username()) { - // Not destined this connection - LOG(LERROR) << "Received STUN packet on wrong address."; - if (msg->type() == STUN_BINDING_REQUEST) { - port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST, - STUN_ERROR_REASON_BAD_REQUEST); - } - delete msg; - } else { - // The packet is STUN, with the current username - // If this is a STUN request, then update the readable bit and respond. - // If this is a STUN response, then update the writable bit. - - switch (msg->type()) { - case STUN_BINDING_REQUEST: - // Incoming, validated stun request from remote peer. - // This call will also set the connection readable. - - port_->SendBindingResponse(msg, addr); - - // If timed out sending writability checks, start up again - if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) - set_write_state(STATE_WRITE_CONNECT); - break; - - case STUN_BINDING_RESPONSE: - case STUN_BINDING_ERROR_RESPONSE: - // Response from remote peer. Does it match request sent? - // This doesn't just check, it makes callbacks if transaction - // id's match - requests_.CheckResponse(msg); - break; - - default: - assert(false); - break; - } - - // Done with the message; delete - - delete msg; - } -} - -void Connection::Prune() { - pruned_ = true; - requests_.Clear(); - set_write_state(STATE_WRITE_TIMEOUT); -} - -void Connection::Destroy() { - set_read_state(STATE_READ_TIMEOUT); - set_write_state(STATE_WRITE_TIMEOUT); -} - -void Connection::UpdateState(uint32 now) { - // Check the readable state. - // - // Since we don't know how many pings the other side has attempted, the best - // test we can do is a simple window. - - if ((read_state_ == STATE_READABLE) && - (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) { - set_read_state(STATE_READ_TIMEOUT); - } - - // Check the writable state. (The order of these checks is important.) - // - // Before becoming unwritable, we allow for a fixed number of pings to fail - // (i.e., receive no response). We also have to give the response time to - // get back, so we include a conservative estimate of this. - // - // Before timing out writability, we give a fixed amount of time. This is to - // allow for changes in network conditions. - - uint32 rtt = ConservativeRTTEstimate(rtt_, rtt_data_points_); - - if ((write_state_ == STATE_WRITABLE) && - TooManyFailures(pings_since_last_response_, - CONNECTION_WRITE_CONNECT_FAILURES, - rtt, - now) && - TooLongWithoutResponse(pings_since_last_response_, - CONNECTION_WRITE_CONNECT_TIMEOUT, - now)) { - set_write_state(STATE_WRITE_CONNECT); - } - - if ((write_state_ == STATE_WRITE_CONNECT) && - TooLongWithoutResponse(pings_since_last_response_, - CONNECTION_WRITE_TIMEOUT, - now)) { - set_write_state(STATE_WRITE_TIMEOUT); - } -} - -void Connection::Ping(uint32 now) { - assert(connected_); - last_ping_sent_ = now; - pings_since_last_response_.push_back(now); - requests_.Send(new ConnectionRequest(this)); -} - -void Connection::ReceivedPing() { - last_ping_received_ = Time(); - set_read_state(STATE_READABLE); -} - -void Connection::OnConnectionRequestResponse(StunMessage *response, uint32 rtt) { - // We have a potentially valid reply from the remote address. - // The packet must include a username that ends with our fragment, - // since it is a response. - - // Check exact message type - bool valid = true; - if (response->type() != STUN_BINDING_RESPONSE) - valid = false; - - // Must have username attribute - const StunByteStringAttribute* username_attr = - response->GetByteString(STUN_ATTR_USERNAME); - if (valid) { - if (!username_attr) { - LOG(LERROR) << "Received likely STUN packet with no username"; - valid = false; - } - } - - // Length must be at least the size of our fragment (actually, should - // be bigger since our fragment is at the end!) - if (valid) { - if (username_attr->length() <= port_->username_fragment().size()) { - LOG(LERROR) << "Received likely STUN packet with short username"; - valid = false; - } - } - - // Compare our fragment with the end of the username - must be exact match - if (valid) { - std::string username_fragment = port_->username_fragment(); - int offset = (int)(username_attr->length() - username_fragment.size()); - if (std::memcmp(username_attr->bytes() + offset, - username_fragment.c_str(), username_fragment.size()) != 0) { - LOG(LERROR) << "Received STUN response with bad username"; - valid = false; - } - } - - if (valid) { - // Valid response. If we're not already, become writable. We may be - // bringing a pruned connection back to life, but if we don't really want - // it, we can always prune it again. - set_write_state(STATE_WRITABLE); - - pings_since_last_response_.clear(); - rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); - rtt_data_points_ += 1; - } -} - -void Connection::OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt) { - const StunErrorCodeAttribute* error = response->GetErrorCode(); - uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE; - - if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE) - || (error_code == STUN_ERROR_SERVER_ERROR) - || (error_code == STUN_ERROR_UNAUTHORIZED)) { - // Recoverable error, retry - } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) { - // Race failure, retry - } else { - // This is not a valid connection. - set_connected(false); - } -} - -void Connection::CheckTimeout() { - // If both read and write have timed out, then this connection can contribute - // no more to p2p socket unless at some later date readability were to come - // back. However, we gave readability a long time to timeout, so at this - // point, it seems fair to get rid of this connectoin. - if ((read_state_ == STATE_READ_TIMEOUT) && - (write_state_ == STATE_WRITE_TIMEOUT)) { - port_->thread()->Post(this, MSG_DELETE); - } -} - -void Connection::OnMessage(Message *pmsg) { - assert(pmsg->message_id == MSG_DELETE); - - LOG(INFO) << "Destroying connection: from " - << local_candidate().address().ToString() - << " to " << remote_candidate_.address().ToString(); - - SignalDestroyed(this); - delete this; -} - -size_t Connection::recv_bytes_second() { - // Snapshot bytes / second calculator - - uint32 current_time = Time(); - if (last_recv_bytes_second_time_ != (uint32)-1) { - int delta = TimeDiff(current_time, last_recv_bytes_second_time_); - if (delta >= 1000) { - int fraction_time = delta % 1000; - int seconds_time = delta - fraction_time; - int fraction_bytes = (int)(recv_total_bytes_ - last_recv_bytes_second_calc_) * fraction_time / delta; - recv_bytes_second_ = (recv_total_bytes_ - last_recv_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; - last_recv_bytes_second_time_ = current_time - fraction_time; - last_recv_bytes_second_calc_ = recv_total_bytes_ - fraction_bytes; - } - } - if (last_recv_bytes_second_time_ == (uint32)-1) { - last_recv_bytes_second_time_ = current_time; - last_recv_bytes_second_calc_ = recv_total_bytes_; - } - - return recv_bytes_second_; -} - -size_t Connection::recv_total_bytes() { - return recv_total_bytes_; -} - -size_t Connection::sent_bytes_second() { - // Snapshot bytes / second calculator - - uint32 current_time = Time(); - if (last_sent_bytes_second_time_ != (uint32)-1) { - int delta = TimeDiff(current_time, last_sent_bytes_second_time_); - if (delta >= 1000) { - int fraction_time = delta % 1000; - int seconds_time = delta - fraction_time; - int fraction_bytes = (int)(sent_total_bytes_ - last_sent_bytes_second_calc_) * fraction_time / delta; - sent_bytes_second_ = (sent_total_bytes_ - last_sent_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; - last_sent_bytes_second_time_ = current_time - fraction_time; - last_sent_bytes_second_calc_ = sent_total_bytes_ - fraction_bytes; - } - } - if (last_sent_bytes_second_time_ == (uint32)-1) { - last_sent_bytes_second_time_ = current_time; - last_sent_bytes_second_calc_ = sent_total_bytes_; - } - - return sent_bytes_second_; -} - -size_t Connection::sent_total_bytes() { - return sent_total_bytes_; -} - -ProxyConnection::ProxyConnection(Port* port, size_t index, const Candidate& candidate) - : Connection(port, index, candidate), error_(0) { -} - -int ProxyConnection::Send(const void* data, size_t size) { - if (write_state() != STATE_WRITABLE) { - error_ = EWOULDBLOCK; - return SOCKET_ERROR; - } - int sent = port_->SendTo(data, size, remote_candidate_.address(), true); - if (sent <= 0) { - assert(sent < 0); - error_ = port_->GetError(); - } else { - sent_total_bytes_ += sent; - } - return sent; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cpp new file mode 100644 index 00000000..14549b5b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cpp @@ -0,0 +1,869 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/base/asyncudpsocket.h" +#include "talk/base/asynctcpsocket.h" +#include "talk/base/socketadapters.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/helpers.h" +#include "talk/base/scoped_ptr.h" +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcmp; +} +#endif + +namespace { + +// The length of time we wait before timing out readability on a connection. +const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds + +// The length of time we wait before timing out writability on a connection. +const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds + +// The length of time we wait before we become unwritable. +const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds + +// The number of pings that must fail to respond before we become unwritable. +const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5; + +// This is the length of time that we wait for a ping response to come back. +const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds + +// Determines whether we have seen at least the given maximum number of +// pings fail to have a response. +inline bool TooManyFailures( + const std::vector& pings_since_last_response, + uint32 maximum_failures, + uint32 rtt_estimate, + uint32 now) { + + // If we haven't sent that many pings, then we can't have failed that many. + if (pings_since_last_response.size() < maximum_failures) + return false; + + // Check if the window in which we would expect a response to the ping has + // already elapsed. + return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now; +} + +// Determines whether we have gone too long without seeing any response. +inline bool TooLongWithoutResponse( + const std::vector& pings_since_last_response, + uint32 maximum_time, + uint32 now) { + + if (pings_since_last_response.size() == 0) + return false; + + return pings_since_last_response[0] + maximum_time < now; +} + +// We will restrict RTT estimates (when used for determining state) to be +// within a reasonable range. +const uint32 MINIMUM_RTT = 100; // 0.1 seconds +const uint32 MAXIMUM_RTT = 3000; // 3 seconds + +// When we don't have any RTT data, we have to pick something reasonable. We +// use a large value just in case the connection is really slow. +const uint32 DEFAULT_RTT = MAXIMUM_RTT; + +// Computes our estimate of the RTT given the current estimate and the number +// of data points on which it is based. +inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) { + if (rtt_data_points == 0) + return DEFAULT_RTT; + else + return cricket::_max(MINIMUM_RTT, cricket::_min(MAXIMUM_RTT, 2 * rtt)); +} + +// Weighting of the old rtt value to new data. +const int RTT_RATIO = 3; // 3 : 1 + +// The delay before we begin checking if this port is useless. +const int kPortTimeoutDelay = 30 * 1000; // 30 seconds + +const uint32 MSG_CHECKTIMEOUT = 1; +const uint32 MSG_DELETE = 1; + +} + +namespace cricket { + +static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" }; + +const char * ProtoToString(ProtocolType proto) { + return PROTO_NAMES[proto]; +} + +bool StringToProto(const char * value, ProtocolType& proto) { + for (size_t i=0; i<=PROTO_LAST; ++i) { + if (strcmp(PROTO_NAMES[i], value) == 0) { + proto = static_cast(i); + return true; + } + } + return false; +} + +ProxyInfo Port::proxy_; + +Port::Port(Thread* thread, const std::string& type, SocketFactory* factory, + Network* network) + : thread_(thread), factory_(factory), type_(type), network_(network), + preference_(-1), lifetime_(LT_PRESTART) { + + if (factory_ == NULL) + factory_ = thread_->socketserver(); + + set_username_fragment(CreateRandomString(16)); + set_password(CreateRandomString(16)); +} + +Port::~Port() { + // Delete all of the remaining connections. We copy the list up front + // because each deletion will cause it to be modified. + + std::vector list; + + AddressMap::iterator iter = connections_.begin(); + while (iter != connections_.end()) { + list.push_back(iter->second); + ++iter; + } + + for (uint32 i = 0; i < list.size(); i++) + delete list[i]; +} + +Connection* Port::GetConnection(const SocketAddress& remote_addr) { + AddressMap::const_iterator iter = connections_.find(remote_addr); + if (iter != connections_.end()) + return iter->second; + else + return NULL; +} + +void Port::set_username_fragment(const std::string& username_fragment) { + username_frag_ = username_fragment; +} + +void Port::set_password(const std::string& password) { + password_ = password; +} + +void Port::add_address(const SocketAddress& address, const std::string& protocol, bool final) { + Candidate c; + c.set_name(name_); + c.set_type(type_); + c.set_protocol(protocol); + c.set_address(address); + c.set_preference(preference_); + c.set_username(username_frag_); + c.set_password(password_); + c.set_network_name(network_->name()); + c.set_generation(generation_); + candidates_.push_back(c); + + if (final) + SignalAddressReady(this); +} + +void Port::AddConnection(Connection* conn) { + connections_[conn->remote_candidate().address()] = conn; + conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); + SignalConnectionCreated(this, conn); +} + +void Port::OnReadPacket( + const char* data, size_t size, const SocketAddress& addr) { + + // If this is an authenticated STUN request, then signal unknown address and + // send back a proper binding response. + StunMessage* msg; + std::string remote_username; + if (!GetStunMessage(data, size, addr, msg, remote_username)) { + LOG(LERROR) << "Received non-STUN packet from unknown address: " + << addr.ToString(); + } else if (!msg) { + // STUN message handled already + } else if (msg->type() == STUN_BINDING_REQUEST) { + SignalUnknownAddress(this, addr, msg, remote_username); + } else { + LOG(LERROR) << "Received unexpected STUN message type (" << msg->type() + << ") from unknown address: " << addr.ToString(); + delete msg; + } +} + +void Port::SendBindingRequest(Connection* conn) { + + // Construct the request message. + + StunMessage request; + request.SetType(STUN_BINDING_REQUEST); + request.SetTransactionID(CreateRandomString(16)); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + std::string username = conn->remote_candidate().username(); + username.append(username_frag_); + username_attr->CopyBytes(username.c_str(), (uint16)username.size()); + request.AddAttribute(username_attr); + + // Send the request message. + // NOTE: If we wanted to, this is where we would add the HMAC. + ByteBuffer buf; + request.Write(&buf); + SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false); +} + +bool Port::GetStunMessage(const char* data, size_t size, + const SocketAddress& addr, StunMessage *& msg, + std::string& remote_username) { + // NOTE: This could clearly be optimized to avoid allocating any memory. + // However, at the data rates we'll be looking at on the client side, + // this probably isn't worth worrying about. + + msg = 0; + + // Parse the request message. If the packet is not a complete and correct + // STUN message, then ignore it. + buzz::scoped_ptr stun_msg(new StunMessage()); + ByteBuffer buf(data, size); + if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { + return false; + } + + // The packet must include a username that either begins or ends with our + // fragment. It should begin with our fragment if it is a request and it + // should end with our fragment if it is a response. + const StunByteStringAttribute* username_attr = + stun_msg->GetByteString(STUN_ATTR_USERNAME); + + int remote_frag_len = (username_attr ? username_attr->length() : 0); + remote_frag_len -= static_cast(username_frag_.size()); + + if (stun_msg->type() == STUN_BINDING_REQUEST) { + if ((remote_frag_len < 0) + || (std::memcmp(username_attr->bytes(), + username_frag_.c_str(), username_frag_.size()) != 0)) { + LOG(LERROR) << "Received STUN request with bad username"; + SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, + STUN_ERROR_REASON_BAD_REQUEST); + return true; + } + + remote_username.assign(username_attr->bytes() + username_frag_.size(), + username_attr->bytes() + username_attr->length()); + } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) + || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { + if ((remote_frag_len < 0) + || (std::memcmp(username_attr->bytes() + remote_frag_len, + username_frag_.c_str(), username_frag_.size()) != 0)) { + LOG(LERROR) << "Received STUN response with bad username"; + // Do not send error response to a response + return true; + } + + remote_username.assign(username_attr->bytes(), + username_attr->bytes() + remote_frag_len); + + if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) { + if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) { + LOG(LERROR) << "Received STUN binding error:" + << " class=" << error_code->error_class() + << " number=" << error_code->number() + << " reason='" << error_code->reason() << "'"; + // Return message to allow error-specific processing + } else { + LOG(LERROR) << "Received STUN error response with no error code"; + // Drop corrupt message + return true; + } + } + } else { + LOG(LERROR) << "Received STUN packet with invalid type: " + << stun_msg->type(); + return true; + } + + // Return the STUN message found. + msg = stun_msg.release(); + return true; +} + +void Port::SendBindingResponse( + StunMessage* request, const SocketAddress& addr) { + + assert(request->type() == STUN_BINDING_REQUEST); + + // Retrieve the username from the request. + const StunByteStringAttribute* username_attr = + request->GetByteString(STUN_ATTR_USERNAME); + assert(username_attr); + + // Fill in the response message. + + StunMessage response; + response.SetType(STUN_BINDING_RESPONSE); + response.SetTransactionID(request->transaction_id()); + + StunByteStringAttribute* username2_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); + response.AddAttribute(username2_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetPort(addr.port()); + addr_attr->SetIP(addr.ip()); + response.AddAttribute(addr_attr); + + // Send the response message. + // NOTE: If we wanted to, this is where we would add the HMAC. + ByteBuffer buf; + response.Write(&buf); + SendTo(buf.Data(), buf.Length(), addr, false); + + // The fact that we received a successful request means that this connection + // (if one exists) should now be readable. + Connection* conn = GetConnection(addr); + assert(conn); + if (conn) + conn->ReceivedPing(); +} + +void Port::SendBindingErrorResponse( + StunMessage* request, const SocketAddress& addr, int error_code, + const std::string& reason) { + + assert(request->type() == STUN_BINDING_REQUEST); + + // Retrieve the username from the request. If it didn't have one, we + // shouldn't be responding at all. + const StunByteStringAttribute* username_attr = + request->GetByteString(STUN_ATTR_USERNAME); + assert(username_attr); + + // Fill in the response message. + + StunMessage response; + response.SetType(STUN_BINDING_ERROR_RESPONSE); + response.SetTransactionID(request->transaction_id()); + + StunByteStringAttribute* username2_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); + response.AddAttribute(username2_attr); + + StunErrorCodeAttribute* error_attr = StunAttribute::CreateErrorCode(); + error_attr->SetErrorCode(error_code); + error_attr->SetReason(reason); + response.AddAttribute(error_attr); + + // Send the response message. + // NOTE: If we wanted to, this is where we would add the HMAC. + ByteBuffer buf; + response.Write(&buf); + SendTo(buf.Data(), buf.Length(), addr, false); +} + +AsyncPacketSocket * Port::CreatePacketSocket(ProtocolType proto) { + if (proto == PROTO_UDP) { + return new AsyncUDPSocket(factory_->CreateAsyncSocket(SOCK_DGRAM)); + } else if ((proto == PROTO_TCP) || (proto == PROTO_SSLTCP)) { + AsyncSocket * socket = factory_->CreateAsyncSocket(SOCK_STREAM); + switch (proxy().type) { + case PROXY_NONE: + break; + case PROXY_SOCKS5: + socket = new AsyncSocksProxySocket(socket, proxy().address, proxy().username, proxy().password); + break; + case PROXY_HTTPS: + default: + socket = new AsyncHttpsProxySocket(socket, proxy().address, proxy().username, proxy().password); + break; + } + if (proto == PROTO_SSLTCP) { + socket = new AsyncSSLSocket(socket); + } + return new AsyncTCPSocket(socket); + } else { + LOG(INFO) << "Unknown protocol: " << proto; + return 0; + } +} + +void Port::OnMessage(Message *pmsg) { + assert(pmsg->message_id == MSG_CHECKTIMEOUT); + assert(lifetime_ == LT_PRETIMEOUT); + lifetime_ = LT_POSTTIMEOUT; + CheckTimeout(); +} + +void Port::Start() { + // The port sticks around for a minimum lifetime, after which + // we destroy it when it drops to zero connections. + if (lifetime_ == LT_PRESTART) { + lifetime_ = LT_PRETIMEOUT; + thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT); + } else { + LOG(WARNING) << "Port restart attempted"; + } +} + +void Port::OnConnectionDestroyed(Connection* conn) { + AddressMap::iterator iter = connections_.find(conn->remote_candidate().address()); + assert(iter != connections_.end()); + connections_.erase(iter); + + CheckTimeout(); +} + +void Port::CheckTimeout() { + // If this port has no connections, then there's no reason to keep it around. + // When the connections time out (both read and write), they will delete + // themselves, so if we have any connections, they are either readable or + // writable (or still connecting). + if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) { + LOG(INFO) << "Destroying port: " << name_ << "-" << type_; + SignalDestroyed(this); + delete this; + } +} + +// A ConnectionRequest is a simple STUN ping used to determine writability. +class ConnectionRequest : public StunRequest { +public: + ConnectionRequest(Connection* connection) : connection_(connection) { + } + + virtual ~ConnectionRequest() { + } + + virtual void Prepare(StunMessage* request) { + request->SetType(STUN_BINDING_REQUEST); + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + std::string username = connection_->remote_candidate().username(); + username.append(connection_->port()->username_fragment()); + username_attr->CopyBytes(username.c_str(), (uint16)username.size()); + request->AddAttribute(username_attr); + } + + virtual void OnResponse(StunMessage* response) { + connection_->OnConnectionRequestResponse(response, Elapsed()); + } + + virtual void OnErrorResponse(StunMessage* response) { + connection_->OnConnectionRequestErrorResponse(response, Elapsed()); + } + + virtual void OnTimeout() { + } + + virtual int GetNextDelay() { + // Each request is sent only once. After a single delay , the request will + // time out. + timeout_ = true; + return CONNECTION_RESPONSE_TIMEOUT; + } + +private: + Connection* connection_; +}; + +// +// Connection +// + +Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate) + : requests_(port->thread()), port_(port), local_candidate_index_(index), + remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT), + write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false), + rtt_(0), rtt_data_points_(0), last_ping_sent_(0), last_ping_received_(0), + recv_total_bytes_(0), recv_bytes_second_(0), + last_recv_bytes_second_time_((uint32)-1), last_recv_bytes_second_calc_(0), + sent_total_bytes_(0), sent_bytes_second_(0), + last_sent_bytes_second_time_((uint32)-1), last_sent_bytes_second_calc_(0) { + + // Wire up to send stun packets + requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket); +} + +Connection::~Connection() { +} + +const Candidate& Connection::local_candidate() const { + if (local_candidate_index_ < port_->candidates().size()) + return port_->candidates()[local_candidate_index_]; + assert(false); + static Candidate foo; + return foo; +} + +void Connection::set_read_state(ReadState value) { + ReadState old_value = read_state_; + read_state_ = value; + if (value != old_value) { + SignalStateChange(this); + CheckTimeout(); + } +} + +void Connection::set_write_state(WriteState value) { + WriteState old_value = write_state_; + write_state_ = value; + if (value != old_value) { + SignalStateChange(this); + CheckTimeout(); + } +} + +void Connection::set_connected(bool value) { + bool old_value = connected_; + connected_ = value; + + // When connectedness is turned off, this connection is done. + if (old_value && !value) + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::OnSendStunPacket(const void* data, size_t size) { + port_->SendTo(data, size, remote_candidate_.address(), false); +} + +void Connection::OnReadPacket(const char* data, size_t size) { + StunMessage* msg; + std::string remote_username; + const SocketAddress& addr(remote_candidate_.address()); + if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) { + // The packet did not parse as a valid STUN message + + // If this connection is readable, then pass along the packet. + if (read_state_ == STATE_READABLE) { + // readable means data from this address is acceptable + // Send it on! + + recv_total_bytes_ += size; + SignalReadPacket(this, data, size); + + // If timed out sending writability checks, start up again + if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) + set_write_state(STATE_WRITE_CONNECT); + } else { + // Not readable means the remote address hasn't send a valid + // binding request yet. + + LOG(WARNING) << "Received non-STUN packet from an unreadable connection."; + } + } else if (!msg) { + // The packet was STUN, but was already handled + } else if (remote_username != remote_candidate_.username()) { + // Not destined this connection + LOG(LERROR) << "Received STUN packet on wrong address."; + if (msg->type() == STUN_BINDING_REQUEST) { + port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST, + STUN_ERROR_REASON_BAD_REQUEST); + } + delete msg; + } else { + // The packet is STUN, with the current username + // If this is a STUN request, then update the readable bit and respond. + // If this is a STUN response, then update the writable bit. + + switch (msg->type()) { + case STUN_BINDING_REQUEST: + // Incoming, validated stun request from remote peer. + // This call will also set the connection readable. + + port_->SendBindingResponse(msg, addr); + + // If timed out sending writability checks, start up again + if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) + set_write_state(STATE_WRITE_CONNECT); + break; + + case STUN_BINDING_RESPONSE: + case STUN_BINDING_ERROR_RESPONSE: + // Response from remote peer. Does it match request sent? + // This doesn't just check, it makes callbacks if transaction + // id's match + requests_.CheckResponse(msg); + break; + + default: + assert(false); + break; + } + + // Done with the message; delete + + delete msg; + } +} + +void Connection::Prune() { + pruned_ = true; + requests_.Clear(); + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::Destroy() { + set_read_state(STATE_READ_TIMEOUT); + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::UpdateState(uint32 now) { + // Check the readable state. + // + // Since we don't know how many pings the other side has attempted, the best + // test we can do is a simple window. + + if ((read_state_ == STATE_READABLE) && + (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) { + set_read_state(STATE_READ_TIMEOUT); + } + + // Check the writable state. (The order of these checks is important.) + // + // Before becoming unwritable, we allow for a fixed number of pings to fail + // (i.e., receive no response). We also have to give the response time to + // get back, so we include a conservative estimate of this. + // + // Before timing out writability, we give a fixed amount of time. This is to + // allow for changes in network conditions. + + uint32 rtt = ConservativeRTTEstimate(rtt_, rtt_data_points_); + + if ((write_state_ == STATE_WRITABLE) && + TooManyFailures(pings_since_last_response_, + CONNECTION_WRITE_CONNECT_FAILURES, + rtt, + now) && + TooLongWithoutResponse(pings_since_last_response_, + CONNECTION_WRITE_CONNECT_TIMEOUT, + now)) { + set_write_state(STATE_WRITE_CONNECT); + } + + if ((write_state_ == STATE_WRITE_CONNECT) && + TooLongWithoutResponse(pings_since_last_response_, + CONNECTION_WRITE_TIMEOUT, + now)) { + set_write_state(STATE_WRITE_TIMEOUT); + } +} + +void Connection::Ping(uint32 now) { + assert(connected_); + last_ping_sent_ = now; + pings_since_last_response_.push_back(now); + requests_.Send(new ConnectionRequest(this)); +} + +void Connection::ReceivedPing() { + last_ping_received_ = Time(); + set_read_state(STATE_READABLE); +} + +void Connection::OnConnectionRequestResponse(StunMessage *response, uint32 rtt) { + // We have a potentially valid reply from the remote address. + // The packet must include a username that ends with our fragment, + // since it is a response. + + // Check exact message type + bool valid = true; + if (response->type() != STUN_BINDING_RESPONSE) + valid = false; + + // Must have username attribute + const StunByteStringAttribute* username_attr = + response->GetByteString(STUN_ATTR_USERNAME); + if (valid) { + if (!username_attr) { + LOG(LERROR) << "Received likely STUN packet with no username"; + valid = false; + } + } + + // Length must be at least the size of our fragment (actually, should + // be bigger since our fragment is at the end!) + if (valid) { + if (username_attr->length() <= port_->username_fragment().size()) { + LOG(LERROR) << "Received likely STUN packet with short username"; + valid = false; + } + } + + // Compare our fragment with the end of the username - must be exact match + if (valid) { + std::string username_fragment = port_->username_fragment(); + int offset = (int)(username_attr->length() - username_fragment.size()); + if (std::memcmp(username_attr->bytes() + offset, + username_fragment.c_str(), username_fragment.size()) != 0) { + LOG(LERROR) << "Received STUN response with bad username"; + valid = false; + } + } + + if (valid) { + // Valid response. If we're not already, become writable. We may be + // bringing a pruned connection back to life, but if we don't really want + // it, we can always prune it again. + set_write_state(STATE_WRITABLE); + + pings_since_last_response_.clear(); + rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); + rtt_data_points_ += 1; + } +} + +void Connection::OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt) { + const StunErrorCodeAttribute* error = response->GetErrorCode(); + uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE; + + if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE) + || (error_code == STUN_ERROR_SERVER_ERROR) + || (error_code == STUN_ERROR_UNAUTHORIZED)) { + // Recoverable error, retry + } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) { + // Race failure, retry + } else { + // This is not a valid connection. + set_connected(false); + } +} + +void Connection::CheckTimeout() { + // If both read and write have timed out, then this connection can contribute + // no more to p2p socket unless at some later date readability were to come + // back. However, we gave readability a long time to timeout, so at this + // point, it seems fair to get rid of this connectoin. + if ((read_state_ == STATE_READ_TIMEOUT) && + (write_state_ == STATE_WRITE_TIMEOUT)) { + port_->thread()->Post(this, MSG_DELETE); + } +} + +void Connection::OnMessage(Message *pmsg) { + assert(pmsg->message_id == MSG_DELETE); + + LOG(INFO) << "Destroying connection: from " + << local_candidate().address().ToString() + << " to " << remote_candidate_.address().ToString(); + + SignalDestroyed(this); + delete this; +} + +size_t Connection::recv_bytes_second() { + // Snapshot bytes / second calculator + + uint32 current_time = Time(); + if (last_recv_bytes_second_time_ != (uint32)-1) { + int delta = TimeDiff(current_time, last_recv_bytes_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds_time = delta - fraction_time; + int fraction_bytes = (int)(recv_total_bytes_ - last_recv_bytes_second_calc_) * fraction_time / delta; + recv_bytes_second_ = (recv_total_bytes_ - last_recv_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; + last_recv_bytes_second_time_ = current_time - fraction_time; + last_recv_bytes_second_calc_ = recv_total_bytes_ - fraction_bytes; + } + } + if (last_recv_bytes_second_time_ == (uint32)-1) { + last_recv_bytes_second_time_ = current_time; + last_recv_bytes_second_calc_ = recv_total_bytes_; + } + + return recv_bytes_second_; +} + +size_t Connection::recv_total_bytes() { + return recv_total_bytes_; +} + +size_t Connection::sent_bytes_second() { + // Snapshot bytes / second calculator + + uint32 current_time = Time(); + if (last_sent_bytes_second_time_ != (uint32)-1) { + int delta = TimeDiff(current_time, last_sent_bytes_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds_time = delta - fraction_time; + int fraction_bytes = (int)(sent_total_bytes_ - last_sent_bytes_second_calc_) * fraction_time / delta; + sent_bytes_second_ = (sent_total_bytes_ - last_sent_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; + last_sent_bytes_second_time_ = current_time - fraction_time; + last_sent_bytes_second_calc_ = sent_total_bytes_ - fraction_bytes; + } + } + if (last_sent_bytes_second_time_ == (uint32)-1) { + last_sent_bytes_second_time_ = current_time; + last_sent_bytes_second_calc_ = sent_total_bytes_; + } + + return sent_bytes_second_; +} + +size_t Connection::sent_total_bytes() { + return sent_total_bytes_; +} + +ProxyConnection::ProxyConnection(Port* port, size_t index, const Candidate& candidate) + : Connection(port, index, candidate), error_(0) { +} + +int ProxyConnection::Send(const void* data, size_t size) { + if (write_state() != STATE_WRITABLE) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = port_->SendTo(data, size, remote_candidate_.address(), true); + if (sent <= 0) { + assert(sent < 0); + error_ = port_->GetError(); + } else { + sent_total_bytes_ += sent; + } + return sent; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc deleted file mode 100644 index 4ba12be3..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc +++ /dev/null @@ -1,640 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/logging.h" -#include "talk/base/asynctcpsocket.h" -#include "talk/p2p/base/relayport.h" -#include "talk/p2p/base/helpers.h" -#include -#include -#ifdef OSX -#include -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -const int KEEPALIVE_DELAY = 10 * 60 * 1000; -const int RETRY_DELAY = 50; // 50ms, from ICE spec -const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs - -const uint32 MSG_DISPOSE_SOCKET = 100; // needs to be more than ID used by Port -typedef TypedMessageData DisposeSocketData; - -class AsyncTCPSocket; - -// Manages a single connection to the relayserver. We aim to use each -// connection for only a specific destination address so that we can avoid -// wrapping every packet in a STUN send / data indication. -class RelayEntry : public sigslot::has_slots<> { -public: - RelayEntry(RelayPort* port, const SocketAddress& ext_addr, const SocketAddress& local_addr); - ~RelayEntry(); - - RelayPort* port() { return port_; } - - const SocketAddress& address() { return ext_addr_; } - void set_address(const SocketAddress& addr) { ext_addr_ = addr; } - - AsyncPacketSocket* socket() { return socket_; } - - bool connected() { return connected_; } - void set_connected(bool connected) { connected_ = connected; } - - bool locked() { return locked_; } - - // Returns the last error on the socket of this entry. - int GetError() { return socket_->GetError(); } - - // Sends the STUN requests to the server to initiate this connection. - void Connect(); - - // Called when this entry becomes connected. The address given is the one - // exposed to the outside world on the relay server. - void OnConnect(const SocketAddress& mapped_addr); - - // Sends a packet to the given destination address using the socket of this - // entry. This will wrap the packet in STUN if necessary. - int SendTo(const void* data, size_t size, const SocketAddress& addr); - - // Schedules a keep-alive allocate request. - void ScheduleKeepAlive(); - - void SetServerIndex(size_t sindex) { server_index_ = sindex; } - size_t ServerIndex() const { return server_index_; } - - // Try a different server address - void HandleConnectFailure(); - -private: - RelayPort* port_; - SocketAddress ext_addr_, local_addr_; - size_t server_index_; - AsyncPacketSocket* socket_; - bool connected_; - bool locked_; - StunRequestManager requests_; - - // Called when a TCP connection is established or fails - void OnSocketConnect(AsyncTCPSocket* socket); - void OnSocketClose(AsyncTCPSocket* socket, int error); - - // Called when a packet is received on this socket. - void OnReadPacket( - const char* data, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket); - - // Called on behalf of a StunRequest to write data to the socket. This is - // already STUN intended for the server, so no wrapping is necessary. - void OnSendPacket(const void* data, size_t size); - - // Sends the given data on the socket to the server with no wrapping. This - // returns the number of bytes written or -1 if an error occurred. - int SendPacket(const void* data, size_t size); -}; - -// Handles an allocate request for a particular RelayEntry. -class AllocateRequest : public StunRequest { -public: - AllocateRequest(RelayEntry* entry); - virtual ~AllocateRequest() {} - - virtual void Prepare(StunMessage* request); - - virtual int GetNextDelay(); - - virtual void OnResponse(StunMessage* response); - virtual void OnErrorResponse(StunMessage* response); - virtual void OnTimeout(); - -private: - RelayEntry* entry_; - uint32 start_time_; -}; - -const std::string RELAY_PORT_TYPE("relay"); - -RelayPort::RelayPort( - Thread* thread, SocketFactory* factory, Network* network, - const SocketAddress& local_addr, const std::string& username, - const std::string& password, const std::string& magic_cookie) - : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr), - ready_(false), magic_cookie_(magic_cookie), error_(0) { - - entries_.push_back(new RelayEntry(this, SocketAddress(), local_addr_)); - - set_username_fragment(username); - set_password(password); - - if (magic_cookie_.size() == 0) - magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4); -} - -RelayPort::~RelayPort() { - for (unsigned i = 0; i < entries_.size(); ++i) - delete entries_[i]; - thread_->Clear(this); -} - -void RelayPort::AddServerAddress(const ProtocolAddress& addr) { - // Since HTTP proxies usually only allow 443, let's up the priority on PROTO_SSLTCP - if ((addr.proto == PROTO_SSLTCP) - && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) { - server_addr_.push_front(addr); - } else { - server_addr_.push_back(addr); - } -} - -void RelayPort::AddExternalAddress(const ProtocolAddress& addr) { - std::string proto_name = ProtoToString(addr.proto); - for (std::vector::const_iterator it = candidates().begin(); it != candidates().end(); ++it) { - if ((it->address() == addr.address) && (it->protocol() == proto_name)) { - LOG(INFO) << "Redundant relay address: " << proto_name << " @ " << addr.address.ToString(); - return; - } - } - add_address(addr.address, proto_name, false); -} - -void RelayPort::SetReady() { - if (!ready_) { - ready_ = true; - SignalAddressReady(this); - } -} - -const ProtocolAddress * RelayPort::ServerAddress(size_t index) const { - if ((index >= 0) && (index < server_addr_.size())) - return &server_addr_[index]; - return 0; -} - -bool RelayPort::HasMagicCookie(const char* data, size_t size) { - if (size < 24 + magic_cookie_.size()) { - return false; - } else { - return 0 == std::memcmp(data + 24, - magic_cookie_.c_str(), - magic_cookie_.size()); - } -} - -void RelayPort::PrepareAddress() { - // We initiate a connect on the first entry. If this completes, it will fill - // in the server address as the address of this port. - assert(entries_.size() == 1); - entries_[0]->Connect(); - ready_ = false; -} - -Connection* RelayPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { - // We only create connections to non-udp sockets if they are incoming on this port - if ((address.protocol() != "udp") && (origin != ORIGIN_THIS_PORT)) - return 0; - - // We don't support loopback on relays - if (address.type() == type()) - return 0; - - size_t index = 0; - for (size_t i = 0; i < candidates().size(); ++i) { - const Candidate& local = candidates()[i]; - if (local.protocol() == address.protocol()) { - index = i; - break; - } - } - - Connection * conn = new ProxyConnection(this, index, address); - AddConnection(conn); - return conn; -} - -int RelayPort::SendTo(const void* data, - size_t size, - const SocketAddress& addr, bool payload) { - - // Try to find an entry for this specific address. Note that the first entry - // created was not given an address initially, so it can be set to the first - // address that comes along. - - RelayEntry* entry = 0; - - for (unsigned i = 0; i < entries_.size(); ++i) { - if (entries_[i]->address().IsAny() && payload) { - entry = entries_[i]; - entry->set_address(addr); - break; - } else if (entries_[i]->address() == addr) { - entry = entries_[i]; - break; - } - } - - // If we did not find one, then we make a new one. This will not be useable - // until it becomes connected, however. - if (!entry && payload) { - entry = new RelayEntry(this, addr, local_addr_); - if (!entries_.empty()) { - // Use the same port to connect to relay server - entry->SetServerIndex(entries_[0]->ServerIndex()); - } - entry->Connect(); - entries_.push_back(entry); - } - - // If the entry is connected, then we can send on it (though wrapping may - // still be necessary). Otherwise, we can't yet use this connection, so we - // default to the first one. - if (!entry || !entry->connected()) { - assert(!entries_.empty()); - entry = entries_[0]; - if (!entry->connected()) { - error_ = EWOULDBLOCK; - return SOCKET_ERROR; - } - } - - // Send the actual contents to the server using the usual mechanism. - int sent = entry->SendTo(data, size, addr); - if (sent <= 0) { - assert(sent < 0); - error_ = entry->GetError(); - return SOCKET_ERROR; - } - - // The caller of the function is expecting the number of user data bytes, - // rather than the size of the packet. - return (int)size; -} - -void RelayPort::OnMessage(Message *pmsg) { - switch (pmsg->message_id) { - case MSG_DISPOSE_SOCKET: { - DisposeSocketData * data = static_cast(pmsg->pdata); - delete data->data(); - delete data; - break; } - default: - Port::OnMessage(pmsg); - } -} - -int RelayPort::SetOption(Socket::Option opt, int value) { - int result = 0; - for (unsigned i = 0; i < entries_.size(); ++i) { - if (entries_[i]->socket()->SetOption(opt, value) < 0) { - result = -1; - error_ = entries_[i]->socket()->GetError(); - } - } - options_.push_back(OptionValue(opt, value)); - return result; -} - -int RelayPort::GetError() { - return error_; -} - -void RelayPort::OnReadPacket( - const char* data, size_t size, const SocketAddress& remote_addr) { - if (Connection* conn = GetConnection(remote_addr)) { - conn->OnReadPacket(data, size); - } else { - Port::OnReadPacket(data, size, remote_addr); - } -} - -void RelayPort::DisposeSocket(AsyncPacketSocket * socket) { - thread_->Post(this, MSG_DISPOSE_SOCKET, new DisposeSocketData(socket)); -} - -RelayEntry::RelayEntry(RelayPort* port, const SocketAddress& ext_addr, - const SocketAddress& local_addr) - : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), server_index_(0), - socket_(0), connected_(false), locked_(false), requests_(port->thread()) { - - requests_.SignalSendPacket.connect(this, &RelayEntry::OnSendPacket); -} - -RelayEntry::~RelayEntry() { - delete socket_; -} - -void RelayEntry::Connect() { - assert(socket_ == 0); - const ProtocolAddress * ra = port()->ServerAddress(server_index_); - if (!ra) { - LOG(INFO) << "Out of relay server connections"; - return; - } - - LOG(INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString(); - - socket_ = port_->CreatePacketSocket(ra->proto); - assert(socket_ != 0); - - socket_->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket); - if (socket_->Bind(local_addr_) < 0) - LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); - - for (unsigned i = 0; i < port_->options().size(); ++i) - socket_->SetOption(port_->options()[i].first, port_->options()[i].second); - - if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) { - AsyncTCPSocket * tcp = static_cast(socket_); - tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose); - tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect); - tcp->Connect(ra->address); - } else { - requests_.Send(new AllocateRequest(this)); - } -} - -void RelayEntry::OnConnect(const SocketAddress& mapped_addr) { - ProtocolType proto = PROTO_UDP; - LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto) << " @ " << mapped_addr.ToString(); - connected_ = true; - - port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto)); - port_->SetReady(); -} - -int RelayEntry::SendTo(const void* data, - size_t size, - const SocketAddress& addr) { - - // If this connection is locked to the address given, then we can send the - // packet with no wrapper. - if (locked_ && (ext_addr_ == addr)) - return SendPacket(data, size); - - // Otherwise, we must wrap the given data in a STUN SEND request so that we - // can communicate the destination address to the server. - // - // Note that we do not use a StunRequest here. This is because there is - // likely no reason to resend this packet. If it is late, we just drop it. - // The next send to this address will try again. - - StunMessage request; - request.SetType(STUN_SEND_REQUEST); - request.SetTransactionID(CreateRandomString(16)); - - StunByteStringAttribute* magic_cookie_attr = - StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); - magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(), - (uint16)port_->magic_cookie().size()); - request.AddAttribute(magic_cookie_attr); - - StunByteStringAttribute* username_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - username_attr->CopyBytes(port_->username_fragment().c_str(), - (uint16)port_->username_fragment().size()); - request.AddAttribute(username_attr); - - StunAddressAttribute* addr_attr = - StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS); - addr_attr->SetFamily(1); - addr_attr->SetIP(addr.ip()); - addr_attr->SetPort(addr.port()); - request.AddAttribute(addr_attr); - - // Attempt to lock - if (ext_addr_ == addr) { - StunUInt32Attribute* options_attr = - StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS); - options_attr->SetValue(0x1); - request.AddAttribute(options_attr); - } - - StunByteStringAttribute* data_attr = - StunAttribute::CreateByteString(STUN_ATTR_DATA); - data_attr->CopyBytes(data, (uint16)size); - request.AddAttribute(data_attr); - - // TODO: compute the HMAC. - - ByteBuffer buf; - request.Write(&buf); - - return SendPacket(buf.Data(), buf.Length()); -} - -void RelayEntry::ScheduleKeepAlive() { - requests_.SendDelayed(new AllocateRequest(this), KEEPALIVE_DELAY); -} - -void RelayEntry::HandleConnectFailure() { - //if (GetMillisecondCount() - start_time_ > RETRY_TIMEOUT) - // return; - //ScheduleKeepAlive(); - - connected_ = false; - port()->DisposeSocket(socket_); - socket_ = 0; - server_index_ += 1; - Connect(); -} - -void RelayEntry::OnSocketConnect(AsyncTCPSocket* socket) { - assert(socket == socket_); - LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString(); - requests_.Send(new AllocateRequest(this)); -} - -void RelayEntry::OnSocketClose(AsyncTCPSocket* socket, int error) { - assert(socket == socket_); - PLOG(LERROR, error) << "relay tcp connect failed"; - HandleConnectFailure(); -} - -void RelayEntry::OnReadPacket(const char* data, - size_t size, - const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - assert(socket == socket_); - //assert(remote_addr == port_->server_addr()); TODO: are we worried about this? - - // If the magic cookie is not present, then this is an unwrapped packet sent - // by the server, The actual remote address is the one we recorded. - if (!port_->HasMagicCookie(data, size)) { - if (locked_) { - port_->OnReadPacket(data, size, ext_addr_); - } else { - LOG(WARNING) << "Dropping packet: entry not locked"; - } - return; - } - - ByteBuffer buf(data, size); - StunMessage msg; - if (!msg.Read(&buf)) { - LOG(INFO) << "Incoming packet was not STUN"; - return; - } - - // The incoming packet should be a STUN ALLOCATE response, SEND response, or - // DATA indication. - if (requests_.CheckResponse(&msg)) { - return; - } else if (msg.type() == STUN_SEND_RESPONSE) { - if (const StunUInt32Attribute* options_attr = msg.GetUInt32(STUN_ATTR_OPTIONS)) { - if (options_attr->value() & 0x1) { - locked_ = true; - } - } - return; - } else if (msg.type() != STUN_DATA_INDICATION) { - LOG(INFO) << "Received BAD stun type from server: " << msg.type() - ; - return; - } - - // This must be a data indication. - - const StunAddressAttribute* addr_attr = - msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2); - if (!addr_attr) { - LOG(INFO) << "Data indication has no source address"; - return; - } else if (addr_attr->family() != 1) { - LOG(INFO) << "Source address has bad family"; - return; - } - - SocketAddress remote_addr2(addr_attr->ip(), addr_attr->port()); - - const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA); - if (!data_attr) { - LOG(INFO) << "Data indication has no data"; - return; - } - - // Process the actual data and remote address in the normal manner. - port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2); -} - -void RelayEntry::OnSendPacket(const void* data, size_t size) { - SendPacket(data, size); -} - -int RelayEntry::SendPacket(const void* data, size_t size) { - const ProtocolAddress * ra = port_->ServerAddress(server_index_); - if (!ra) { - socket_->SetError(ENOTCONN); - return SOCKET_ERROR; - } - int sent = socket_->SendTo(data, size, ra->address); - if (sent <= 0) { - LOG(LS_VERBOSE) << "sendto: " << std::strerror(socket_->GetError()); - assert(sent < 0); - } - return sent; -} - -AllocateRequest::AllocateRequest(RelayEntry* entry) : entry_(entry) { - start_time_ = GetMillisecondCount(); -} - -void AllocateRequest::Prepare(StunMessage* request) { - request->SetType(STUN_ALLOCATE_REQUEST); - - StunByteStringAttribute* magic_cookie_attr = - StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); - magic_cookie_attr->CopyBytes( - entry_->port()->magic_cookie().c_str(), - (uint16)entry_->port()->magic_cookie().size()); - request->AddAttribute(magic_cookie_attr); - - StunByteStringAttribute* username_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - username_attr->CopyBytes( - entry_->port()->username_fragment().c_str(), - (uint16)entry_->port()->username_fragment().size()); - request->AddAttribute(username_attr); -} - -int AllocateRequest::GetNextDelay() { - int delay = 100 * _max(1 << count_, 2); - count_ += 1; - if (count_ == 5) - timeout_ = true; - return delay; -} - -void AllocateRequest::OnResponse(StunMessage* response) { - const StunAddressAttribute* addr_attr = - response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); - if (!addr_attr) { - LOG(INFO) << "Allocate response missing mapped address."; - } else if (addr_attr->family() != 1) { - LOG(INFO) << "Mapped address has bad family"; - } else { - SocketAddress addr(addr_attr->ip(), addr_attr->port()); - entry_->OnConnect(addr); - } - - // We will do a keep-alive regardless of whether this request suceeds. - // This should have almost no impact on network usage. - entry_->ScheduleKeepAlive(); -} - -void AllocateRequest::OnErrorResponse(StunMessage* response) { - const StunErrorCodeAttribute* attr = response->GetErrorCode(); - if (!attr) { - LOG(INFO) << "Bad allocate response error code"; - } else { - LOG(INFO) << "Allocate error response:" - << " code=" << static_cast(attr->error_code()) - << " reason='" << attr->reason() << "'"; - } - - if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) - entry_->ScheduleKeepAlive(); -} - -void AllocateRequest::OnTimeout() { - LOG(INFO) << "Allocate request timed out"; - entry_->HandleConnectFailure(); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cpp new file mode 100644 index 00000000..4ba12be3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cpp @@ -0,0 +1,640 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/base/asynctcpsocket.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/helpers.h" +#include +#include +#ifdef OSX +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +const int KEEPALIVE_DELAY = 10 * 60 * 1000; +const int RETRY_DELAY = 50; // 50ms, from ICE spec +const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs + +const uint32 MSG_DISPOSE_SOCKET = 100; // needs to be more than ID used by Port +typedef TypedMessageData DisposeSocketData; + +class AsyncTCPSocket; + +// Manages a single connection to the relayserver. We aim to use each +// connection for only a specific destination address so that we can avoid +// wrapping every packet in a STUN send / data indication. +class RelayEntry : public sigslot::has_slots<> { +public: + RelayEntry(RelayPort* port, const SocketAddress& ext_addr, const SocketAddress& local_addr); + ~RelayEntry(); + + RelayPort* port() { return port_; } + + const SocketAddress& address() { return ext_addr_; } + void set_address(const SocketAddress& addr) { ext_addr_ = addr; } + + AsyncPacketSocket* socket() { return socket_; } + + bool connected() { return connected_; } + void set_connected(bool connected) { connected_ = connected; } + + bool locked() { return locked_; } + + // Returns the last error on the socket of this entry. + int GetError() { return socket_->GetError(); } + + // Sends the STUN requests to the server to initiate this connection. + void Connect(); + + // Called when this entry becomes connected. The address given is the one + // exposed to the outside world on the relay server. + void OnConnect(const SocketAddress& mapped_addr); + + // Sends a packet to the given destination address using the socket of this + // entry. This will wrap the packet in STUN if necessary. + int SendTo(const void* data, size_t size, const SocketAddress& addr); + + // Schedules a keep-alive allocate request. + void ScheduleKeepAlive(); + + void SetServerIndex(size_t sindex) { server_index_ = sindex; } + size_t ServerIndex() const { return server_index_; } + + // Try a different server address + void HandleConnectFailure(); + +private: + RelayPort* port_; + SocketAddress ext_addr_, local_addr_; + size_t server_index_; + AsyncPacketSocket* socket_; + bool connected_; + bool locked_; + StunRequestManager requests_; + + // Called when a TCP connection is established or fails + void OnSocketConnect(AsyncTCPSocket* socket); + void OnSocketClose(AsyncTCPSocket* socket, int error); + + // Called when a packet is received on this socket. + void OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + + // Called on behalf of a StunRequest to write data to the socket. This is + // already STUN intended for the server, so no wrapping is necessary. + void OnSendPacket(const void* data, size_t size); + + // Sends the given data on the socket to the server with no wrapping. This + // returns the number of bytes written or -1 if an error occurred. + int SendPacket(const void* data, size_t size); +}; + +// Handles an allocate request for a particular RelayEntry. +class AllocateRequest : public StunRequest { +public: + AllocateRequest(RelayEntry* entry); + virtual ~AllocateRequest() {} + + virtual void Prepare(StunMessage* request); + + virtual int GetNextDelay(); + + virtual void OnResponse(StunMessage* response); + virtual void OnErrorResponse(StunMessage* response); + virtual void OnTimeout(); + +private: + RelayEntry* entry_; + uint32 start_time_; +}; + +const std::string RELAY_PORT_TYPE("relay"); + +RelayPort::RelayPort( + Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& local_addr, const std::string& username, + const std::string& password, const std::string& magic_cookie) + : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr), + ready_(false), magic_cookie_(magic_cookie), error_(0) { + + entries_.push_back(new RelayEntry(this, SocketAddress(), local_addr_)); + + set_username_fragment(username); + set_password(password); + + if (magic_cookie_.size() == 0) + magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4); +} + +RelayPort::~RelayPort() { + for (unsigned i = 0; i < entries_.size(); ++i) + delete entries_[i]; + thread_->Clear(this); +} + +void RelayPort::AddServerAddress(const ProtocolAddress& addr) { + // Since HTTP proxies usually only allow 443, let's up the priority on PROTO_SSLTCP + if ((addr.proto == PROTO_SSLTCP) + && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) { + server_addr_.push_front(addr); + } else { + server_addr_.push_back(addr); + } +} + +void RelayPort::AddExternalAddress(const ProtocolAddress& addr) { + std::string proto_name = ProtoToString(addr.proto); + for (std::vector::const_iterator it = candidates().begin(); it != candidates().end(); ++it) { + if ((it->address() == addr.address) && (it->protocol() == proto_name)) { + LOG(INFO) << "Redundant relay address: " << proto_name << " @ " << addr.address.ToString(); + return; + } + } + add_address(addr.address, proto_name, false); +} + +void RelayPort::SetReady() { + if (!ready_) { + ready_ = true; + SignalAddressReady(this); + } +} + +const ProtocolAddress * RelayPort::ServerAddress(size_t index) const { + if ((index >= 0) && (index < server_addr_.size())) + return &server_addr_[index]; + return 0; +} + +bool RelayPort::HasMagicCookie(const char* data, size_t size) { + if (size < 24 + magic_cookie_.size()) { + return false; + } else { + return 0 == std::memcmp(data + 24, + magic_cookie_.c_str(), + magic_cookie_.size()); + } +} + +void RelayPort::PrepareAddress() { + // We initiate a connect on the first entry. If this completes, it will fill + // in the server address as the address of this port. + assert(entries_.size() == 1); + entries_[0]->Connect(); + ready_ = false; +} + +Connection* RelayPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + // We only create connections to non-udp sockets if they are incoming on this port + if ((address.protocol() != "udp") && (origin != ORIGIN_THIS_PORT)) + return 0; + + // We don't support loopback on relays + if (address.type() == type()) + return 0; + + size_t index = 0; + for (size_t i = 0; i < candidates().size(); ++i) { + const Candidate& local = candidates()[i]; + if (local.protocol() == address.protocol()) { + index = i; + break; + } + } + + Connection * conn = new ProxyConnection(this, index, address); + AddConnection(conn); + return conn; +} + +int RelayPort::SendTo(const void* data, + size_t size, + const SocketAddress& addr, bool payload) { + + // Try to find an entry for this specific address. Note that the first entry + // created was not given an address initially, so it can be set to the first + // address that comes along. + + RelayEntry* entry = 0; + + for (unsigned i = 0; i < entries_.size(); ++i) { + if (entries_[i]->address().IsAny() && payload) { + entry = entries_[i]; + entry->set_address(addr); + break; + } else if (entries_[i]->address() == addr) { + entry = entries_[i]; + break; + } + } + + // If we did not find one, then we make a new one. This will not be useable + // until it becomes connected, however. + if (!entry && payload) { + entry = new RelayEntry(this, addr, local_addr_); + if (!entries_.empty()) { + // Use the same port to connect to relay server + entry->SetServerIndex(entries_[0]->ServerIndex()); + } + entry->Connect(); + entries_.push_back(entry); + } + + // If the entry is connected, then we can send on it (though wrapping may + // still be necessary). Otherwise, we can't yet use this connection, so we + // default to the first one. + if (!entry || !entry->connected()) { + assert(!entries_.empty()); + entry = entries_[0]; + if (!entry->connected()) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + } + + // Send the actual contents to the server using the usual mechanism. + int sent = entry->SendTo(data, size, addr); + if (sent <= 0) { + assert(sent < 0); + error_ = entry->GetError(); + return SOCKET_ERROR; + } + + // The caller of the function is expecting the number of user data bytes, + // rather than the size of the packet. + return (int)size; +} + +void RelayPort::OnMessage(Message *pmsg) { + switch (pmsg->message_id) { + case MSG_DISPOSE_SOCKET: { + DisposeSocketData * data = static_cast(pmsg->pdata); + delete data->data(); + delete data; + break; } + default: + Port::OnMessage(pmsg); + } +} + +int RelayPort::SetOption(Socket::Option opt, int value) { + int result = 0; + for (unsigned i = 0; i < entries_.size(); ++i) { + if (entries_[i]->socket()->SetOption(opt, value) < 0) { + result = -1; + error_ = entries_[i]->socket()->GetError(); + } + } + options_.push_back(OptionValue(opt, value)); + return result; +} + +int RelayPort::GetError() { + return error_; +} + +void RelayPort::OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr) { + if (Connection* conn = GetConnection(remote_addr)) { + conn->OnReadPacket(data, size); + } else { + Port::OnReadPacket(data, size, remote_addr); + } +} + +void RelayPort::DisposeSocket(AsyncPacketSocket * socket) { + thread_->Post(this, MSG_DISPOSE_SOCKET, new DisposeSocketData(socket)); +} + +RelayEntry::RelayEntry(RelayPort* port, const SocketAddress& ext_addr, + const SocketAddress& local_addr) + : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), server_index_(0), + socket_(0), connected_(false), locked_(false), requests_(port->thread()) { + + requests_.SignalSendPacket.connect(this, &RelayEntry::OnSendPacket); +} + +RelayEntry::~RelayEntry() { + delete socket_; +} + +void RelayEntry::Connect() { + assert(socket_ == 0); + const ProtocolAddress * ra = port()->ServerAddress(server_index_); + if (!ra) { + LOG(INFO) << "Out of relay server connections"; + return; + } + + LOG(INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString(); + + socket_ = port_->CreatePacketSocket(ra->proto); + assert(socket_ != 0); + + socket_->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket); + if (socket_->Bind(local_addr_) < 0) + LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); + + for (unsigned i = 0; i < port_->options().size(); ++i) + socket_->SetOption(port_->options()[i].first, port_->options()[i].second); + + if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) { + AsyncTCPSocket * tcp = static_cast(socket_); + tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose); + tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect); + tcp->Connect(ra->address); + } else { + requests_.Send(new AllocateRequest(this)); + } +} + +void RelayEntry::OnConnect(const SocketAddress& mapped_addr) { + ProtocolType proto = PROTO_UDP; + LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto) << " @ " << mapped_addr.ToString(); + connected_ = true; + + port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto)); + port_->SetReady(); +} + +int RelayEntry::SendTo(const void* data, + size_t size, + const SocketAddress& addr) { + + // If this connection is locked to the address given, then we can send the + // packet with no wrapper. + if (locked_ && (ext_addr_ == addr)) + return SendPacket(data, size); + + // Otherwise, we must wrap the given data in a STUN SEND request so that we + // can communicate the destination address to the server. + // + // Note that we do not use a StunRequest here. This is because there is + // likely no reason to resend this packet. If it is late, we just drop it. + // The next send to this address will try again. + + StunMessage request; + request.SetType(STUN_SEND_REQUEST); + request.SetTransactionID(CreateRandomString(16)); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(), + (uint16)port_->magic_cookie().size()); + request.AddAttribute(magic_cookie_attr); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username_attr->CopyBytes(port_->username_fragment().c_str(), + (uint16)port_->username_fragment().size()); + request.AddAttribute(username_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetIP(addr.ip()); + addr_attr->SetPort(addr.port()); + request.AddAttribute(addr_attr); + + // Attempt to lock + if (ext_addr_ == addr) { + StunUInt32Attribute* options_attr = + StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS); + options_attr->SetValue(0x1); + request.AddAttribute(options_attr); + } + + StunByteStringAttribute* data_attr = + StunAttribute::CreateByteString(STUN_ATTR_DATA); + data_attr->CopyBytes(data, (uint16)size); + request.AddAttribute(data_attr); + + // TODO: compute the HMAC. + + ByteBuffer buf; + request.Write(&buf); + + return SendPacket(buf.Data(), buf.Length()); +} + +void RelayEntry::ScheduleKeepAlive() { + requests_.SendDelayed(new AllocateRequest(this), KEEPALIVE_DELAY); +} + +void RelayEntry::HandleConnectFailure() { + //if (GetMillisecondCount() - start_time_ > RETRY_TIMEOUT) + // return; + //ScheduleKeepAlive(); + + connected_ = false; + port()->DisposeSocket(socket_); + socket_ = 0; + server_index_ += 1; + Connect(); +} + +void RelayEntry::OnSocketConnect(AsyncTCPSocket* socket) { + assert(socket == socket_); + LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString(); + requests_.Send(new AllocateRequest(this)); +} + +void RelayEntry::OnSocketClose(AsyncTCPSocket* socket, int error) { + assert(socket == socket_); + PLOG(LERROR, error) << "relay tcp connect failed"; + HandleConnectFailure(); +} + +void RelayEntry::OnReadPacket(const char* data, + size_t size, + const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + //assert(remote_addr == port_->server_addr()); TODO: are we worried about this? + + // If the magic cookie is not present, then this is an unwrapped packet sent + // by the server, The actual remote address is the one we recorded. + if (!port_->HasMagicCookie(data, size)) { + if (locked_) { + port_->OnReadPacket(data, size, ext_addr_); + } else { + LOG(WARNING) << "Dropping packet: entry not locked"; + } + return; + } + + ByteBuffer buf(data, size); + StunMessage msg; + if (!msg.Read(&buf)) { + LOG(INFO) << "Incoming packet was not STUN"; + return; + } + + // The incoming packet should be a STUN ALLOCATE response, SEND response, or + // DATA indication. + if (requests_.CheckResponse(&msg)) { + return; + } else if (msg.type() == STUN_SEND_RESPONSE) { + if (const StunUInt32Attribute* options_attr = msg.GetUInt32(STUN_ATTR_OPTIONS)) { + if (options_attr->value() & 0x1) { + locked_ = true; + } + } + return; + } else if (msg.type() != STUN_DATA_INDICATION) { + LOG(INFO) << "Received BAD stun type from server: " << msg.type() + ; + return; + } + + // This must be a data indication. + + const StunAddressAttribute* addr_attr = + msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2); + if (!addr_attr) { + LOG(INFO) << "Data indication has no source address"; + return; + } else if (addr_attr->family() != 1) { + LOG(INFO) << "Source address has bad family"; + return; + } + + SocketAddress remote_addr2(addr_attr->ip(), addr_attr->port()); + + const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA); + if (!data_attr) { + LOG(INFO) << "Data indication has no data"; + return; + } + + // Process the actual data and remote address in the normal manner. + port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2); +} + +void RelayEntry::OnSendPacket(const void* data, size_t size) { + SendPacket(data, size); +} + +int RelayEntry::SendPacket(const void* data, size_t size) { + const ProtocolAddress * ra = port_->ServerAddress(server_index_); + if (!ra) { + socket_->SetError(ENOTCONN); + return SOCKET_ERROR; + } + int sent = socket_->SendTo(data, size, ra->address); + if (sent <= 0) { + LOG(LS_VERBOSE) << "sendto: " << std::strerror(socket_->GetError()); + assert(sent < 0); + } + return sent; +} + +AllocateRequest::AllocateRequest(RelayEntry* entry) : entry_(entry) { + start_time_ = GetMillisecondCount(); +} + +void AllocateRequest::Prepare(StunMessage* request) { + request->SetType(STUN_ALLOCATE_REQUEST); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes( + entry_->port()->magic_cookie().c_str(), + (uint16)entry_->port()->magic_cookie().size()); + request->AddAttribute(magic_cookie_attr); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username_attr->CopyBytes( + entry_->port()->username_fragment().c_str(), + (uint16)entry_->port()->username_fragment().size()); + request->AddAttribute(username_attr); +} + +int AllocateRequest::GetNextDelay() { + int delay = 100 * _max(1 << count_, 2); + count_ += 1; + if (count_ == 5) + timeout_ = true; + return delay; +} + +void AllocateRequest::OnResponse(StunMessage* response) { + const StunAddressAttribute* addr_attr = + response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + if (!addr_attr) { + LOG(INFO) << "Allocate response missing mapped address."; + } else if (addr_attr->family() != 1) { + LOG(INFO) << "Mapped address has bad family"; + } else { + SocketAddress addr(addr_attr->ip(), addr_attr->port()); + entry_->OnConnect(addr); + } + + // We will do a keep-alive regardless of whether this request suceeds. + // This should have almost no impact on network usage. + entry_->ScheduleKeepAlive(); +} + +void AllocateRequest::OnErrorResponse(StunMessage* response) { + const StunErrorCodeAttribute* attr = response->GetErrorCode(); + if (!attr) { + LOG(INFO) << "Bad allocate response error code"; + } else { + LOG(INFO) << "Allocate error response:" + << " code=" << static_cast(attr->error_code()) + << " reason='" << attr->reason() << "'"; + } + + if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + entry_->ScheduleKeepAlive(); +} + +void AllocateRequest::OnTimeout() { + LOG(INFO) << "Allocate request timed out"; + entry_->HandleConnectFailure(); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc deleted file mode 100644 index bb52a1d5..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc +++ /dev/null @@ -1,657 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/p2p/base/relayserver.h" -#include "talk/p2p/base/helpers.h" -#include -#include -#include -#include - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -// By default, we require a ping every 90 seconds. -const int MAX_LIFETIME = 15 * 60 * 1000; - -// The number of bytes in each of the usernames we use. -const uint32 USERNAME_LENGTH = 16; - -// Calls SendTo on the given socket and logs any bad results. -void Send(AsyncPacketSocket* socket, const char* bytes, size_t size, - const SocketAddress& addr) { - int result = socket->SendTo(bytes, size, addr); - if (result < int(size)) { - std::cerr << "SendTo wrote only " << result << " of " << int(size) - << " bytes" << std::endl; - } else if (result < 0) { - std::cerr << "SendTo: " << std::strerror(errno) << std::endl; - } -} - -// Sends the given STUN message on the given socket. -void SendStun(const StunMessage& msg, - AsyncPacketSocket* socket, - const SocketAddress& addr) { - ByteBuffer buf; - msg.Write(&buf); - Send(socket, buf.Data(), buf.Length(), addr); -} - -// Constructs a STUN error response and sends it on the given socket. -void SendStunError(const StunMessage& msg, AsyncPacketSocket* socket, - const SocketAddress& remote_addr, int error_code, - const char* error_desc, const std::string& magic_cookie) { - - StunMessage err_msg; - err_msg.SetType(GetStunErrorResponseType(msg.type())); - err_msg.SetTransactionID(msg.transaction_id()); - - StunByteStringAttribute* magic_cookie_attr = - StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); - if (magic_cookie.size() == 0) - magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4); - else - magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size()); - err_msg.AddAttribute(magic_cookie_attr); - - StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); - err_code->SetErrorClass(error_code / 100); - err_code->SetNumber(error_code % 100); - err_code->SetReason(error_desc); - err_msg.AddAttribute(err_code); - - SendStun(err_msg, socket, remote_addr); -} - -RelayServer::RelayServer(Thread* thread) : thread_(thread) { -} - -RelayServer::~RelayServer() { - for (unsigned i = 0; i < internal_sockets_.size(); i++) - delete internal_sockets_[i]; - for (unsigned i = 0; i < external_sockets_.size(); i++) - delete external_sockets_[i]; -} - -void RelayServer::AddInternalSocket(AsyncPacketSocket* socket) { - assert(internal_sockets_.end() == - std::find(internal_sockets_.begin(), internal_sockets_.end(), socket)); - internal_sockets_.push_back(socket); - socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket); -} - -void RelayServer::RemoveInternalSocket(AsyncPacketSocket* socket) { - SocketList::iterator iter = - std::find(internal_sockets_.begin(), internal_sockets_.end(), socket); - assert(iter != internal_sockets_.end()); - internal_sockets_.erase(iter); - socket->SignalReadPacket.disconnect(this); -} - -void RelayServer::AddExternalSocket(AsyncPacketSocket* socket) { - assert(external_sockets_.end() == - std::find(external_sockets_.begin(), external_sockets_.end(), socket)); - external_sockets_.push_back(socket); - socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket); -} - -void RelayServer::RemoveExternalSocket(AsyncPacketSocket* socket) { - SocketList::iterator iter = - std::find(external_sockets_.begin(), external_sockets_.end(), socket); - assert(iter != external_sockets_.end()); - external_sockets_.erase(iter); - socket->SignalReadPacket.disconnect(this); -} - -void RelayServer::OnInternalPacket( - const char* bytes, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - - // Get the address of the connection we just received on. - SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); - assert(!ap.destination().IsAny()); - - // If this did not come from an existing connection, it should be a STUN - // allocate request. - ConnectionMap::iterator piter = connections_.find(ap); - if (piter == connections_.end()) { - HandleStunAllocate(bytes, size, ap, socket); - return; - } - - RelayServerConnection* int_conn = piter->second; - - // Handle STUN requests to the server itself. - if (int_conn->binding()->HasMagicCookie(bytes, size)) { - HandleStun(int_conn, bytes, size); - return; - } - - // Otherwise, this is a non-wrapped packet that we are to forward. Make sure - // that this connection has been locked. (Otherwise, we would not know what - // address to forward to.) - if (!int_conn->locked()) { - std::cerr << "Dropping packet: connection not locked" << std::endl; - return; - } - - // Forward this to the destination address into the connection. - RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection( - int_conn->default_destination()); - if (ext_conn) { - // TODO: Check the HMAC. - ext_conn->Send(bytes, size); - } else { - // This happens very often and is not an error. - //std::cerr << "Dropping packet: no external connection" << std::endl; - } -} - -void RelayServer::OnExternalPacket( - const char* bytes, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - - // Get the address of the connection we just received on. - SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); - assert(!ap.destination().IsAny()); - - // If this connection already exists, then forward the traffic. - ConnectionMap::iterator piter = connections_.find(ap); - if (piter != connections_.end()) { - // TODO: Check the HMAC. - RelayServerConnection* ext_conn = piter->second; - RelayServerConnection* int_conn = - ext_conn->binding()->GetInternalConnection( - ext_conn->addr_pair().source()); - assert(int_conn); - int_conn->Send(bytes, size, ext_conn->addr_pair().source()); - return; - } - - // The first packet should always be a STUN / TURN packet. If it isn't, then - // we should just ignore this packet. - StunMessage msg; - ByteBuffer buf = ByteBuffer(bytes, size); - if (!msg.Read(&buf)) { - std::cerr << "Dropping packet: first packet not STUN" << std::endl; - return; - } - - // The initial packet should have a username (which identifies the binding). - const StunByteStringAttribute* username_attr = - msg.GetByteString(STUN_ATTR_USERNAME); - if (!username_attr) { - std::cerr << "Dropping packet: no username" << std::endl; - return; - } - - uint32 length = _min(uint32(username_attr->length()), USERNAME_LENGTH); - std::string username(username_attr->bytes(), length); - // TODO: Check the HMAC. - - // The binding should already be present. - BindingMap::iterator biter = bindings_.find(username); - if (biter == bindings_.end()) { - // TODO: Turn this back on. This is the sign of a client bug. - //std::cerr << "Dropping packet: no binding with username" << std::endl; - return; - } - - // Add this authenticted connection to the binding. - RelayServerConnection* ext_conn = - new RelayServerConnection(biter->second, ap, socket); - ext_conn->binding()->AddExternalConnection(ext_conn); - AddConnection(ext_conn); - - // We always know where external packets should be forwarded, so we can lock - // them from the beginning. - ext_conn->Lock(); - - // Send this message on the appropriate internal connection. - RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection( - ext_conn->addr_pair().source()); - assert(int_conn); - int_conn->Send(bytes, size, ext_conn->addr_pair().source()); -} - -bool RelayServer::HandleStun( - const char* bytes, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket, std::string* username, StunMessage* msg) { - - // Parse this into a stun message. - ByteBuffer buf = ByteBuffer(bytes, size); - if (!msg->Read(&buf)) { - SendStunError(*msg, socket, remote_addr, 400, "Bad Request", ""); - return false; - } - - // The initial packet should have a username (which identifies the binding). - const StunByteStringAttribute* username_attr = - msg->GetByteString(STUN_ATTR_USERNAME); - if (!username_attr) { - SendStunError(*msg, socket, remote_addr, 432, "Missing Username", ""); - return false; - } - - // Record the username if requested. - if (username) - username->append(username_attr->bytes(), username_attr->length()); - - // TODO: Check for unknown attributes (<= 0x7fff) - - return true; -} - -void RelayServer::HandleStunAllocate( - const char* bytes, size_t size, const SocketAddressPair& ap, - AsyncPacketSocket* socket) { - - // Make sure this is a valid STUN request. - StunMessage request; - std::string username; - if (!HandleStun(bytes, size, ap.source(), socket, &username, &request)) - return; - - // Make sure this is a an allocate request. - if (request.type() != STUN_ALLOCATE_REQUEST) { - SendStunError(request, - socket, - ap.source(), - 600, - "Operation Not Supported", - ""); - return; - } - - // TODO: Check the HMAC. - - // Find or create the binding for this username. - - RelayServerBinding* binding; - - BindingMap::iterator biter = bindings_.find(username); - if (biter != bindings_.end()) { - - binding = biter->second; - - } else { - - // NOTE: In the future, bindings will be created by the bot only. This - // else-branch will then disappear. - - // Compute the appropriate lifetime for this binding. - uint32 lifetime = MAX_LIFETIME; - const StunUInt32Attribute* lifetime_attr = - request.GetUInt32(STUN_ATTR_LIFETIME); - if (lifetime_attr) - lifetime = _min(lifetime, lifetime_attr->value() * 1000); - - binding = new RelayServerBinding(this, username, "0", lifetime); - binding->SignalTimeout.connect(this, &RelayServer::OnTimeout); - bindings_[username] = binding; - - std::cout << "Added new binding: " << bindings_.size() << " total" << std::endl; - } - - // Add this connection to the binding. It starts out unlocked. - RelayServerConnection* int_conn = - new RelayServerConnection(binding, ap, socket); - binding->AddInternalConnection(int_conn); - AddConnection(int_conn); - - // Now that we have a connection, this other method takes over. - HandleStunAllocate(int_conn, request); -} - -void RelayServer::HandleStun( - RelayServerConnection* int_conn, const char* bytes, size_t size) { - - // Make sure this is a valid STUN request. - StunMessage request; - std::string username; - if (!HandleStun(bytes, size, int_conn->addr_pair().source(), - int_conn->socket(), &username, &request)) - return; - - // Make sure the username is the one were were expecting. - if (username != int_conn->binding()->username()) { - int_conn->SendStunError(request, 430, "Stale Credentials"); - return; - } - - // TODO: Check the HMAC. - - // Send this request to the appropriate handler. - if (request.type() == STUN_SEND_REQUEST) - HandleStunSend(int_conn, request); - else if (request.type() == STUN_ALLOCATE_REQUEST) - HandleStunAllocate(int_conn, request); - else - int_conn->SendStunError(request, 600, "Operation Not Supported"); -} - -void RelayServer::HandleStunAllocate( - RelayServerConnection* int_conn, const StunMessage& request) { - - // Create a response message that includes an address with which external - // clients can communicate. - - StunMessage response; - response.SetType(STUN_ALLOCATE_RESPONSE); - response.SetTransactionID(request.transaction_id()); - - StunByteStringAttribute* magic_cookie_attr = - StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); - magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), - int_conn->binding()->magic_cookie().size()); - response.AddAttribute(magic_cookie_attr); - - size_t index = rand() % external_sockets_.size(); - SocketAddress ext_addr = external_sockets_[index]->GetLocalAddress(); - - StunAddressAttribute* addr_attr = - StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); - addr_attr->SetFamily(1); - addr_attr->SetIP(ext_addr.ip()); - addr_attr->SetPort(ext_addr.port()); - response.AddAttribute(addr_attr); - - StunUInt32Attribute* res_lifetime_attr = - StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME); - res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000); - response.AddAttribute(res_lifetime_attr); - - // TODO: Support transport-prefs (preallocate RTCP port). - // TODO: Support bandwidth restrictions. - // TODO: Add message integrity check. - - // Send a response to the caller. - int_conn->SendStun(response); -} - -void RelayServer::HandleStunSend( - RelayServerConnection* int_conn, const StunMessage& request) { - - const StunAddressAttribute* addr_attr = - request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS); - if (!addr_attr) { - int_conn->SendStunError(request, 400, "Bad Request"); - return; - } - - const StunByteStringAttribute* data_attr = - request.GetByteString(STUN_ATTR_DATA); - if (!data_attr) { - int_conn->SendStunError(request, 400, "Bad Request"); - return; - } - - SocketAddress ext_addr(addr_attr->ip(), addr_attr->port()); - RelayServerConnection* ext_conn = - int_conn->binding()->GetExternalConnection(ext_addr); - if (!ext_conn) { - // This happens very often and is not an error. - //std::cerr << "Dropping packet: no external connection" << std::endl; - return; - } - - ext_conn->Send(data_attr->bytes(), data_attr->length()); - - const StunUInt32Attribute* options_attr = - request.GetUInt32(STUN_ATTR_OPTIONS); - if (options_attr && (options_attr->value() & 0x01 != 0)) { - int_conn->set_default_destination(ext_addr); - int_conn->Lock(); - - StunMessage response; - response.SetType(STUN_SEND_RESPONSE); - response.SetTransactionID(request.transaction_id()); - - StunByteStringAttribute* magic_cookie_attr = - StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); - magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), - int_conn->binding()->magic_cookie().size()); - response.AddAttribute(magic_cookie_attr); - - StunUInt32Attribute* options2_attr = - StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS); - options2_attr->SetValue(0x01); - response.AddAttribute(options2_attr); - - int_conn->SendStun(response); - } -} - -void RelayServer::AddConnection(RelayServerConnection* conn) { - assert(connections_.find(conn->addr_pair()) == connections_.end()); - connections_[conn->addr_pair()] = conn; -} - -void RelayServer::RemoveConnection(RelayServerConnection* conn) { - ConnectionMap::iterator iter = connections_.find(conn->addr_pair()); - assert(iter != connections_.end()); - connections_.erase(iter); -} - -void RelayServer::RemoveBinding(RelayServerBinding* binding) { - BindingMap::iterator iter = bindings_.find(binding->username()); - assert(iter != bindings_.end()); - bindings_.erase(iter); - - std::cout << "Removed a binding: " << bindings_.size() << " remaining" << std::endl; -} - -void RelayServer::OnTimeout(RelayServerBinding* binding) { - // This call will result in all of the necessary clean-up. - delete binding; -} - -RelayServerConnection::RelayServerConnection( - RelayServerBinding* binding, const SocketAddressPair& addrs, - AsyncPacketSocket* socket) - : binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) { - - // The creation of a new connection constitutes a use of the binding. - binding_->NoteUsed(); -} - -RelayServerConnection::~RelayServerConnection() { - // Remove this connection from the server's map (if it exists there). - binding_->server()->RemoveConnection(this); -} - -void RelayServerConnection::Send(const char* data, size_t size) { - // Note that the binding has been used again. - binding_->NoteUsed(); - - cricket::Send(socket_, data, size, addr_pair_.source()); -} - -void RelayServerConnection::Send( - const char* data, size_t size, const SocketAddress& from_addr) { - // If the from address is known to the client, we don't need to send it. - if (locked() && (from_addr == default_dest_)) { - Send(data, size); - return; - } - - // Wrap the given data in a data-indication packet. - - StunMessage msg; - msg.SetType(STUN_DATA_INDICATION); - msg.SetTransactionID("0000000000000000"); - - StunByteStringAttribute* magic_cookie_attr = - StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); - magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(), - binding_->magic_cookie().size()); - msg.AddAttribute(magic_cookie_attr); - - StunAddressAttribute* addr_attr = - StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2); - addr_attr->SetFamily(1); - addr_attr->SetIP(from_addr.ip()); - addr_attr->SetPort(from_addr.port()); - msg.AddAttribute(addr_attr); - - StunByteStringAttribute* data_attr = - StunAttribute::CreateByteString(STUN_ATTR_DATA); - assert(size <= 65536); - data_attr->CopyBytes(data, uint16(size)); - msg.AddAttribute(data_attr); - - SendStun(msg); -} - -void RelayServerConnection::SendStun(const StunMessage& msg) { - // Note that the binding has been used again. - binding_->NoteUsed(); - - cricket::SendStun(msg, socket_, addr_pair_.source()); -} - -void RelayServerConnection::SendStunError( - const StunMessage& request, int error_code, const char* error_desc) { - // An error does not indicate use. If no legitimate use off the binding - // occurs, we want it to be cleaned up even if errors are still occuring. - - cricket::SendStunError( - request, socket_, addr_pair_.source(), error_code, error_desc, - binding_->magic_cookie()); -} - -void RelayServerConnection::Lock() { - locked_ = true; -} - -void RelayServerConnection::Unlock() { - locked_ = false; -} - -// IDs used for posted messages: -const uint32 MSG_LIFETIME_TIMER = 1; - -RelayServerBinding::RelayServerBinding( - RelayServer* server, const std::string& username, - const std::string& password, uint32 lifetime) - : server_(server), username_(username), password_(password), - lifetime_(lifetime) { - - // For now, every connection uses the standard magic cookie value. - magic_cookie_.append( - reinterpret_cast(STUN_MAGIC_COOKIE_VALUE), 4); - - // Initialize the last-used time to now. - NoteUsed(); - - // Set the first timeout check. - server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); -} - -RelayServerBinding::~RelayServerBinding() { - // Clear the outstanding timeout check. - server_->thread()->Clear(this); - - // Clean up all of the connections. - for (size_t i = 0; i < internal_connections_.size(); ++i) - delete internal_connections_[i]; - for (size_t i = 0; i < external_connections_.size(); ++i) - delete external_connections_[i]; - - // Remove this binding from the server's map. - server_->RemoveBinding(this); -} - -void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) { - internal_connections_.push_back(conn); -} - -void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) { - external_connections_.push_back(conn); -} - -void RelayServerBinding::NoteUsed() { - last_used_ = GetMillisecondCount(); -} - -bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const { - if (size < 24 + magic_cookie_.size()) { - return false; - } else { - return 0 == std::memcmp( - bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()); - } -} - -RelayServerConnection* RelayServerBinding::GetInternalConnection( - const SocketAddress& ext_addr) { - - // Look for an internal connection that is locked to this address. - for (size_t i = 0; i < internal_connections_.size(); ++i) { - if (internal_connections_[i]->locked() && - (ext_addr == internal_connections_[i]->default_destination())) - return internal_connections_[i]; - } - - // If one was not found, we send to the first connection. - assert(internal_connections_.size() > 0); - return internal_connections_[0]; -} - -RelayServerConnection* RelayServerBinding::GetExternalConnection( - const SocketAddress& ext_addr) { - for (size_t i = 0; i < external_connections_.size(); ++i) { - if (ext_addr == external_connections_[i]->addr_pair().source()) - return external_connections_[i]; - } - return 0; -} - -void RelayServerBinding::OnMessage(Message *pmsg) { - if (pmsg->message_id == MSG_LIFETIME_TIMER) { - assert(!pmsg->pdata); - - // If the lifetime timeout has been exceeded, then send a signal. - // Otherwise, just keep waiting. - if (GetMillisecondCount() >= last_used_ + lifetime_) { - SignalTimeout(this); - } else { - server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); - } - - } else { - assert(false); - } -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cpp new file mode 100644 index 00000000..bb52a1d5 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cpp @@ -0,0 +1,657 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/p2p/base/relayserver.h" +#include "talk/p2p/base/helpers.h" +#include +#include +#include +#include + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +// By default, we require a ping every 90 seconds. +const int MAX_LIFETIME = 15 * 60 * 1000; + +// The number of bytes in each of the usernames we use. +const uint32 USERNAME_LENGTH = 16; + +// Calls SendTo on the given socket and logs any bad results. +void Send(AsyncPacketSocket* socket, const char* bytes, size_t size, + const SocketAddress& addr) { + int result = socket->SendTo(bytes, size, addr); + if (result < int(size)) { + std::cerr << "SendTo wrote only " << result << " of " << int(size) + << " bytes" << std::endl; + } else if (result < 0) { + std::cerr << "SendTo: " << std::strerror(errno) << std::endl; + } +} + +// Sends the given STUN message on the given socket. +void SendStun(const StunMessage& msg, + AsyncPacketSocket* socket, + const SocketAddress& addr) { + ByteBuffer buf; + msg.Write(&buf); + Send(socket, buf.Data(), buf.Length(), addr); +} + +// Constructs a STUN error response and sends it on the given socket. +void SendStunError(const StunMessage& msg, AsyncPacketSocket* socket, + const SocketAddress& remote_addr, int error_code, + const char* error_desc, const std::string& magic_cookie) { + + StunMessage err_msg; + err_msg.SetType(GetStunErrorResponseType(msg.type())); + err_msg.SetTransactionID(msg.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + if (magic_cookie.size() == 0) + magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4); + else + magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size()); + err_msg.AddAttribute(magic_cookie_attr); + + StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); + err_code->SetErrorClass(error_code / 100); + err_code->SetNumber(error_code % 100); + err_code->SetReason(error_desc); + err_msg.AddAttribute(err_code); + + SendStun(err_msg, socket, remote_addr); +} + +RelayServer::RelayServer(Thread* thread) : thread_(thread) { +} + +RelayServer::~RelayServer() { + for (unsigned i = 0; i < internal_sockets_.size(); i++) + delete internal_sockets_[i]; + for (unsigned i = 0; i < external_sockets_.size(); i++) + delete external_sockets_[i]; +} + +void RelayServer::AddInternalSocket(AsyncPacketSocket* socket) { + assert(internal_sockets_.end() == + std::find(internal_sockets_.begin(), internal_sockets_.end(), socket)); + internal_sockets_.push_back(socket); + socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket); +} + +void RelayServer::RemoveInternalSocket(AsyncPacketSocket* socket) { + SocketList::iterator iter = + std::find(internal_sockets_.begin(), internal_sockets_.end(), socket); + assert(iter != internal_sockets_.end()); + internal_sockets_.erase(iter); + socket->SignalReadPacket.disconnect(this); +} + +void RelayServer::AddExternalSocket(AsyncPacketSocket* socket) { + assert(external_sockets_.end() == + std::find(external_sockets_.begin(), external_sockets_.end(), socket)); + external_sockets_.push_back(socket); + socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket); +} + +void RelayServer::RemoveExternalSocket(AsyncPacketSocket* socket) { + SocketList::iterator iter = + std::find(external_sockets_.begin(), external_sockets_.end(), socket); + assert(iter != external_sockets_.end()); + external_sockets_.erase(iter); + socket->SignalReadPacket.disconnect(this); +} + +void RelayServer::OnInternalPacket( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + // Get the address of the connection we just received on. + SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + assert(!ap.destination().IsAny()); + + // If this did not come from an existing connection, it should be a STUN + // allocate request. + ConnectionMap::iterator piter = connections_.find(ap); + if (piter == connections_.end()) { + HandleStunAllocate(bytes, size, ap, socket); + return; + } + + RelayServerConnection* int_conn = piter->second; + + // Handle STUN requests to the server itself. + if (int_conn->binding()->HasMagicCookie(bytes, size)) { + HandleStun(int_conn, bytes, size); + return; + } + + // Otherwise, this is a non-wrapped packet that we are to forward. Make sure + // that this connection has been locked. (Otherwise, we would not know what + // address to forward to.) + if (!int_conn->locked()) { + std::cerr << "Dropping packet: connection not locked" << std::endl; + return; + } + + // Forward this to the destination address into the connection. + RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection( + int_conn->default_destination()); + if (ext_conn) { + // TODO: Check the HMAC. + ext_conn->Send(bytes, size); + } else { + // This happens very often and is not an error. + //std::cerr << "Dropping packet: no external connection" << std::endl; + } +} + +void RelayServer::OnExternalPacket( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + // Get the address of the connection we just received on. + SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + assert(!ap.destination().IsAny()); + + // If this connection already exists, then forward the traffic. + ConnectionMap::iterator piter = connections_.find(ap); + if (piter != connections_.end()) { + // TODO: Check the HMAC. + RelayServerConnection* ext_conn = piter->second; + RelayServerConnection* int_conn = + ext_conn->binding()->GetInternalConnection( + ext_conn->addr_pair().source()); + assert(int_conn); + int_conn->Send(bytes, size, ext_conn->addr_pair().source()); + return; + } + + // The first packet should always be a STUN / TURN packet. If it isn't, then + // we should just ignore this packet. + StunMessage msg; + ByteBuffer buf = ByteBuffer(bytes, size); + if (!msg.Read(&buf)) { + std::cerr << "Dropping packet: first packet not STUN" << std::endl; + return; + } + + // The initial packet should have a username (which identifies the binding). + const StunByteStringAttribute* username_attr = + msg.GetByteString(STUN_ATTR_USERNAME); + if (!username_attr) { + std::cerr << "Dropping packet: no username" << std::endl; + return; + } + + uint32 length = _min(uint32(username_attr->length()), USERNAME_LENGTH); + std::string username(username_attr->bytes(), length); + // TODO: Check the HMAC. + + // The binding should already be present. + BindingMap::iterator biter = bindings_.find(username); + if (biter == bindings_.end()) { + // TODO: Turn this back on. This is the sign of a client bug. + //std::cerr << "Dropping packet: no binding with username" << std::endl; + return; + } + + // Add this authenticted connection to the binding. + RelayServerConnection* ext_conn = + new RelayServerConnection(biter->second, ap, socket); + ext_conn->binding()->AddExternalConnection(ext_conn); + AddConnection(ext_conn); + + // We always know where external packets should be forwarded, so we can lock + // them from the beginning. + ext_conn->Lock(); + + // Send this message on the appropriate internal connection. + RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection( + ext_conn->addr_pair().source()); + assert(int_conn); + int_conn->Send(bytes, size, ext_conn->addr_pair().source()); +} + +bool RelayServer::HandleStun( + const char* bytes, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket, std::string* username, StunMessage* msg) { + + // Parse this into a stun message. + ByteBuffer buf = ByteBuffer(bytes, size); + if (!msg->Read(&buf)) { + SendStunError(*msg, socket, remote_addr, 400, "Bad Request", ""); + return false; + } + + // The initial packet should have a username (which identifies the binding). + const StunByteStringAttribute* username_attr = + msg->GetByteString(STUN_ATTR_USERNAME); + if (!username_attr) { + SendStunError(*msg, socket, remote_addr, 432, "Missing Username", ""); + return false; + } + + // Record the username if requested. + if (username) + username->append(username_attr->bytes(), username_attr->length()); + + // TODO: Check for unknown attributes (<= 0x7fff) + + return true; +} + +void RelayServer::HandleStunAllocate( + const char* bytes, size_t size, const SocketAddressPair& ap, + AsyncPacketSocket* socket) { + + // Make sure this is a valid STUN request. + StunMessage request; + std::string username; + if (!HandleStun(bytes, size, ap.source(), socket, &username, &request)) + return; + + // Make sure this is a an allocate request. + if (request.type() != STUN_ALLOCATE_REQUEST) { + SendStunError(request, + socket, + ap.source(), + 600, + "Operation Not Supported", + ""); + return; + } + + // TODO: Check the HMAC. + + // Find or create the binding for this username. + + RelayServerBinding* binding; + + BindingMap::iterator biter = bindings_.find(username); + if (biter != bindings_.end()) { + + binding = biter->second; + + } else { + + // NOTE: In the future, bindings will be created by the bot only. This + // else-branch will then disappear. + + // Compute the appropriate lifetime for this binding. + uint32 lifetime = MAX_LIFETIME; + const StunUInt32Attribute* lifetime_attr = + request.GetUInt32(STUN_ATTR_LIFETIME); + if (lifetime_attr) + lifetime = _min(lifetime, lifetime_attr->value() * 1000); + + binding = new RelayServerBinding(this, username, "0", lifetime); + binding->SignalTimeout.connect(this, &RelayServer::OnTimeout); + bindings_[username] = binding; + + std::cout << "Added new binding: " << bindings_.size() << " total" << std::endl; + } + + // Add this connection to the binding. It starts out unlocked. + RelayServerConnection* int_conn = + new RelayServerConnection(binding, ap, socket); + binding->AddInternalConnection(int_conn); + AddConnection(int_conn); + + // Now that we have a connection, this other method takes over. + HandleStunAllocate(int_conn, request); +} + +void RelayServer::HandleStun( + RelayServerConnection* int_conn, const char* bytes, size_t size) { + + // Make sure this is a valid STUN request. + StunMessage request; + std::string username; + if (!HandleStun(bytes, size, int_conn->addr_pair().source(), + int_conn->socket(), &username, &request)) + return; + + // Make sure the username is the one were were expecting. + if (username != int_conn->binding()->username()) { + int_conn->SendStunError(request, 430, "Stale Credentials"); + return; + } + + // TODO: Check the HMAC. + + // Send this request to the appropriate handler. + if (request.type() == STUN_SEND_REQUEST) + HandleStunSend(int_conn, request); + else if (request.type() == STUN_ALLOCATE_REQUEST) + HandleStunAllocate(int_conn, request); + else + int_conn->SendStunError(request, 600, "Operation Not Supported"); +} + +void RelayServer::HandleStunAllocate( + RelayServerConnection* int_conn, const StunMessage& request) { + + // Create a response message that includes an address with which external + // clients can communicate. + + StunMessage response; + response.SetType(STUN_ALLOCATE_RESPONSE); + response.SetTransactionID(request.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), + int_conn->binding()->magic_cookie().size()); + response.AddAttribute(magic_cookie_attr); + + size_t index = rand() % external_sockets_.size(); + SocketAddress ext_addr = external_sockets_[index]->GetLocalAddress(); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetIP(ext_addr.ip()); + addr_attr->SetPort(ext_addr.port()); + response.AddAttribute(addr_attr); + + StunUInt32Attribute* res_lifetime_attr = + StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME); + res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000); + response.AddAttribute(res_lifetime_attr); + + // TODO: Support transport-prefs (preallocate RTCP port). + // TODO: Support bandwidth restrictions. + // TODO: Add message integrity check. + + // Send a response to the caller. + int_conn->SendStun(response); +} + +void RelayServer::HandleStunSend( + RelayServerConnection* int_conn, const StunMessage& request) { + + const StunAddressAttribute* addr_attr = + request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS); + if (!addr_attr) { + int_conn->SendStunError(request, 400, "Bad Request"); + return; + } + + const StunByteStringAttribute* data_attr = + request.GetByteString(STUN_ATTR_DATA); + if (!data_attr) { + int_conn->SendStunError(request, 400, "Bad Request"); + return; + } + + SocketAddress ext_addr(addr_attr->ip(), addr_attr->port()); + RelayServerConnection* ext_conn = + int_conn->binding()->GetExternalConnection(ext_addr); + if (!ext_conn) { + // This happens very often and is not an error. + //std::cerr << "Dropping packet: no external connection" << std::endl; + return; + } + + ext_conn->Send(data_attr->bytes(), data_attr->length()); + + const StunUInt32Attribute* options_attr = + request.GetUInt32(STUN_ATTR_OPTIONS); + if (options_attr && (options_attr->value() & 0x01 != 0)) { + int_conn->set_default_destination(ext_addr); + int_conn->Lock(); + + StunMessage response; + response.SetType(STUN_SEND_RESPONSE); + response.SetTransactionID(request.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), + int_conn->binding()->magic_cookie().size()); + response.AddAttribute(magic_cookie_attr); + + StunUInt32Attribute* options2_attr = + StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS); + options2_attr->SetValue(0x01); + response.AddAttribute(options2_attr); + + int_conn->SendStun(response); + } +} + +void RelayServer::AddConnection(RelayServerConnection* conn) { + assert(connections_.find(conn->addr_pair()) == connections_.end()); + connections_[conn->addr_pair()] = conn; +} + +void RelayServer::RemoveConnection(RelayServerConnection* conn) { + ConnectionMap::iterator iter = connections_.find(conn->addr_pair()); + assert(iter != connections_.end()); + connections_.erase(iter); +} + +void RelayServer::RemoveBinding(RelayServerBinding* binding) { + BindingMap::iterator iter = bindings_.find(binding->username()); + assert(iter != bindings_.end()); + bindings_.erase(iter); + + std::cout << "Removed a binding: " << bindings_.size() << " remaining" << std::endl; +} + +void RelayServer::OnTimeout(RelayServerBinding* binding) { + // This call will result in all of the necessary clean-up. + delete binding; +} + +RelayServerConnection::RelayServerConnection( + RelayServerBinding* binding, const SocketAddressPair& addrs, + AsyncPacketSocket* socket) + : binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) { + + // The creation of a new connection constitutes a use of the binding. + binding_->NoteUsed(); +} + +RelayServerConnection::~RelayServerConnection() { + // Remove this connection from the server's map (if it exists there). + binding_->server()->RemoveConnection(this); +} + +void RelayServerConnection::Send(const char* data, size_t size) { + // Note that the binding has been used again. + binding_->NoteUsed(); + + cricket::Send(socket_, data, size, addr_pair_.source()); +} + +void RelayServerConnection::Send( + const char* data, size_t size, const SocketAddress& from_addr) { + // If the from address is known to the client, we don't need to send it. + if (locked() && (from_addr == default_dest_)) { + Send(data, size); + return; + } + + // Wrap the given data in a data-indication packet. + + StunMessage msg; + msg.SetType(STUN_DATA_INDICATION); + msg.SetTransactionID("0000000000000000"); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(), + binding_->magic_cookie().size()); + msg.AddAttribute(magic_cookie_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2); + addr_attr->SetFamily(1); + addr_attr->SetIP(from_addr.ip()); + addr_attr->SetPort(from_addr.port()); + msg.AddAttribute(addr_attr); + + StunByteStringAttribute* data_attr = + StunAttribute::CreateByteString(STUN_ATTR_DATA); + assert(size <= 65536); + data_attr->CopyBytes(data, uint16(size)); + msg.AddAttribute(data_attr); + + SendStun(msg); +} + +void RelayServerConnection::SendStun(const StunMessage& msg) { + // Note that the binding has been used again. + binding_->NoteUsed(); + + cricket::SendStun(msg, socket_, addr_pair_.source()); +} + +void RelayServerConnection::SendStunError( + const StunMessage& request, int error_code, const char* error_desc) { + // An error does not indicate use. If no legitimate use off the binding + // occurs, we want it to be cleaned up even if errors are still occuring. + + cricket::SendStunError( + request, socket_, addr_pair_.source(), error_code, error_desc, + binding_->magic_cookie()); +} + +void RelayServerConnection::Lock() { + locked_ = true; +} + +void RelayServerConnection::Unlock() { + locked_ = false; +} + +// IDs used for posted messages: +const uint32 MSG_LIFETIME_TIMER = 1; + +RelayServerBinding::RelayServerBinding( + RelayServer* server, const std::string& username, + const std::string& password, uint32 lifetime) + : server_(server), username_(username), password_(password), + lifetime_(lifetime) { + + // For now, every connection uses the standard magic cookie value. + magic_cookie_.append( + reinterpret_cast(STUN_MAGIC_COOKIE_VALUE), 4); + + // Initialize the last-used time to now. + NoteUsed(); + + // Set the first timeout check. + server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); +} + +RelayServerBinding::~RelayServerBinding() { + // Clear the outstanding timeout check. + server_->thread()->Clear(this); + + // Clean up all of the connections. + for (size_t i = 0; i < internal_connections_.size(); ++i) + delete internal_connections_[i]; + for (size_t i = 0; i < external_connections_.size(); ++i) + delete external_connections_[i]; + + // Remove this binding from the server's map. + server_->RemoveBinding(this); +} + +void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) { + internal_connections_.push_back(conn); +} + +void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) { + external_connections_.push_back(conn); +} + +void RelayServerBinding::NoteUsed() { + last_used_ = GetMillisecondCount(); +} + +bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const { + if (size < 24 + magic_cookie_.size()) { + return false; + } else { + return 0 == std::memcmp( + bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()); + } +} + +RelayServerConnection* RelayServerBinding::GetInternalConnection( + const SocketAddress& ext_addr) { + + // Look for an internal connection that is locked to this address. + for (size_t i = 0; i < internal_connections_.size(); ++i) { + if (internal_connections_[i]->locked() && + (ext_addr == internal_connections_[i]->default_destination())) + return internal_connections_[i]; + } + + // If one was not found, we send to the first connection. + assert(internal_connections_.size() > 0); + return internal_connections_[0]; +} + +RelayServerConnection* RelayServerBinding::GetExternalConnection( + const SocketAddress& ext_addr) { + for (size_t i = 0; i < external_connections_.size(); ++i) { + if (ext_addr == external_connections_[i]->addr_pair().source()) + return external_connections_[i]; + } + return 0; +} + +void RelayServerBinding::OnMessage(Message *pmsg) { + if (pmsg->message_id == MSG_LIFETIME_TIMER) { + assert(!pmsg->pdata); + + // If the lifetime timeout has been exceeded, then send a signal. + // Otherwise, just keep waiting. + if (GetMillisecondCount() >= last_used_ + lifetime_) { + SignalTimeout(this); + } else { + server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); + } + + } else { + assert(false); + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro index 41bc6b63..ca7426e9 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro @@ -6,9 +6,9 @@ include(../../../../../conf.pri) # Input SOURCES += \ - relayserver.cc \ - relayserver_main.cc \ - ../../base/host.cc \ - ../../base/socketaddresspair.cc + relayserver.cpp \ + relayserver_main.cpp \ + ../../base/host.cpp \ + ../../base/socketaddresspair.cpp LIBS += ../../../liblibjingle.a diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc deleted file mode 100644 index 4dfae42c..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" -#include "talk/base/thread.h" -#include "talk/p2p/base/relayserver.h" -#include -#include -#include - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -using namespace cricket; - -int main(int argc, char **argv) { - if (argc != 1) { - std::cerr << "usage: relayserver" << std::endl; - return 1; - } - - assert(LocalHost().networks().size() >= 2); - SocketAddress int_addr(LocalHost().networks()[1]->ip(), 5000); - SocketAddress ext_addr(LocalHost().networks()[1]->ip(), 5001); - - Thread *pthMain = Thread::Current(); - - AsyncUDPSocket* int_socket = CreateAsyncUDPSocket(pthMain->socketserver()); - if (int_socket->Bind(int_addr) < 0) { - std::cerr << "bind: " << std::strerror(errno) << std::endl; - return 1; - } - - AsyncUDPSocket* ext_socket = CreateAsyncUDPSocket(pthMain->socketserver()); - if (ext_socket->Bind(ext_addr) < 0) { - std::cerr << "bind: " << std::strerror(errno) << std::endl; - return 1; - } - - RelayServer server(pthMain); - server.AddInternalSocket(int_socket); - server.AddExternalSocket(ext_socket); - - std::cout << "Listening internally at " << int_addr.ToString() << std::endl; - std::cout << "Listening externally at " << ext_addr.ToString() << std::endl; - - pthMain->Loop(); - return 0; -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cpp new file mode 100644 index 00000000..4dfae42c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cpp @@ -0,0 +1,76 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/relayserver.h" +#include +#include +#include + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +using namespace cricket; + +int main(int argc, char **argv) { + if (argc != 1) { + std::cerr << "usage: relayserver" << std::endl; + return 1; + } + + assert(LocalHost().networks().size() >= 2); + SocketAddress int_addr(LocalHost().networks()[1]->ip(), 5000); + SocketAddress ext_addr(LocalHost().networks()[1]->ip(), 5001); + + Thread *pthMain = Thread::Current(); + + AsyncUDPSocket* int_socket = CreateAsyncUDPSocket(pthMain->socketserver()); + if (int_socket->Bind(int_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + AsyncUDPSocket* ext_socket = CreateAsyncUDPSocket(pthMain->socketserver()); + if (ext_socket->Bind(ext_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + RelayServer server(pthMain); + server.AddInternalSocket(int_socket); + server.AddExternalSocket(ext_socket); + + std::cout << "Listening internally at " << int_addr.ToString() << std::endl; + std::cout << "Listening externally at " << ext_addr.ToString() << std::endl; + + pthMain->Loop(); + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc deleted file mode 100644 index 73873338..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc +++ /dev/null @@ -1,421 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/common.h" -#include "talk/base/logging.h" -#include "talk/p2p/base/helpers.h" -#include "talk/p2p/base/session.h" - -namespace cricket { - -const uint32 MSG_TIMEOUT = 1; -const uint32 MSG_ERROR = 2; -const uint32 MSG_STATE = 3; - -Session::Session(SessionManager *session_manager, const std::string &name, - const SessionID& id) { - session_manager_ = session_manager; - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - name_ = name; - id_ = id; - error_ = ERROR_NONE; - state_ = STATE_INIT; - initiator_ = false; - description_ = NULL; - remote_description_ = NULL; - socket_manager_ = new SocketManager(session_manager_); - socket_manager_->SignalCandidatesReady.connect(this, &Session::OnCandidatesReady); - socket_manager_->SignalNetworkError.connect(this, &Session::OnNetworkError); - socket_manager_->SignalState.connect(this, &Session::OnSocketState); - socket_manager_->SignalRequestSignaling.connect(this, &Session::OnRequestSignaling); -} - -Session::~Session() { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - delete description_; - delete remote_description_; - delete socket_manager_; - session_manager_->signaling_thread()->Clear(this); -} - -P2PSocket *Session::CreateSocket(const std::string &name) { - return socket_manager_->CreateSocket(name); -} - -void Session::DestroySocket(P2PSocket *socket) { - socket_manager_->DestroySocket(socket); -} - -void Session::OnCandidatesReady(const std::vector& candidates) { - SendSessionMessage(SessionMessage::TYPE_CANDIDATES, NULL, &candidates, NULL); -} - -void Session::OnNetworkError() { - // Socket manager is experiencing a network error trying to allocate - // network resources (usually port allocation) - - set_error(ERROR_NETWORK); -} - -void Session::OnSocketState() { - // If the call is not in progress, then we don't care about writability. - // We have separate timers for making sure we transition back to the in- - // progress state in time. - if (state_ != STATE_INPROGRESS) - return; - - // Put the timer into the write state. This is called when the state changes, - // so we will restart the timer each time we lose writability. - if (socket_manager_->writable()) { - session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); - } else { - session_manager_->signaling_thread()->PostDelayed( - session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); - } -} - -void Session::OnRequestSignaling() { - SignalRequestSignaling(); -} - -void Session::OnSignalingReady() { - socket_manager_->OnSignalingReady(); -} - -void Session::SendSessionMessage(SessionMessage::Type type, - const SessionDescription* description, - const std::vector* candidates, - SessionMessage::Cookie* redirect_cookie) { - SessionMessage m; - m.set_type(type); - m.set_to(remote_address_); - m.set_name(name_); - m.set_description(description); - m.set_session_id(id_); - if (candidates) - m.set_candidates(*candidates); - m.set_redirect_target(redirect_target_); - m.set_redirect_cookie(redirect_cookie); - SignalOutgoingMessage(this, m); -} - -bool Session::Initiate(const std::string &to, const SessionDescription *description) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // Only from STATE_INIT - if (state_ != STATE_INIT) - return false; - - // Setup for signaling. Initiate is asynchronous. It occurs once the address - // candidates are ready. - initiator_ = true; - remote_address_ = to; - description_ = description; - SendSessionMessage(SessionMessage::TYPE_INITIATE, description, NULL, NULL); - set_state(Session::STATE_SENTINITIATE); - - // Let the socket manager know we now want the candidates - socket_manager_->StartProcessingCandidates(); - - // Start the session timeout - session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); - session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); - return true; -} - -bool Session::Accept(const SessionDescription *description) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // Only if just received initiate - if (state_ != STATE_RECEIVEDINITIATE) - return false; - - // Setup for signaling. Accept is asynchronous. It occurs once the address - // candidates are ready. - initiator_ = false; - description_ = description; - SendSessionMessage(SessionMessage::TYPE_ACCEPT, description, NULL, NULL); - set_state(Session::STATE_SENTACCEPT); - - return true; -} - -bool Session::Modify(const SessionDescription *description) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // Only if session already STATE_INPROGRESS - if (state_ != STATE_INPROGRESS) - return false; - - // Modify is asynchronous. It occurs once the address candidates are ready. - // Either side can send a modify. It is only valid in an already accepted - // session. - description_ = description; - SendSessionMessage(SessionMessage::TYPE_MODIFY, description, NULL, NULL); - set_state(Session::STATE_SENTMODIFY); - - // Start the session timeout - session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); - session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); - return true; -} - -bool Session::Redirect(const std::string& target) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // Redirect is sent in response to an initiate or modify, to redirect the - // request - if (state_ != STATE_RECEIVEDINITIATE) - return false; - - initiator_ = false; - redirect_target_ = target; - SendSessionMessage(SessionMessage::TYPE_REDIRECT, NULL, NULL, NULL); - - // A redirect puts us in the same state as reject. It just sends a different - // kind of reject message, if you like. - set_state(STATE_SENTREDIRECT); - - return true; -} - -bool Session::Reject() { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // Reject is sent in response to an initiate or modify, to reject the - // request - if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY) - return false; - - initiator_ = false; - SendSessionMessage(SessionMessage::TYPE_REJECT, NULL, NULL, NULL); - set_state(STATE_SENTREJECT); - - return true; -} - -bool Session::Terminate() { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // Either side can terminate, at any time. - if (state_ == STATE_SENTTERMINATE && state_ != STATE_RECEIVEDTERMINATE) - return false; - - // But we don't need to terminate if we already rejected. The other client - // already knows that we're done with this session. - if (state_ != STATE_SENTREDIRECT) - SendSessionMessage(SessionMessage::TYPE_TERMINATE, NULL, NULL, NULL); - - set_state(STATE_SENTTERMINATE); - - return true; -} - -void Session::OnIncomingError(const SessionMessage &m) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - // If a candidate message errors out or gets dropped for some reason we - // ignore the error. - if (m.type() != SessionMessage::TYPE_CANDIDATES) { - set_error(ERROR_RESPONSE); - } -} - -void Session::OnIncomingMessage(const SessionMessage &m) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - - switch (m.type()) { - case SessionMessage::TYPE_INITIATE: - remote_description_ = m.description(); - remote_address_ = m.from(); - name_ = m.name(); - initiator_ = false; - set_state(STATE_RECEIVEDINITIATE); - - // Let the socket manager know we now want the initial candidates - socket_manager_->StartProcessingCandidates(); - break; - - case SessionMessage::TYPE_ACCEPT: - remote_description_ = m.description(); - set_state(STATE_RECEIVEDACCEPT); - break; - - case SessionMessage::TYPE_MODIFY: - remote_description_ = m.description(); - set_state(STATE_RECEIVEDMODIFY); - break; - - case SessionMessage::TYPE_CANDIDATES: - socket_manager_->AddRemoteCandidates(m.candidates()); - break; - - case SessionMessage::TYPE_REJECT: - set_state(STATE_RECEIVEDREJECT); - break; - - case SessionMessage::TYPE_REDIRECT: - OnRedirectMessage(m); - break; - - case SessionMessage::TYPE_TERMINATE: - set_state(STATE_RECEIVEDTERMINATE); - break; - } -} - -void Session::OnRedirectMessage(const SessionMessage &m) { - ASSERT(state_ == STATE_SENTINITIATE); - if (state_ != STATE_SENTINITIATE) - return; - - ASSERT(m.redirect_target().size() != 0); - remote_address_ = m.redirect_target(); - - SendSessionMessage(SessionMessage::TYPE_INITIATE, description_, NULL, - m.redirect_cookie()->Copy()); - - // Restart the session timeout. - session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); - session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); - - // Reset all of the sockets back into the initial state. - socket_manager_->ResetSockets(); -} - -Session::State Session::state() { - return state_; -} - -void Session::set_state(State state) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - if (state != state_) { - state_ = state; - SignalState(this, state); - session_manager_->signaling_thread()->Post(this, MSG_STATE); - } -} - -Session::Error Session::error() { - return error_; -} - -void Session::set_error(Error error) { - ASSERT(session_manager_->signaling_thread()->IsCurrent()); - if (error != error_) { - error_ = error; - SignalError(this, error); - session_manager_->signaling_thread()->Post(this, MSG_ERROR); - } -} - -const std::string &Session::name() { - return name_; -} - -const std::string &Session::remote_address() { - return remote_address_; -} - -bool Session::initiator() { - return initiator_; -} - -const SessionID& Session::id() { - return id_; -} - -const SessionDescription *Session::description() { - return description_; -} - -const SessionDescription *Session::remote_description() { - return remote_description_; -} - -SessionManager *Session::session_manager() { - return session_manager_; -} - -void Session::OnMessage(Message *pmsg) { - switch(pmsg->message_id) { - case MSG_TIMEOUT: - // Session timeout has occured. Check to see if the session is still trying - // to signal. If so, the session has timed out. - // The Sockets have their own timeout for connectivity. - set_error(ERROR_TIME); - break; - - case MSG_ERROR: - switch (error_) { - case ERROR_RESPONSE: - // This state could be reached if we get an error in response to an IQ - // or if the network is so slow we time out on an individual IQ exchange. - // In either case, Terminate (send more messages) and ignore the likely - // cascade of more errors. - - // fall through - case ERROR_NETWORK: - case ERROR_TIME: - // Time ran out - no response - Terminate(); - break; - - default: - break; - } - break; - - case MSG_STATE: - switch (state_) { - case STATE_SENTACCEPT: - case STATE_RECEIVEDACCEPT: - set_state(STATE_INPROGRESS); - session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); - OnSocketState(); // Update the writability timeout state. - break; - - case STATE_SENTREJECT: - case STATE_SENTREDIRECT: - case STATE_RECEIVEDREJECT: - Terminate(); - break; - - case STATE_SENTTERMINATE: - case STATE_RECEIVEDTERMINATE: - session_manager_->DestroySession(this); - break; - - default: - // explicitly ignoring some states here - break; - } - break; - } -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cpp new file mode 100644 index 00000000..73873338 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cpp @@ -0,0 +1,421 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/p2p/base/helpers.h" +#include "talk/p2p/base/session.h" + +namespace cricket { + +const uint32 MSG_TIMEOUT = 1; +const uint32 MSG_ERROR = 2; +const uint32 MSG_STATE = 3; + +Session::Session(SessionManager *session_manager, const std::string &name, + const SessionID& id) { + session_manager_ = session_manager; + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + name_ = name; + id_ = id; + error_ = ERROR_NONE; + state_ = STATE_INIT; + initiator_ = false; + description_ = NULL; + remote_description_ = NULL; + socket_manager_ = new SocketManager(session_manager_); + socket_manager_->SignalCandidatesReady.connect(this, &Session::OnCandidatesReady); + socket_manager_->SignalNetworkError.connect(this, &Session::OnNetworkError); + socket_manager_->SignalState.connect(this, &Session::OnSocketState); + socket_manager_->SignalRequestSignaling.connect(this, &Session::OnRequestSignaling); +} + +Session::~Session() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + delete description_; + delete remote_description_; + delete socket_manager_; + session_manager_->signaling_thread()->Clear(this); +} + +P2PSocket *Session::CreateSocket(const std::string &name) { + return socket_manager_->CreateSocket(name); +} + +void Session::DestroySocket(P2PSocket *socket) { + socket_manager_->DestroySocket(socket); +} + +void Session::OnCandidatesReady(const std::vector& candidates) { + SendSessionMessage(SessionMessage::TYPE_CANDIDATES, NULL, &candidates, NULL); +} + +void Session::OnNetworkError() { + // Socket manager is experiencing a network error trying to allocate + // network resources (usually port allocation) + + set_error(ERROR_NETWORK); +} + +void Session::OnSocketState() { + // If the call is not in progress, then we don't care about writability. + // We have separate timers for making sure we transition back to the in- + // progress state in time. + if (state_ != STATE_INPROGRESS) + return; + + // Put the timer into the write state. This is called when the state changes, + // so we will restart the timer each time we lose writability. + if (socket_manager_->writable()) { + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + } else { + session_manager_->signaling_thread()->PostDelayed( + session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + } +} + +void Session::OnRequestSignaling() { + SignalRequestSignaling(); +} + +void Session::OnSignalingReady() { + socket_manager_->OnSignalingReady(); +} + +void Session::SendSessionMessage(SessionMessage::Type type, + const SessionDescription* description, + const std::vector* candidates, + SessionMessage::Cookie* redirect_cookie) { + SessionMessage m; + m.set_type(type); + m.set_to(remote_address_); + m.set_name(name_); + m.set_description(description); + m.set_session_id(id_); + if (candidates) + m.set_candidates(*candidates); + m.set_redirect_target(redirect_target_); + m.set_redirect_cookie(redirect_cookie); + SignalOutgoingMessage(this, m); +} + +bool Session::Initiate(const std::string &to, const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only from STATE_INIT + if (state_ != STATE_INIT) + return false; + + // Setup for signaling. Initiate is asynchronous. It occurs once the address + // candidates are ready. + initiator_ = true; + remote_address_ = to; + description_ = description; + SendSessionMessage(SessionMessage::TYPE_INITIATE, description, NULL, NULL); + set_state(Session::STATE_SENTINITIATE); + + // Let the socket manager know we now want the candidates + socket_manager_->StartProcessingCandidates(); + + // Start the session timeout + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + return true; +} + +bool Session::Accept(const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only if just received initiate + if (state_ != STATE_RECEIVEDINITIATE) + return false; + + // Setup for signaling. Accept is asynchronous. It occurs once the address + // candidates are ready. + initiator_ = false; + description_ = description; + SendSessionMessage(SessionMessage::TYPE_ACCEPT, description, NULL, NULL); + set_state(Session::STATE_SENTACCEPT); + + return true; +} + +bool Session::Modify(const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only if session already STATE_INPROGRESS + if (state_ != STATE_INPROGRESS) + return false; + + // Modify is asynchronous. It occurs once the address candidates are ready. + // Either side can send a modify. It is only valid in an already accepted + // session. + description_ = description; + SendSessionMessage(SessionMessage::TYPE_MODIFY, description, NULL, NULL); + set_state(Session::STATE_SENTMODIFY); + + // Start the session timeout + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + return true; +} + +bool Session::Redirect(const std::string& target) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Redirect is sent in response to an initiate or modify, to redirect the + // request + if (state_ != STATE_RECEIVEDINITIATE) + return false; + + initiator_ = false; + redirect_target_ = target; + SendSessionMessage(SessionMessage::TYPE_REDIRECT, NULL, NULL, NULL); + + // A redirect puts us in the same state as reject. It just sends a different + // kind of reject message, if you like. + set_state(STATE_SENTREDIRECT); + + return true; +} + +bool Session::Reject() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Reject is sent in response to an initiate or modify, to reject the + // request + if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY) + return false; + + initiator_ = false; + SendSessionMessage(SessionMessage::TYPE_REJECT, NULL, NULL, NULL); + set_state(STATE_SENTREJECT); + + return true; +} + +bool Session::Terminate() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Either side can terminate, at any time. + if (state_ == STATE_SENTTERMINATE && state_ != STATE_RECEIVEDTERMINATE) + return false; + + // But we don't need to terminate if we already rejected. The other client + // already knows that we're done with this session. + if (state_ != STATE_SENTREDIRECT) + SendSessionMessage(SessionMessage::TYPE_TERMINATE, NULL, NULL, NULL); + + set_state(STATE_SENTTERMINATE); + + return true; +} + +void Session::OnIncomingError(const SessionMessage &m) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // If a candidate message errors out or gets dropped for some reason we + // ignore the error. + if (m.type() != SessionMessage::TYPE_CANDIDATES) { + set_error(ERROR_RESPONSE); + } +} + +void Session::OnIncomingMessage(const SessionMessage &m) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + switch (m.type()) { + case SessionMessage::TYPE_INITIATE: + remote_description_ = m.description(); + remote_address_ = m.from(); + name_ = m.name(); + initiator_ = false; + set_state(STATE_RECEIVEDINITIATE); + + // Let the socket manager know we now want the initial candidates + socket_manager_->StartProcessingCandidates(); + break; + + case SessionMessage::TYPE_ACCEPT: + remote_description_ = m.description(); + set_state(STATE_RECEIVEDACCEPT); + break; + + case SessionMessage::TYPE_MODIFY: + remote_description_ = m.description(); + set_state(STATE_RECEIVEDMODIFY); + break; + + case SessionMessage::TYPE_CANDIDATES: + socket_manager_->AddRemoteCandidates(m.candidates()); + break; + + case SessionMessage::TYPE_REJECT: + set_state(STATE_RECEIVEDREJECT); + break; + + case SessionMessage::TYPE_REDIRECT: + OnRedirectMessage(m); + break; + + case SessionMessage::TYPE_TERMINATE: + set_state(STATE_RECEIVEDTERMINATE); + break; + } +} + +void Session::OnRedirectMessage(const SessionMessage &m) { + ASSERT(state_ == STATE_SENTINITIATE); + if (state_ != STATE_SENTINITIATE) + return; + + ASSERT(m.redirect_target().size() != 0); + remote_address_ = m.redirect_target(); + + SendSessionMessage(SessionMessage::TYPE_INITIATE, description_, NULL, + m.redirect_cookie()->Copy()); + + // Restart the session timeout. + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + + // Reset all of the sockets back into the initial state. + socket_manager_->ResetSockets(); +} + +Session::State Session::state() { + return state_; +} + +void Session::set_state(State state) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (state != state_) { + state_ = state; + SignalState(this, state); + session_manager_->signaling_thread()->Post(this, MSG_STATE); + } +} + +Session::Error Session::error() { + return error_; +} + +void Session::set_error(Error error) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (error != error_) { + error_ = error; + SignalError(this, error); + session_manager_->signaling_thread()->Post(this, MSG_ERROR); + } +} + +const std::string &Session::name() { + return name_; +} + +const std::string &Session::remote_address() { + return remote_address_; +} + +bool Session::initiator() { + return initiator_; +} + +const SessionID& Session::id() { + return id_; +} + +const SessionDescription *Session::description() { + return description_; +} + +const SessionDescription *Session::remote_description() { + return remote_description_; +} + +SessionManager *Session::session_manager() { + return session_manager_; +} + +void Session::OnMessage(Message *pmsg) { + switch(pmsg->message_id) { + case MSG_TIMEOUT: + // Session timeout has occured. Check to see if the session is still trying + // to signal. If so, the session has timed out. + // The Sockets have their own timeout for connectivity. + set_error(ERROR_TIME); + break; + + case MSG_ERROR: + switch (error_) { + case ERROR_RESPONSE: + // This state could be reached if we get an error in response to an IQ + // or if the network is so slow we time out on an individual IQ exchange. + // In either case, Terminate (send more messages) and ignore the likely + // cascade of more errors. + + // fall through + case ERROR_NETWORK: + case ERROR_TIME: + // Time ran out - no response + Terminate(); + break; + + default: + break; + } + break; + + case MSG_STATE: + switch (state_) { + case STATE_SENTACCEPT: + case STATE_RECEIVEDACCEPT: + set_state(STATE_INPROGRESS); + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + OnSocketState(); // Update the writability timeout state. + break; + + case STATE_SENTREJECT: + case STATE_SENTREDIRECT: + case STATE_RECEIVEDREJECT: + Terminate(); + break; + + case STATE_SENTTERMINATE: + case STATE_RECEIVEDTERMINATE: + session_manager_->DestroySession(this); + break; + + default: + // explicitly ignoring some states here + break; + } + break; + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc deleted file mode 100644 index 4c1c09d9..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/common.h" -#include "talk/p2p/base/helpers.h" -#include "sessionmanager.h" - -namespace cricket { - -SessionManager::SessionManager(PortAllocator *allocator, Thread *worker) { - allocator_ = allocator; - signaling_thread_ = Thread::Current(); - if (worker == NULL) { - worker_thread_ = Thread::Current(); - } else { - worker_thread_ = worker; - } - timeout_ = 50; -} - -SessionManager::~SessionManager() { - // Note: Session::Terminate occurs asynchronously, so it's too late to - // delete them now. They better be all gone. - ASSERT(session_map_.empty()); - //TerminateAll(); -} - -Session *SessionManager::CreateSession(const std::string &name, const std::string& initiator) { - return CreateSession(name, SessionID(initiator, CreateRandomId()), false); -} - -Session *SessionManager::CreateSession(const std::string &name, const SessionID& id, bool received_initiate) { - Session *session = new Session(this, name, id); - session_map_[session->id()] = session; - session->SignalRequestSignaling.connect(this, &SessionManager::OnRequestSignaling); - SignalSessionCreate(session, received_initiate); - return session; -} - -void SessionManager::DestroySession(Session *session) { - if (session != NULL) { - std::map::iterator it = session_map_.find(session->id()); - if (it != session_map_.end()) { - SignalSessionDestroy(session); - session_map_.erase(it); - delete session; - } - } -} - -Session *SessionManager::GetSession(const SessionID& id) { - // If the id isn't present, the [] operator will make a NULL entry - std::map::iterator it = session_map_.find(id); - if (it != session_map_.end()) - return (*it).second; - return NULL; -} - -void SessionManager::TerminateAll() { - while (session_map_.begin() != session_map_.end()) { - Session *session = session_map_.begin()->second; - session->Terminate(); - } -} - -void SessionManager::OnIncomingError(const SessionMessage &m) { - // Incoming signaling error. This means, as the result of trying - // to send message m, and error was generated. In all cases, a - // session should already exist - - Session *session; - switch (m.type()) { - case SessionMessage::TYPE_INITIATE: - case SessionMessage::TYPE_ACCEPT: - case SessionMessage::TYPE_MODIFY: - case SessionMessage::TYPE_CANDIDATES: - case SessionMessage::TYPE_REJECT: - case SessionMessage::TYPE_TERMINATE: - session = GetSession(m.session_id()); - break; - - default: - return; - } - - if (session != NULL) - session->OnIncomingError(m); - -} - -void SessionManager::OnIncomingMessage(const SessionMessage &m) { - // In the case of an incoming initiate, there is no session yet, and one needs to be created. - // The other cases have sessions already. - - Session *session; - switch (m.type()) { - case SessionMessage::TYPE_INITIATE: - session = CreateSession(m.name(), m.session_id(), true); - break; - - case SessionMessage::TYPE_ACCEPT: - case SessionMessage::TYPE_MODIFY: - case SessionMessage::TYPE_CANDIDATES: - case SessionMessage::TYPE_REJECT: - case SessionMessage::TYPE_REDIRECT: - case SessionMessage::TYPE_TERMINATE: - session = GetSession(m.session_id()); - break; - - default: - return; - } - - if (session != NULL) - session->OnIncomingMessage(m); -} - -void SessionManager::OnSignalingReady() { - for (std::map::iterator it = session_map_.begin(); - it != session_map_.end(); ++it) { - it->second->OnSignalingReady(); - } -} - -void SessionManager::OnRequestSignaling() { - SignalRequestSignaling(); -} - -PortAllocator *SessionManager::port_allocator() const { - return allocator_; -} - -Thread *SessionManager::worker_thread() const { - return worker_thread_; -} - -Thread *SessionManager::signaling_thread() const { - return signaling_thread_; -} - -int SessionManager::session_timeout() { - return timeout_; -} - -void SessionManager::set_session_timeout(int timeout) { - timeout_ = timeout; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cpp new file mode 100644 index 00000000..4c1c09d9 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cpp @@ -0,0 +1,173 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/common.h" +#include "talk/p2p/base/helpers.h" +#include "sessionmanager.h" + +namespace cricket { + +SessionManager::SessionManager(PortAllocator *allocator, Thread *worker) { + allocator_ = allocator; + signaling_thread_ = Thread::Current(); + if (worker == NULL) { + worker_thread_ = Thread::Current(); + } else { + worker_thread_ = worker; + } + timeout_ = 50; +} + +SessionManager::~SessionManager() { + // Note: Session::Terminate occurs asynchronously, so it's too late to + // delete them now. They better be all gone. + ASSERT(session_map_.empty()); + //TerminateAll(); +} + +Session *SessionManager::CreateSession(const std::string &name, const std::string& initiator) { + return CreateSession(name, SessionID(initiator, CreateRandomId()), false); +} + +Session *SessionManager::CreateSession(const std::string &name, const SessionID& id, bool received_initiate) { + Session *session = new Session(this, name, id); + session_map_[session->id()] = session; + session->SignalRequestSignaling.connect(this, &SessionManager::OnRequestSignaling); + SignalSessionCreate(session, received_initiate); + return session; +} + +void SessionManager::DestroySession(Session *session) { + if (session != NULL) { + std::map::iterator it = session_map_.find(session->id()); + if (it != session_map_.end()) { + SignalSessionDestroy(session); + session_map_.erase(it); + delete session; + } + } +} + +Session *SessionManager::GetSession(const SessionID& id) { + // If the id isn't present, the [] operator will make a NULL entry + std::map::iterator it = session_map_.find(id); + if (it != session_map_.end()) + return (*it).second; + return NULL; +} + +void SessionManager::TerminateAll() { + while (session_map_.begin() != session_map_.end()) { + Session *session = session_map_.begin()->second; + session->Terminate(); + } +} + +void SessionManager::OnIncomingError(const SessionMessage &m) { + // Incoming signaling error. This means, as the result of trying + // to send message m, and error was generated. In all cases, a + // session should already exist + + Session *session; + switch (m.type()) { + case SessionMessage::TYPE_INITIATE: + case SessionMessage::TYPE_ACCEPT: + case SessionMessage::TYPE_MODIFY: + case SessionMessage::TYPE_CANDIDATES: + case SessionMessage::TYPE_REJECT: + case SessionMessage::TYPE_TERMINATE: + session = GetSession(m.session_id()); + break; + + default: + return; + } + + if (session != NULL) + session->OnIncomingError(m); + +} + +void SessionManager::OnIncomingMessage(const SessionMessage &m) { + // In the case of an incoming initiate, there is no session yet, and one needs to be created. + // The other cases have sessions already. + + Session *session; + switch (m.type()) { + case SessionMessage::TYPE_INITIATE: + session = CreateSession(m.name(), m.session_id(), true); + break; + + case SessionMessage::TYPE_ACCEPT: + case SessionMessage::TYPE_MODIFY: + case SessionMessage::TYPE_CANDIDATES: + case SessionMessage::TYPE_REJECT: + case SessionMessage::TYPE_REDIRECT: + case SessionMessage::TYPE_TERMINATE: + session = GetSession(m.session_id()); + break; + + default: + return; + } + + if (session != NULL) + session->OnIncomingMessage(m); +} + +void SessionManager::OnSignalingReady() { + for (std::map::iterator it = session_map_.begin(); + it != session_map_.end(); ++it) { + it->second->OnSignalingReady(); + } +} + +void SessionManager::OnRequestSignaling() { + SignalRequestSignaling(); +} + +PortAllocator *SessionManager::port_allocator() const { + return allocator_; +} + +Thread *SessionManager::worker_thread() const { + return worker_thread_; +} + +Thread *SessionManager::signaling_thread() const { + return signaling_thread_; +} + +int SessionManager::session_timeout() { + return timeout_; +} + +void SessionManager::set_session_timeout(int timeout) { + timeout_ = timeout; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc deleted file mode 100644 index 2f0d67b8..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc +++ /dev/null @@ -1,273 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "socketmanager.h" -#include - -namespace cricket { - -const uint32 MSG_CREATESOCKET = 1; -const uint32 MSG_DESTROYSOCKET = 2; -const uint32 MSG_ONSIGNALINGREADY = 3; -const uint32 MSG_CANDIDATESREADY = 4; -const uint32 MSG_ADDREMOTECANDIDATES = 5; -const uint32 MSG_ONREQUESTSIGNALING = 6; -const uint32 MSG_RESETSOCKETS = 7; - -struct CreateParams { - CreateParams() {} - P2PSocket *socket; - std::string name; -}; - -SocketManager::SocketManager(SessionManager *session_manager) { - session_manager_ = session_manager; - candidates_requested_ = false; - writable_ = false; -} - -SocketManager::~SocketManager() { - assert(Thread::Current() == session_manager_->signaling_thread()); - - // Are the sockets destroyed? If not, destroy them - - critSM_.Enter(); - while (sockets_.size() != 0) { - P2PSocket *socket = sockets_[0]; - critSM_.Leave(); - DestroySocket(socket); - critSM_.Enter(); - } - critSM_.Leave(); - - // Clear queues - - session_manager_->signaling_thread()->Clear(this); - session_manager_->worker_thread()->Clear(this); -} - -P2PSocket *SocketManager::CreateSocket(const std::string &name) { - // Can occur on any thread - CreateParams params; - params.name = name; - params.socket = NULL; - TypedMessageData data(¶ms); - session_manager_->worker_thread()->Send(this, MSG_CREATESOCKET, &data); - return data.data()->socket; -} - -P2PSocket *SocketManager::CreateSocket_w(const std::string &name) { - // Only on worker thread - assert(Thread::Current() == session_manager_->worker_thread()); - CritScope cs(&critSM_); - P2PSocket *socket = new P2PSocket(name, session_manager_->port_allocator()); - socket->SignalCandidatesReady.connect(this, &SocketManager::OnCandidatesReady); - socket->SignalState.connect(this, &SocketManager::OnSocketState); - socket->SignalRequestSignaling.connect(this, &SocketManager::OnRequestSignaling); - sockets_.push_back(socket); - socket->StartProcessingCandidates(); - return socket; -} - -void SocketManager::DestroySocket(P2PSocket *socket) { - // Can occur on any thread - TypedMessageData data(socket); - session_manager_->worker_thread()->Send(this, MSG_DESTROYSOCKET, &data); -} - -void SocketManager::DestroySocket_w(P2PSocket *socket) { - // Only on worker thread - assert(Thread::Current() == session_manager_->worker_thread()); - - // Only if socket exists - CritScope cs(&critSM_); - std::vector::iterator it; - it = std::find(sockets_.begin(), sockets_.end(), socket); - if (it == sockets_.end()) - return; - sockets_.erase(it); - delete socket; -} - -void SocketManager::StartProcessingCandidates() { - // Only on signaling thread - assert(Thread::Current() == session_manager_->signaling_thread()); - - // When sockets are created, their candidates are requested. - // When the candidates are ready, the client is signaled - // on the signaling thread - candidates_requested_ = true; - session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY); -} - -void SocketManager::OnSignalingReady() { - session_manager_->worker_thread()->Post(this, MSG_ONSIGNALINGREADY); -} - -void SocketManager::OnSignalingReady_w() { - // Only on worker thread - assert(Thread::Current() == session_manager_->worker_thread()); - for (uint32 i = 0; i < sockets_.size(); ++i) { - sockets_[i]->OnSignalingReady(); - } -} - -void SocketManager::OnCandidatesReady( - P2PSocket *socket, const std::vector& candidates) { - // Only on worker thread - assert(Thread::Current() == session_manager_->worker_thread()); - - // Remember candidates - CritScope cs(&critSM_); - std::vector::const_iterator it; - for (it = candidates.begin(); it != candidates.end(); it++) - candidates_.push_back(*it); - - // If candidates requested, tell signaling thread - if (candidates_requested_) - session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY); -} - -void SocketManager::ResetSockets() { - assert(Thread::Current() == session_manager_->signaling_thread()); - session_manager_->worker_thread()->Post(this, MSG_RESETSOCKETS); -} - -void SocketManager::ResetSockets_w() { - assert(Thread::Current() == session_manager_->worker_thread()); - - for (size_t i = 0; i < sockets_.size(); ++i) - sockets_[i]->Reset(); -} - -void SocketManager::OnSocketState(P2PSocket* socket, P2PSocket::State state) { - assert(Thread::Current() == session_manager_->worker_thread()); - - bool writable = false; - for (uint32 i = 0; i < sockets_.size(); ++i) - if (sockets_[i]->writable()) - writable = true; - - if (writable_ != writable) { - writable_ = writable; - SignalState(); - } -} - -void SocketManager::OnRequestSignaling() { - assert(Thread::Current() == session_manager_->worker_thread()); - session_manager_->signaling_thread()->Post(this, MSG_ONREQUESTSIGNALING); -} - - -void SocketManager::AddRemoteCandidates(const std::vector &remote_candidates) { - assert(Thread::Current() == session_manager_->signaling_thread()); - TypedMessageData > *data = new TypedMessageData >(remote_candidates); - session_manager_->worker_thread()->Post(this, MSG_ADDREMOTECANDIDATES, data); -} - -void SocketManager::AddRemoteCandidates_w(const std::vector &remote_candidates) { - assert(Thread::Current() == session_manager_->worker_thread()); - - // Local and remote candidates now exist, so connectivity checking can - // commence. Tell the P2PSockets about the remote candidates. - // Group candidates by socket name - - CritScope cs(&critSM_); - std::vector::iterator it_socket; - for (it_socket = sockets_.begin(); it_socket != sockets_.end(); it_socket++) { - // Create a vector of remote candidates for each socket - std::string name = (*it_socket)->name(); - std::vector candidate_bundle; - std::vector::const_iterator it_candidate; - for (it_candidate = remote_candidates.begin(); it_candidate != remote_candidates.end(); it_candidate++) { - if ((*it_candidate).name() == name) - candidate_bundle.push_back(*it_candidate); - } - if (candidate_bundle.size() != 0) - (*it_socket)->AddRemoteCandidates(candidate_bundle); - } -} - -void SocketManager::OnMessage(Message *message) { - switch (message->message_id) { - case MSG_CREATESOCKET: - { - assert(Thread::Current() == session_manager_->worker_thread()); - TypedMessageData *params = static_cast *>(message->pdata); - params->data()->socket = CreateSocket_w(params->data()->name); - } - break; - - case MSG_DESTROYSOCKET: - { - assert(Thread::Current() == session_manager_->worker_thread()); - TypedMessageData *data = static_cast *>(message->pdata); - DestroySocket_w(data->data()); - } - break; - - case MSG_ONSIGNALINGREADY: - assert(Thread::Current() == session_manager_->worker_thread()); - OnSignalingReady_w(); - break; - - case MSG_ONREQUESTSIGNALING: - assert(Thread::Current() == session_manager_->signaling_thread()); - SignalRequestSignaling(); - break; - - case MSG_CANDIDATESREADY: - assert(Thread::Current() == session_manager_->signaling_thread()); - if (candidates_requested_) { - CritScope cs(&critSM_); - if (candidates_.size() > 0) { - SignalCandidatesReady(candidates_); - candidates_.clear(); - } - } - break; - - case MSG_ADDREMOTECANDIDATES: - { - assert(Thread::Current() == session_manager_->worker_thread()); - TypedMessageData > *data = static_cast > *>(message->pdata); - AddRemoteCandidates_w(data->data()); - delete data; - } - break; - - case MSG_RESETSOCKETS: - ResetSockets_w(); - break; - } -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cpp new file mode 100644 index 00000000..2f0d67b8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cpp @@ -0,0 +1,273 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "socketmanager.h" +#include + +namespace cricket { + +const uint32 MSG_CREATESOCKET = 1; +const uint32 MSG_DESTROYSOCKET = 2; +const uint32 MSG_ONSIGNALINGREADY = 3; +const uint32 MSG_CANDIDATESREADY = 4; +const uint32 MSG_ADDREMOTECANDIDATES = 5; +const uint32 MSG_ONREQUESTSIGNALING = 6; +const uint32 MSG_RESETSOCKETS = 7; + +struct CreateParams { + CreateParams() {} + P2PSocket *socket; + std::string name; +}; + +SocketManager::SocketManager(SessionManager *session_manager) { + session_manager_ = session_manager; + candidates_requested_ = false; + writable_ = false; +} + +SocketManager::~SocketManager() { + assert(Thread::Current() == session_manager_->signaling_thread()); + + // Are the sockets destroyed? If not, destroy them + + critSM_.Enter(); + while (sockets_.size() != 0) { + P2PSocket *socket = sockets_[0]; + critSM_.Leave(); + DestroySocket(socket); + critSM_.Enter(); + } + critSM_.Leave(); + + // Clear queues + + session_manager_->signaling_thread()->Clear(this); + session_manager_->worker_thread()->Clear(this); +} + +P2PSocket *SocketManager::CreateSocket(const std::string &name) { + // Can occur on any thread + CreateParams params; + params.name = name; + params.socket = NULL; + TypedMessageData data(¶ms); + session_manager_->worker_thread()->Send(this, MSG_CREATESOCKET, &data); + return data.data()->socket; +} + +P2PSocket *SocketManager::CreateSocket_w(const std::string &name) { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + CritScope cs(&critSM_); + P2PSocket *socket = new P2PSocket(name, session_manager_->port_allocator()); + socket->SignalCandidatesReady.connect(this, &SocketManager::OnCandidatesReady); + socket->SignalState.connect(this, &SocketManager::OnSocketState); + socket->SignalRequestSignaling.connect(this, &SocketManager::OnRequestSignaling); + sockets_.push_back(socket); + socket->StartProcessingCandidates(); + return socket; +} + +void SocketManager::DestroySocket(P2PSocket *socket) { + // Can occur on any thread + TypedMessageData data(socket); + session_manager_->worker_thread()->Send(this, MSG_DESTROYSOCKET, &data); +} + +void SocketManager::DestroySocket_w(P2PSocket *socket) { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + + // Only if socket exists + CritScope cs(&critSM_); + std::vector::iterator it; + it = std::find(sockets_.begin(), sockets_.end(), socket); + if (it == sockets_.end()) + return; + sockets_.erase(it); + delete socket; +} + +void SocketManager::StartProcessingCandidates() { + // Only on signaling thread + assert(Thread::Current() == session_manager_->signaling_thread()); + + // When sockets are created, their candidates are requested. + // When the candidates are ready, the client is signaled + // on the signaling thread + candidates_requested_ = true; + session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY); +} + +void SocketManager::OnSignalingReady() { + session_manager_->worker_thread()->Post(this, MSG_ONSIGNALINGREADY); +} + +void SocketManager::OnSignalingReady_w() { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + for (uint32 i = 0; i < sockets_.size(); ++i) { + sockets_[i]->OnSignalingReady(); + } +} + +void SocketManager::OnCandidatesReady( + P2PSocket *socket, const std::vector& candidates) { + // Only on worker thread + assert(Thread::Current() == session_manager_->worker_thread()); + + // Remember candidates + CritScope cs(&critSM_); + std::vector::const_iterator it; + for (it = candidates.begin(); it != candidates.end(); it++) + candidates_.push_back(*it); + + // If candidates requested, tell signaling thread + if (candidates_requested_) + session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY); +} + +void SocketManager::ResetSockets() { + assert(Thread::Current() == session_manager_->signaling_thread()); + session_manager_->worker_thread()->Post(this, MSG_RESETSOCKETS); +} + +void SocketManager::ResetSockets_w() { + assert(Thread::Current() == session_manager_->worker_thread()); + + for (size_t i = 0; i < sockets_.size(); ++i) + sockets_[i]->Reset(); +} + +void SocketManager::OnSocketState(P2PSocket* socket, P2PSocket::State state) { + assert(Thread::Current() == session_manager_->worker_thread()); + + bool writable = false; + for (uint32 i = 0; i < sockets_.size(); ++i) + if (sockets_[i]->writable()) + writable = true; + + if (writable_ != writable) { + writable_ = writable; + SignalState(); + } +} + +void SocketManager::OnRequestSignaling() { + assert(Thread::Current() == session_manager_->worker_thread()); + session_manager_->signaling_thread()->Post(this, MSG_ONREQUESTSIGNALING); +} + + +void SocketManager::AddRemoteCandidates(const std::vector &remote_candidates) { + assert(Thread::Current() == session_manager_->signaling_thread()); + TypedMessageData > *data = new TypedMessageData >(remote_candidates); + session_manager_->worker_thread()->Post(this, MSG_ADDREMOTECANDIDATES, data); +} + +void SocketManager::AddRemoteCandidates_w(const std::vector &remote_candidates) { + assert(Thread::Current() == session_manager_->worker_thread()); + + // Local and remote candidates now exist, so connectivity checking can + // commence. Tell the P2PSockets about the remote candidates. + // Group candidates by socket name + + CritScope cs(&critSM_); + std::vector::iterator it_socket; + for (it_socket = sockets_.begin(); it_socket != sockets_.end(); it_socket++) { + // Create a vector of remote candidates for each socket + std::string name = (*it_socket)->name(); + std::vector candidate_bundle; + std::vector::const_iterator it_candidate; + for (it_candidate = remote_candidates.begin(); it_candidate != remote_candidates.end(); it_candidate++) { + if ((*it_candidate).name() == name) + candidate_bundle.push_back(*it_candidate); + } + if (candidate_bundle.size() != 0) + (*it_socket)->AddRemoteCandidates(candidate_bundle); + } +} + +void SocketManager::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CREATESOCKET: + { + assert(Thread::Current() == session_manager_->worker_thread()); + TypedMessageData *params = static_cast *>(message->pdata); + params->data()->socket = CreateSocket_w(params->data()->name); + } + break; + + case MSG_DESTROYSOCKET: + { + assert(Thread::Current() == session_manager_->worker_thread()); + TypedMessageData *data = static_cast *>(message->pdata); + DestroySocket_w(data->data()); + } + break; + + case MSG_ONSIGNALINGREADY: + assert(Thread::Current() == session_manager_->worker_thread()); + OnSignalingReady_w(); + break; + + case MSG_ONREQUESTSIGNALING: + assert(Thread::Current() == session_manager_->signaling_thread()); + SignalRequestSignaling(); + break; + + case MSG_CANDIDATESREADY: + assert(Thread::Current() == session_manager_->signaling_thread()); + if (candidates_requested_) { + CritScope cs(&critSM_); + if (candidates_.size() > 0) { + SignalCandidatesReady(candidates_); + candidates_.clear(); + } + } + break; + + case MSG_ADDREMOTECANDIDATES: + { + assert(Thread::Current() == session_manager_->worker_thread()); + TypedMessageData > *data = static_cast > *>(message->pdata); + AddRemoteCandidates_w(data->data()); + delete data; + } + break; + + case MSG_RESETSOCKETS: + ResetSockets_w(); + break; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc deleted file mode 100644 index 2d6aa67c..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc +++ /dev/null @@ -1,577 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/logging.h" -#include "talk/p2p/base/stun.h" -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::memcpy; -} -#endif - -namespace cricket { - -const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST"; -const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED"; -const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE"; -const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS"; -const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE"; -const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME"; -const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS"; -const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR"; -const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE"; - -StunMessage::StunMessage() : type_(0), length_(0), - transaction_id_("0000000000000000") { - assert(transaction_id_.size() == 16); - attrs_ = new std::vector(); -} - -StunMessage::~StunMessage() { - for (unsigned i = 0; i < attrs_->size(); i++) - delete (*attrs_)[i]; - delete attrs_; -} - -void StunMessage::SetTransactionID(const std::string& str) { - assert(str.size() == 16); - transaction_id_ = str; -} - -void StunMessage::AddAttribute(StunAttribute* attr) { - attrs_->push_back(attr); - length_ += attr->length() + 4; -} - -const StunAddressAttribute* -StunMessage::GetAddress(StunAttributeType type) const { - switch (type) { - case STUN_ATTR_MAPPED_ADDRESS: - case STUN_ATTR_RESPONSE_ADDRESS: - case STUN_ATTR_SOURCE_ADDRESS: - case STUN_ATTR_CHANGED_ADDRESS: - case STUN_ATTR_REFLECTED_FROM: - case STUN_ATTR_ALTERNATE_SERVER: - case STUN_ATTR_DESTINATION_ADDRESS: - case STUN_ATTR_SOURCE_ADDRESS2: - return reinterpret_cast(GetAttribute(type)); - - default: - assert(0); - return 0; - } -} - -const StunUInt32Attribute* -StunMessage::GetUInt32(StunAttributeType type) const { - switch (type) { - case STUN_ATTR_CHANGE_REQUEST: - case STUN_ATTR_LIFETIME: - case STUN_ATTR_BANDWIDTH: - case STUN_ATTR_OPTIONS: - return reinterpret_cast(GetAttribute(type)); - - default: - assert(0); - return 0; - } -} - -const StunByteStringAttribute* -StunMessage::GetByteString(StunAttributeType type) const { - switch (type) { - case STUN_ATTR_USERNAME: - case STUN_ATTR_PASSWORD: - case STUN_ATTR_MESSAGE_INTEGRITY: - case STUN_ATTR_DATA: - case STUN_ATTR_MAGIC_COOKIE: - return reinterpret_cast(GetAttribute(type)); - - default: - assert(0); - return 0; - } -} - -const StunErrorCodeAttribute* StunMessage::GetErrorCode() const { - return reinterpret_cast( - GetAttribute(STUN_ATTR_ERROR_CODE)); -} - -const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const { - return reinterpret_cast( - GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES)); -} - -const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const { - return reinterpret_cast( - GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES)); -} - -const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const { - for (unsigned i = 0; i < attrs_->size(); i++) { - if ((*attrs_)[i]->type() == type) - return (*attrs_)[i]; - } - return 0; -} - -bool StunMessage::Read(ByteBuffer* buf) { - if (!buf->ReadUInt16(type_)) - return false; - - if (!buf->ReadUInt16(length_)) - return false; - - std::string transaction_id; - if (!buf->ReadString(transaction_id, 16)) - return false; - assert(transaction_id.size() == 16); - transaction_id_ = transaction_id; - - if (length_ > buf->Length()) - return false; - - attrs_->resize(0); - - size_t rest = buf->Length() - length_; - while (buf->Length() > rest) { - uint16 attr_type, attr_length; - if (!buf->ReadUInt16(attr_type)) - return false; - if (!buf->ReadUInt16(attr_length)) - return false; - - StunAttribute* attr = StunAttribute::Create(attr_type, attr_length); - if (!attr || !attr->Read(buf)) - return false; - - attrs_->push_back(attr); - } - - if (buf->Length() != rest) { - // fixme: shouldn't be doing this - LOG(LERROR) << "wrong message length" - << " (" << (int)rest << " != " << (int)buf->Length() << ")"; - return false; - } - - return true; -} - -void StunMessage::Write(ByteBuffer* buf) const { - buf->WriteUInt16(type_); - buf->WriteUInt16(length_); - buf->WriteString(transaction_id_); - - for (unsigned i = 0; i < attrs_->size(); i++) { - buf->WriteUInt16((*attrs_)[i]->type()); - buf->WriteUInt16((*attrs_)[i]->length()); - (*attrs_)[i]->Write(buf); - } -} - -StunAttribute::StunAttribute(uint16 type, uint16 length) - : type_(type), length_(length) { -} - -StunAttribute* StunAttribute::Create(uint16 type, uint16 length) { - switch (type) { - case STUN_ATTR_MAPPED_ADDRESS: - case STUN_ATTR_RESPONSE_ADDRESS: - case STUN_ATTR_SOURCE_ADDRESS: - case STUN_ATTR_CHANGED_ADDRESS: - case STUN_ATTR_REFLECTED_FROM: - case STUN_ATTR_ALTERNATE_SERVER: - case STUN_ATTR_DESTINATION_ADDRESS: - case STUN_ATTR_SOURCE_ADDRESS2: - if (length != StunAddressAttribute::SIZE) - return 0; - return new StunAddressAttribute(type); - - case STUN_ATTR_CHANGE_REQUEST: - case STUN_ATTR_LIFETIME: - case STUN_ATTR_BANDWIDTH: - case STUN_ATTR_OPTIONS: - if (length != StunUInt32Attribute::SIZE) - return 0; - return new StunUInt32Attribute(type); - - case STUN_ATTR_USERNAME: - case STUN_ATTR_PASSWORD: - case STUN_ATTR_MAGIC_COOKIE: - return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0; - - case STUN_ATTR_MESSAGE_INTEGRITY: - return (length == 20) ? new StunByteStringAttribute(type, length) : 0; - - case STUN_ATTR_DATA: - return new StunByteStringAttribute(type, length); - - case STUN_ATTR_ERROR_CODE: - if (length < StunErrorCodeAttribute::MIN_SIZE) - return 0; - return new StunErrorCodeAttribute(type, length); - - case STUN_ATTR_UNKNOWN_ATTRIBUTES: - return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0; - - case STUN_ATTR_TRANSPORT_PREFERENCES: - if ((length != StunTransportPrefsAttribute::SIZE1) && - (length != StunTransportPrefsAttribute::SIZE2)) - return 0; - return new StunTransportPrefsAttribute(type, length); - - default: - return 0; - } -} - -StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) { - switch (type) { - case STUN_ATTR_MAPPED_ADDRESS: - case STUN_ATTR_RESPONSE_ADDRESS: - case STUN_ATTR_SOURCE_ADDRESS: - case STUN_ATTR_CHANGED_ADDRESS: - case STUN_ATTR_REFLECTED_FROM: - case STUN_ATTR_ALTERNATE_SERVER: - case STUN_ATTR_DESTINATION_ADDRESS: - case STUN_ATTR_SOURCE_ADDRESS2: - return new StunAddressAttribute(type); - - default: - assert(false); - return 0; - } -} - -StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) { - switch (type) { - case STUN_ATTR_CHANGE_REQUEST: - case STUN_ATTR_LIFETIME: - case STUN_ATTR_BANDWIDTH: - case STUN_ATTR_OPTIONS: - return new StunUInt32Attribute(type); - - default: - assert(false); - return 0; - } -} - -StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) { - switch (type) { - case STUN_ATTR_USERNAME: - case STUN_ATTR_PASSWORD: - case STUN_ATTR_MESSAGE_INTEGRITY: - case STUN_ATTR_DATA: - case STUN_ATTR_MAGIC_COOKIE: - return new StunByteStringAttribute(type, 0); - - default: - assert(false); - return 0; - } -} - -StunErrorCodeAttribute* StunAttribute::CreateErrorCode() { - return new StunErrorCodeAttribute( - STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE); -} - -StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() { - return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0); -} - -StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() { - return new StunTransportPrefsAttribute( - STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1); -} - -StunAddressAttribute::StunAddressAttribute(uint16 type) - : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) { -} - -bool StunAddressAttribute::Read(ByteBuffer* buf) { - uint8 dummy; - if (!buf->ReadUInt8(dummy)) - return false; - if (!buf->ReadUInt8(family_)) - return false; - if (!buf->ReadUInt16(port_)) - return false; - if (!buf->ReadUInt32(ip_)) - return false; - return true; -} - -void StunAddressAttribute::Write(ByteBuffer* buf) const { - buf->WriteUInt8(0); - buf->WriteUInt8(family_); - buf->WriteUInt16(port_); - buf->WriteUInt32(ip_); -} - -StunUInt32Attribute::StunUInt32Attribute(uint16 type) - : StunAttribute(type, SIZE), bits_(0) { -} - -bool StunUInt32Attribute::GetBit(int index) const { - assert((0 <= index) && (index < 32)); - return static_cast((bits_ >> index) & 0x1); -} - -void StunUInt32Attribute::SetBit(int index, bool value) { - assert((0 <= index) && (index < 32)); - bits_ &= ~(1 << index); - bits_ |= value ? (1 << index) : 0; -} - -bool StunUInt32Attribute::Read(ByteBuffer* buf) { - if (!buf->ReadUInt32(bits_)) - return false; - return true; -} - -void StunUInt32Attribute::Write(ByteBuffer* buf) const { - buf->WriteUInt32(bits_); -} - -StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length) - : StunAttribute(type, length), bytes_(0) { -} - -StunByteStringAttribute::~StunByteStringAttribute() { - delete [] bytes_; -} - -void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) { - delete [] bytes_; - bytes_ = bytes; - SetLength(length); -} - -void StunByteStringAttribute::CopyBytes(const char* bytes) { - CopyBytes(bytes, (uint16)strlen(bytes)); -} - -void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) { - char* new_bytes = new char[length]; - std::memcpy(new_bytes, bytes, length); - SetBytes(new_bytes, length); -} - -uint8 StunByteStringAttribute::GetByte(int index) const { - assert(bytes_); - assert((0 <= index) && (index < length())); - return static_cast(bytes_[index]); -} - -void StunByteStringAttribute::SetByte(int index, uint8 value) { - assert(bytes_); - assert((0 <= index) && (index < length())); - bytes_[index] = value; -} - -bool StunByteStringAttribute::Read(ByteBuffer* buf) { - bytes_ = new char[length()]; - if (!buf->ReadBytes(bytes_, length())) - return false; - return true; -} - -void StunByteStringAttribute::Write(ByteBuffer* buf) const { - buf->WriteBytes(bytes_, length()); -} - -StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length) - : StunAttribute(type, length), class_(0), number_(0) { -} - -StunErrorCodeAttribute::~StunErrorCodeAttribute() { -} - -void StunErrorCodeAttribute::SetErrorCode(uint32 code) { - class_ = (uint8)((code >> 8) & 0x7); - number_ = (uint8)(code & 0xff); -} - -void StunErrorCodeAttribute::SetReason(const std::string& reason) { - SetLength(MIN_SIZE + (uint16)reason.size()); - reason_ = reason; -} - -bool StunErrorCodeAttribute::Read(ByteBuffer* buf) { - uint32 val; - if (!buf->ReadUInt32(val)) - return false; - - if ((val >> 11) != 0) - LOG(LERROR) << "error-code bits not zero"; - - SetErrorCode(val); - - if (!buf->ReadString(reason_, length() - 4)) - return false; - - return true; -} - -void StunErrorCodeAttribute::Write(ByteBuffer* buf) const { - buf->WriteUInt32(error_code()); - buf->WriteString(reason_); -} - -StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length) - : StunAttribute(type, length) { - attr_types_ = new std::vector(); -} - -StunUInt16ListAttribute::~StunUInt16ListAttribute() { - delete attr_types_; -} - -size_t StunUInt16ListAttribute::Size() const { - return attr_types_->size(); -} - -uint16 StunUInt16ListAttribute::GetType(int index) const { - return (*attr_types_)[index]; -} - -void StunUInt16ListAttribute::SetType(int index, uint16 value) { - (*attr_types_)[index] = value; -} - -void StunUInt16ListAttribute::AddType(uint16 value) { - attr_types_->push_back(value); - SetLength((uint16)attr_types_->size() * 2); -} - -bool StunUInt16ListAttribute::Read(ByteBuffer* buf) { - for (int i = 0; i < length() / 2; i++) { - uint16 attr; - if (!buf->ReadUInt16(attr)) - return false; - attr_types_->push_back(attr); - } - return true; -} - -void StunUInt16ListAttribute::Write(ByteBuffer* buf) const { - for (unsigned i = 0; i < attr_types_->size(); i++) - buf->WriteUInt16((*attr_types_)[i]); -} - -StunTransportPrefsAttribute::StunTransportPrefsAttribute( - uint16 type, uint16 length) - : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) { -} - -StunTransportPrefsAttribute::~StunTransportPrefsAttribute() { - delete addr_; -} - -void StunTransportPrefsAttribute::SetPreallocateAddress( - StunAddressAttribute* addr) { - if (!addr) { - preallocate_ = false; - addr_ = 0; - SetLength(SIZE1); - } else { - preallocate_ = true; - addr_ = addr; - SetLength(SIZE2); - } -} - -bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) { - uint32 val; - if (!buf->ReadUInt32(val)) - return false; - - if ((val >> 3) != 0) - LOG(LERROR) << "transport-preferences bits not zero"; - - preallocate_ = static_cast((val >> 2) & 0x1); - prefs_ = (uint8)(val & 0x3); - - if (preallocate_ && (prefs_ == 3)) - LOG(LERROR) << "transport-preferences imcompatible P and Typ"; - - if (!preallocate_) { - if (length() != StunUInt32Attribute::SIZE) - return false; - } else { - if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE) - return false; - - addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS); - addr_->Read(buf); - } - - return true; -} - -void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const { - buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_); - - if (preallocate_) - addr_->Write(buf); -} - -StunMessageType GetStunResponseType(StunMessageType request_type) { - switch (request_type) { - case STUN_SHARED_SECRET_REQUEST: - return STUN_SHARED_SECRET_RESPONSE; - case STUN_ALLOCATE_REQUEST: - return STUN_ALLOCATE_RESPONSE; - case STUN_SEND_REQUEST: - return STUN_SEND_RESPONSE; - default: - return STUN_BINDING_RESPONSE; - } -} - -StunMessageType GetStunErrorResponseType(StunMessageType request_type) { - switch (request_type) { - case STUN_SHARED_SECRET_REQUEST: - return STUN_SHARED_SECRET_ERROR_RESPONSE; - case STUN_ALLOCATE_REQUEST: - return STUN_ALLOCATE_ERROR_RESPONSE; - case STUN_SEND_REQUEST: - return STUN_SEND_ERROR_RESPONSE; - default: - return STUN_BINDING_ERROR_RESPONSE; - } -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cpp new file mode 100644 index 00000000..2d6aa67c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cpp @@ -0,0 +1,577 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/logging.h" +#include "talk/p2p/base/stun.h" +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcpy; +} +#endif + +namespace cricket { + +const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST"; +const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED"; +const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE"; +const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS"; +const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE"; +const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME"; +const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS"; +const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR"; +const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE"; + +StunMessage::StunMessage() : type_(0), length_(0), + transaction_id_("0000000000000000") { + assert(transaction_id_.size() == 16); + attrs_ = new std::vector(); +} + +StunMessage::~StunMessage() { + for (unsigned i = 0; i < attrs_->size(); i++) + delete (*attrs_)[i]; + delete attrs_; +} + +void StunMessage::SetTransactionID(const std::string& str) { + assert(str.size() == 16); + transaction_id_ = str; +} + +void StunMessage::AddAttribute(StunAttribute* attr) { + attrs_->push_back(attr); + length_ += attr->length() + 4; +} + +const StunAddressAttribute* +StunMessage::GetAddress(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + return reinterpret_cast(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunUInt32Attribute* +StunMessage::GetUInt32(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + return reinterpret_cast(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunByteStringAttribute* +StunMessage::GetByteString(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MESSAGE_INTEGRITY: + case STUN_ATTR_DATA: + case STUN_ATTR_MAGIC_COOKIE: + return reinterpret_cast(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunErrorCodeAttribute* StunMessage::GetErrorCode() const { + return reinterpret_cast( + GetAttribute(STUN_ATTR_ERROR_CODE)); +} + +const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const { + return reinterpret_cast( + GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES)); +} + +const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const { + return reinterpret_cast( + GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES)); +} + +const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const { + for (unsigned i = 0; i < attrs_->size(); i++) { + if ((*attrs_)[i]->type() == type) + return (*attrs_)[i]; + } + return 0; +} + +bool StunMessage::Read(ByteBuffer* buf) { + if (!buf->ReadUInt16(type_)) + return false; + + if (!buf->ReadUInt16(length_)) + return false; + + std::string transaction_id; + if (!buf->ReadString(transaction_id, 16)) + return false; + assert(transaction_id.size() == 16); + transaction_id_ = transaction_id; + + if (length_ > buf->Length()) + return false; + + attrs_->resize(0); + + size_t rest = buf->Length() - length_; + while (buf->Length() > rest) { + uint16 attr_type, attr_length; + if (!buf->ReadUInt16(attr_type)) + return false; + if (!buf->ReadUInt16(attr_length)) + return false; + + StunAttribute* attr = StunAttribute::Create(attr_type, attr_length); + if (!attr || !attr->Read(buf)) + return false; + + attrs_->push_back(attr); + } + + if (buf->Length() != rest) { + // fixme: shouldn't be doing this + LOG(LERROR) << "wrong message length" + << " (" << (int)rest << " != " << (int)buf->Length() << ")"; + return false; + } + + return true; +} + +void StunMessage::Write(ByteBuffer* buf) const { + buf->WriteUInt16(type_); + buf->WriteUInt16(length_); + buf->WriteString(transaction_id_); + + for (unsigned i = 0; i < attrs_->size(); i++) { + buf->WriteUInt16((*attrs_)[i]->type()); + buf->WriteUInt16((*attrs_)[i]->length()); + (*attrs_)[i]->Write(buf); + } +} + +StunAttribute::StunAttribute(uint16 type, uint16 length) + : type_(type), length_(length) { +} + +StunAttribute* StunAttribute::Create(uint16 type, uint16 length) { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + if (length != StunAddressAttribute::SIZE) + return 0; + return new StunAddressAttribute(type); + + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + if (length != StunUInt32Attribute::SIZE) + return 0; + return new StunUInt32Attribute(type); + + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MAGIC_COOKIE: + return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0; + + case STUN_ATTR_MESSAGE_INTEGRITY: + return (length == 20) ? new StunByteStringAttribute(type, length) : 0; + + case STUN_ATTR_DATA: + return new StunByteStringAttribute(type, length); + + case STUN_ATTR_ERROR_CODE: + if (length < StunErrorCodeAttribute::MIN_SIZE) + return 0; + return new StunErrorCodeAttribute(type, length); + + case STUN_ATTR_UNKNOWN_ATTRIBUTES: + return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0; + + case STUN_ATTR_TRANSPORT_PREFERENCES: + if ((length != StunTransportPrefsAttribute::SIZE1) && + (length != StunTransportPrefsAttribute::SIZE2)) + return 0; + return new StunTransportPrefsAttribute(type, length); + + default: + return 0; + } +} + +StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + return new StunAddressAttribute(type); + + default: + assert(false); + return 0; + } +} + +StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) { + switch (type) { + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + return new StunUInt32Attribute(type); + + default: + assert(false); + return 0; + } +} + +StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) { + switch (type) { + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MESSAGE_INTEGRITY: + case STUN_ATTR_DATA: + case STUN_ATTR_MAGIC_COOKIE: + return new StunByteStringAttribute(type, 0); + + default: + assert(false); + return 0; + } +} + +StunErrorCodeAttribute* StunAttribute::CreateErrorCode() { + return new StunErrorCodeAttribute( + STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE); +} + +StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() { + return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0); +} + +StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() { + return new StunTransportPrefsAttribute( + STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1); +} + +StunAddressAttribute::StunAddressAttribute(uint16 type) + : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) { +} + +bool StunAddressAttribute::Read(ByteBuffer* buf) { + uint8 dummy; + if (!buf->ReadUInt8(dummy)) + return false; + if (!buf->ReadUInt8(family_)) + return false; + if (!buf->ReadUInt16(port_)) + return false; + if (!buf->ReadUInt32(ip_)) + return false; + return true; +} + +void StunAddressAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt8(0); + buf->WriteUInt8(family_); + buf->WriteUInt16(port_); + buf->WriteUInt32(ip_); +} + +StunUInt32Attribute::StunUInt32Attribute(uint16 type) + : StunAttribute(type, SIZE), bits_(0) { +} + +bool StunUInt32Attribute::GetBit(int index) const { + assert((0 <= index) && (index < 32)); + return static_cast((bits_ >> index) & 0x1); +} + +void StunUInt32Attribute::SetBit(int index, bool value) { + assert((0 <= index) && (index < 32)); + bits_ &= ~(1 << index); + bits_ |= value ? (1 << index) : 0; +} + +bool StunUInt32Attribute::Read(ByteBuffer* buf) { + if (!buf->ReadUInt32(bits_)) + return false; + return true; +} + +void StunUInt32Attribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32(bits_); +} + +StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length) + : StunAttribute(type, length), bytes_(0) { +} + +StunByteStringAttribute::~StunByteStringAttribute() { + delete [] bytes_; +} + +void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) { + delete [] bytes_; + bytes_ = bytes; + SetLength(length); +} + +void StunByteStringAttribute::CopyBytes(const char* bytes) { + CopyBytes(bytes, (uint16)strlen(bytes)); +} + +void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) { + char* new_bytes = new char[length]; + std::memcpy(new_bytes, bytes, length); + SetBytes(new_bytes, length); +} + +uint8 StunByteStringAttribute::GetByte(int index) const { + assert(bytes_); + assert((0 <= index) && (index < length())); + return static_cast(bytes_[index]); +} + +void StunByteStringAttribute::SetByte(int index, uint8 value) { + assert(bytes_); + assert((0 <= index) && (index < length())); + bytes_[index] = value; +} + +bool StunByteStringAttribute::Read(ByteBuffer* buf) { + bytes_ = new char[length()]; + if (!buf->ReadBytes(bytes_, length())) + return false; + return true; +} + +void StunByteStringAttribute::Write(ByteBuffer* buf) const { + buf->WriteBytes(bytes_, length()); +} + +StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length) + : StunAttribute(type, length), class_(0), number_(0) { +} + +StunErrorCodeAttribute::~StunErrorCodeAttribute() { +} + +void StunErrorCodeAttribute::SetErrorCode(uint32 code) { + class_ = (uint8)((code >> 8) & 0x7); + number_ = (uint8)(code & 0xff); +} + +void StunErrorCodeAttribute::SetReason(const std::string& reason) { + SetLength(MIN_SIZE + (uint16)reason.size()); + reason_ = reason; +} + +bool StunErrorCodeAttribute::Read(ByteBuffer* buf) { + uint32 val; + if (!buf->ReadUInt32(val)) + return false; + + if ((val >> 11) != 0) + LOG(LERROR) << "error-code bits not zero"; + + SetErrorCode(val); + + if (!buf->ReadString(reason_, length() - 4)) + return false; + + return true; +} + +void StunErrorCodeAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32(error_code()); + buf->WriteString(reason_); +} + +StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length) + : StunAttribute(type, length) { + attr_types_ = new std::vector(); +} + +StunUInt16ListAttribute::~StunUInt16ListAttribute() { + delete attr_types_; +} + +size_t StunUInt16ListAttribute::Size() const { + return attr_types_->size(); +} + +uint16 StunUInt16ListAttribute::GetType(int index) const { + return (*attr_types_)[index]; +} + +void StunUInt16ListAttribute::SetType(int index, uint16 value) { + (*attr_types_)[index] = value; +} + +void StunUInt16ListAttribute::AddType(uint16 value) { + attr_types_->push_back(value); + SetLength((uint16)attr_types_->size() * 2); +} + +bool StunUInt16ListAttribute::Read(ByteBuffer* buf) { + for (int i = 0; i < length() / 2; i++) { + uint16 attr; + if (!buf->ReadUInt16(attr)) + return false; + attr_types_->push_back(attr); + } + return true; +} + +void StunUInt16ListAttribute::Write(ByteBuffer* buf) const { + for (unsigned i = 0; i < attr_types_->size(); i++) + buf->WriteUInt16((*attr_types_)[i]); +} + +StunTransportPrefsAttribute::StunTransportPrefsAttribute( + uint16 type, uint16 length) + : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) { +} + +StunTransportPrefsAttribute::~StunTransportPrefsAttribute() { + delete addr_; +} + +void StunTransportPrefsAttribute::SetPreallocateAddress( + StunAddressAttribute* addr) { + if (!addr) { + preallocate_ = false; + addr_ = 0; + SetLength(SIZE1); + } else { + preallocate_ = true; + addr_ = addr; + SetLength(SIZE2); + } +} + +bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) { + uint32 val; + if (!buf->ReadUInt32(val)) + return false; + + if ((val >> 3) != 0) + LOG(LERROR) << "transport-preferences bits not zero"; + + preallocate_ = static_cast((val >> 2) & 0x1); + prefs_ = (uint8)(val & 0x3); + + if (preallocate_ && (prefs_ == 3)) + LOG(LERROR) << "transport-preferences imcompatible P and Typ"; + + if (!preallocate_) { + if (length() != StunUInt32Attribute::SIZE) + return false; + } else { + if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE) + return false; + + addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS); + addr_->Read(buf); + } + + return true; +} + +void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_); + + if (preallocate_) + addr_->Write(buf); +} + +StunMessageType GetStunResponseType(StunMessageType request_type) { + switch (request_type) { + case STUN_SHARED_SECRET_REQUEST: + return STUN_SHARED_SECRET_RESPONSE; + case STUN_ALLOCATE_REQUEST: + return STUN_ALLOCATE_RESPONSE; + case STUN_SEND_REQUEST: + return STUN_SEND_RESPONSE; + default: + return STUN_BINDING_RESPONSE; + } +} + +StunMessageType GetStunErrorResponseType(StunMessageType request_type) { + switch (request_type) { + case STUN_SHARED_SECRET_REQUEST: + return STUN_SHARED_SECRET_ERROR_RESPONSE; + case STUN_ALLOCATE_REQUEST: + return STUN_ALLOCATE_ERROR_RESPONSE; + case STUN_SEND_REQUEST: + return STUN_SEND_ERROR_RESPONSE; + default: + return STUN_BINDING_ERROR_RESPONSE; + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc deleted file mode 100644 index 6d1dc6b1..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc +++ /dev/null @@ -1,171 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/logging.h" -#include "talk/p2p/base/stunport.h" -#include "talk/p2p/base/helpers.h" -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts -const int RETRY_DELAY = 50; // 50ms, from ICE spec -const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs - -// Handles a binding request sent to the STUN server. -class StunPortBindingRequest : public StunRequest { -public: - StunPortBindingRequest(StunPort* port) : port_(port) { - start_time_ = GetMillisecondCount(); - } - - virtual ~StunPortBindingRequest() { - } - - virtual void Prepare(StunMessage* request) { - request->SetType(STUN_BINDING_REQUEST); - } - - virtual void OnResponse(StunMessage* response) { - const StunAddressAttribute* addr_attr = - response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); - if (!addr_attr) { - LOG(LERROR) << "Binding response missing mapped address."; - } else if (addr_attr->family() != 1) { - LOG(LERROR) << "Binding address has bad family"; - } else { - SocketAddress addr(addr_attr->ip(), addr_attr->port()); - if (port_->candidates().empty()) - port_->add_address(addr, "udp"); - } - - // We will do a keep-alive regardless of whether this request suceeds. - // This should have almost no impact on network usage. - port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY); - } - - virtual void OnErrorResponse(StunMessage* response) { - const StunErrorCodeAttribute* attr = response->GetErrorCode(); - if (!attr) { - LOG(LERROR) << "Bad allocate response error code"; - } else { - LOG(LERROR) << "Binding error response:" - << " class=" << attr->error_class() - << " number=" << attr->number() - << " reason='" << attr->reason() << "'"; - } - - if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) - port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY); - } - - virtual void OnTimeout() { - LOG(LERROR) << "Binding request timed out"; - if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) - port_->requests_.SendDelayed(new StunPortBindingRequest(port_), RETRY_DELAY); - } - -private: - uint32 start_time_; - StunPort* port_; -}; - -const std::string STUN_PORT_TYPE("stun"); - -StunPort::StunPort(Thread* thread, SocketFactory* factory, Network* network, - const SocketAddress& local_addr, - const SocketAddress& server_addr) - : UDPPort(thread, STUN_PORT_TYPE, factory, network), - server_addr_(server_addr), requests_(thread), error_(0) { - - socket_ = CreatePacketSocket(PROTO_UDP); - socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket); - if (socket_->Bind(local_addr) < 0) - PLOG(LERROR, socket_->GetError()) << "bind"; - - requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket); -} - -StunPort::~StunPort() { - delete socket_; -} - -void StunPort::PrepareAddress() { - requests_.Send(new StunPortBindingRequest(this)); -} - -int StunPort::SendTo( - const void* data, size_t size, const SocketAddress& addr, bool payload) { - int sent = socket_->SendTo(data, size, addr); - if (sent < 0) - error_ = socket_->GetError(); - return sent; -} - -int StunPort::SetOption(Socket::Option opt, int value) { - return socket_->SetOption(opt, value); -} - -int StunPort::GetError() { - return error_; -} - -void StunPort::OnReadPacket( - const char* data, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - assert(socket == socket_); - - // Look for a response to a binding request. - if (requests_.CheckResponse(data, size)) - return; - - // Process this data packet in the normal manner. - UDPPort::OnReadPacket(data, size, remote_addr); -} - -void StunPort::OnSendPacket(const void* data, size_t size) { - if (socket_->SendTo(data, size, server_addr_) < 0) - PLOG(LERROR, socket_->GetError()) << "sendto"; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cpp new file mode 100644 index 00000000..6d1dc6b1 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cpp @@ -0,0 +1,171 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/helpers.h" +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts +const int RETRY_DELAY = 50; // 50ms, from ICE spec +const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs + +// Handles a binding request sent to the STUN server. +class StunPortBindingRequest : public StunRequest { +public: + StunPortBindingRequest(StunPort* port) : port_(port) { + start_time_ = GetMillisecondCount(); + } + + virtual ~StunPortBindingRequest() { + } + + virtual void Prepare(StunMessage* request) { + request->SetType(STUN_BINDING_REQUEST); + } + + virtual void OnResponse(StunMessage* response) { + const StunAddressAttribute* addr_attr = + response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + if (!addr_attr) { + LOG(LERROR) << "Binding response missing mapped address."; + } else if (addr_attr->family() != 1) { + LOG(LERROR) << "Binding address has bad family"; + } else { + SocketAddress addr(addr_attr->ip(), addr_attr->port()); + if (port_->candidates().empty()) + port_->add_address(addr, "udp"); + } + + // We will do a keep-alive regardless of whether this request suceeds. + // This should have almost no impact on network usage. + port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY); + } + + virtual void OnErrorResponse(StunMessage* response) { + const StunErrorCodeAttribute* attr = response->GetErrorCode(); + if (!attr) { + LOG(LERROR) << "Bad allocate response error code"; + } else { + LOG(LERROR) << "Binding error response:" + << " class=" << attr->error_class() + << " number=" << attr->number() + << " reason='" << attr->reason() << "'"; + } + + if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY); + } + + virtual void OnTimeout() { + LOG(LERROR) << "Binding request timed out"; + if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + port_->requests_.SendDelayed(new StunPortBindingRequest(port_), RETRY_DELAY); + } + +private: + uint32 start_time_; + StunPort* port_; +}; + +const std::string STUN_PORT_TYPE("stun"); + +StunPort::StunPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& local_addr, + const SocketAddress& server_addr) + : UDPPort(thread, STUN_PORT_TYPE, factory, network), + server_addr_(server_addr), requests_(thread), error_(0) { + + socket_ = CreatePacketSocket(PROTO_UDP); + socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket); + if (socket_->Bind(local_addr) < 0) + PLOG(LERROR, socket_->GetError()) << "bind"; + + requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket); +} + +StunPort::~StunPort() { + delete socket_; +} + +void StunPort::PrepareAddress() { + requests_.Send(new StunPortBindingRequest(this)); +} + +int StunPort::SendTo( + const void* data, size_t size, const SocketAddress& addr, bool payload) { + int sent = socket_->SendTo(data, size, addr); + if (sent < 0) + error_ = socket_->GetError(); + return sent; +} + +int StunPort::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int StunPort::GetError() { + return error_; +} + +void StunPort::OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + + // Look for a response to a binding request. + if (requests_.CheckResponse(data, size)) + return; + + // Process this data packet in the normal manner. + UDPPort::OnReadPacket(data, size, remote_addr); +} + +void StunPort::OnSendPacket(const void* data, size_t size) { + if (socket_->SendTo(data, size, server_addr_) < 0) + PLOG(LERROR, socket_->GetError()) << "sendto"; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc deleted file mode 100644 index 14d64735..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc +++ /dev/null @@ -1,198 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/logging.h" -#include "talk/p2p/base/stunrequest.h" -#include "talk/p2p/base/helpers.h" -#include -#include - -namespace cricket { - -const uint32 MSG_STUN_SEND = 1; - -const int MAX_SENDS = 9; -const int DELAY_UNIT = 100; // 100 milliseconds -const int DELAY_MAX_FACTOR = 16; - -StunRequestManager::StunRequestManager(Thread* thread) : thread_(thread) { -} - -StunRequestManager::~StunRequestManager() { - while (requests_.begin() != requests_.end()) { - StunRequest *request = requests_.begin()->second; - requests_.erase(requests_.begin()); - delete request; - } -} - -void StunRequestManager::Send(StunRequest* request) { - SendDelayed(request, 0); -} - -void StunRequestManager::SendDelayed(StunRequest* request, int delay) { - request->set_manager(this); - assert(requests_.find(request->id()) == requests_.end()); - requests_[request->id()] = request; - thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); -} - -void StunRequestManager::Remove(StunRequest* request) { - assert(request->manager() == this); - RequestMap::iterator iter = requests_.find(request->id()); - if (iter != requests_.end()) { - assert(iter->second == request); - requests_.erase(iter); - thread_->Clear(request); - } -} - -void StunRequestManager::Clear() { - std::vector requests; - for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) - requests.push_back(i->second); - - for (uint32 i = 0; i < requests.size(); ++i) - Remove(requests[i]); -} - -bool StunRequestManager::CheckResponse(StunMessage* msg) { - RequestMap::iterator iter = requests_.find(msg->transaction_id()); - if (iter == requests_.end()) - return false; - - StunRequest* request = iter->second; - if (msg->type() == GetStunResponseType(request->type())) { - request->OnResponse(msg); - } else if (msg->type() == GetStunErrorResponseType(request->type())) { - request->OnErrorResponse(msg); - } else { - LOG(LERROR) << "Received response with wrong type: " << msg->type() - << " (expecting " << GetStunResponseType(request->type()) << ")"; - return false; - } - - delete request; - return true; -} - -bool StunRequestManager::CheckResponse(const char* data, size_t size) { - // Check the appropriate bytes of the stream to see if they match the - // transaction ID of a response we are expecting. - - if (size < 20) - return false; - - std::string id; - id.append(data + 4, 16); - - RequestMap::iterator iter = requests_.find(id); - if (iter == requests_.end()) - return false; - - // Parse the STUN message and continue processing as usual. - - ByteBuffer buf(data, size); - StunMessage msg; - if (!msg.Read(&buf)) - return false; - - return CheckResponse(&msg); -} - -StunRequest::StunRequest() - : manager_(0), id_(CreateRandomString(16)), msg_(0), count_(0), - timeout_(false), tstamp_(0) { -} - -StunRequest::StunRequest(StunMessage* request) - : manager_(0), id_(request->transaction_id()), msg_(request), - count_(0), timeout_(false) { -} - -StunRequest::~StunRequest() { - assert(manager_ != NULL); - if (manager_) { - manager_->Remove(this); - manager_->thread_->Clear(this); - } - delete msg_; -} - -const StunMessageType StunRequest::type() { - assert(msg_); - return msg_->type(); -} - -void StunRequest::set_manager(StunRequestManager* manager) { - assert(!manager_); - manager_ = manager; -} - -void StunRequest::OnMessage(Message* pmsg) { - assert(manager_); - assert(pmsg->message_id == MSG_STUN_SEND); - - if (!msg_) { - msg_ = new StunMessage(); - msg_->SetTransactionID(id_); - Prepare(msg_); - assert(msg_->transaction_id() == id_); - } - - if (timeout_) { - OnTimeout(); - delete this; - return; - } - - tstamp_ = GetMillisecondCount(); - - ByteBuffer buf; - msg_->Write(&buf); - manager_->SignalSendPacket(buf.Data(), buf.Length()); - - int delay = GetNextDelay(); - manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL); -} - -uint32 StunRequest::Elapsed() const { - return (GetMillisecondCount() - tstamp_); -} - -int StunRequest::GetNextDelay() { - int delay = DELAY_UNIT * _min(1 << count_, DELAY_MAX_FACTOR); - count_ += 1; - if (count_ == MAX_SENDS) - timeout_ = true; - return delay; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cpp new file mode 100644 index 00000000..14d64735 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cpp @@ -0,0 +1,198 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/stunrequest.h" +#include "talk/p2p/base/helpers.h" +#include +#include + +namespace cricket { + +const uint32 MSG_STUN_SEND = 1; + +const int MAX_SENDS = 9; +const int DELAY_UNIT = 100; // 100 milliseconds +const int DELAY_MAX_FACTOR = 16; + +StunRequestManager::StunRequestManager(Thread* thread) : thread_(thread) { +} + +StunRequestManager::~StunRequestManager() { + while (requests_.begin() != requests_.end()) { + StunRequest *request = requests_.begin()->second; + requests_.erase(requests_.begin()); + delete request; + } +} + +void StunRequestManager::Send(StunRequest* request) { + SendDelayed(request, 0); +} + +void StunRequestManager::SendDelayed(StunRequest* request, int delay) { + request->set_manager(this); + assert(requests_.find(request->id()) == requests_.end()); + requests_[request->id()] = request; + thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); +} + +void StunRequestManager::Remove(StunRequest* request) { + assert(request->manager() == this); + RequestMap::iterator iter = requests_.find(request->id()); + if (iter != requests_.end()) { + assert(iter->second == request); + requests_.erase(iter); + thread_->Clear(request); + } +} + +void StunRequestManager::Clear() { + std::vector requests; + for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) + requests.push_back(i->second); + + for (uint32 i = 0; i < requests.size(); ++i) + Remove(requests[i]); +} + +bool StunRequestManager::CheckResponse(StunMessage* msg) { + RequestMap::iterator iter = requests_.find(msg->transaction_id()); + if (iter == requests_.end()) + return false; + + StunRequest* request = iter->second; + if (msg->type() == GetStunResponseType(request->type())) { + request->OnResponse(msg); + } else if (msg->type() == GetStunErrorResponseType(request->type())) { + request->OnErrorResponse(msg); + } else { + LOG(LERROR) << "Received response with wrong type: " << msg->type() + << " (expecting " << GetStunResponseType(request->type()) << ")"; + return false; + } + + delete request; + return true; +} + +bool StunRequestManager::CheckResponse(const char* data, size_t size) { + // Check the appropriate bytes of the stream to see if they match the + // transaction ID of a response we are expecting. + + if (size < 20) + return false; + + std::string id; + id.append(data + 4, 16); + + RequestMap::iterator iter = requests_.find(id); + if (iter == requests_.end()) + return false; + + // Parse the STUN message and continue processing as usual. + + ByteBuffer buf(data, size); + StunMessage msg; + if (!msg.Read(&buf)) + return false; + + return CheckResponse(&msg); +} + +StunRequest::StunRequest() + : manager_(0), id_(CreateRandomString(16)), msg_(0), count_(0), + timeout_(false), tstamp_(0) { +} + +StunRequest::StunRequest(StunMessage* request) + : manager_(0), id_(request->transaction_id()), msg_(request), + count_(0), timeout_(false) { +} + +StunRequest::~StunRequest() { + assert(manager_ != NULL); + if (manager_) { + manager_->Remove(this); + manager_->thread_->Clear(this); + } + delete msg_; +} + +const StunMessageType StunRequest::type() { + assert(msg_); + return msg_->type(); +} + +void StunRequest::set_manager(StunRequestManager* manager) { + assert(!manager_); + manager_ = manager; +} + +void StunRequest::OnMessage(Message* pmsg) { + assert(manager_); + assert(pmsg->message_id == MSG_STUN_SEND); + + if (!msg_) { + msg_ = new StunMessage(); + msg_->SetTransactionID(id_); + Prepare(msg_); + assert(msg_->transaction_id() == id_); + } + + if (timeout_) { + OnTimeout(); + delete this; + return; + } + + tstamp_ = GetMillisecondCount(); + + ByteBuffer buf; + msg_->Write(&buf); + manager_->SignalSendPacket(buf.Data(), buf.Length()); + + int delay = GetNextDelay(); + manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL); +} + +uint32 StunRequest::Elapsed() const { + return (GetMillisecondCount() - tstamp_); +} + +int StunRequest::GetNextDelay() { + int delay = DELAY_UNIT * _min(1 << count_, DELAY_MAX_FACTOR); + count_ += 1; + if (count_ == MAX_SENDS) + timeout_ = true; + return delay; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc deleted file mode 100644 index c6d9f9f8..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc +++ /dev/null @@ -1,161 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/bytebuffer.h" -#include "talk/p2p/base/stunserver.h" -#include -#include - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -StunServer::StunServer(AsyncUDPSocket* socket) : socket_(socket) { - socket_->SignalReadPacket.connect(this, &StunServer::OnPacket); -} - -StunServer::~StunServer() { - socket_->SignalReadPacket.disconnect(this); -} - -void StunServer::OnPacket( - const char* buf, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - - // TODO: If appropriate, look for the magic cookie before parsing. - - // Parse the STUN message. - ByteBuffer bbuf(buf, size); - StunMessage msg; - if (!msg.Read(&bbuf)) { - SendErrorResponse(msg, remote_addr, 400, "Bad Request"); - return; - } - - // TODO: If this is UDP, then we shouldn't allow non-fully-parsed messages. - - // TODO: If unknown non-optiional (<= 0x7fff) attributes are found, send a - // 420 "Unknown Attribute" response. - - // TODO: Check that a message-integrity attribute was given (or send 401 - // "Unauthorized"). Check that a username attribute was given (or send - // 432 "Missing Username"). Look up the username and password. If it - // is missing or the HMAC is wrong, send 431 "Integrity Check Failure". - - // Send the message to the appropriate handler function. - switch (msg.type()) { - case STUN_BINDING_REQUEST: - OnBindingRequest(&msg, remote_addr); - return; - - case STUN_ALLOCATE_REQUEST: - OnAllocateRequest(&msg, remote_addr); - return; - - default: - SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported"); - } -} - -void StunServer::OnBindingRequest( - StunMessage* msg, const SocketAddress& remote_addr) { - StunMessage response; - response.SetType(STUN_BINDING_RESPONSE); - response.SetTransactionID(msg->transaction_id()); - - // Tell the user the address that we received their request from. - StunAddressAttribute* mapped_addr = - StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); - mapped_addr->SetFamily(1); - mapped_addr->SetPort(remote_addr.port()); - mapped_addr->SetIP(remote_addr.ip()); - response.AddAttribute(mapped_addr); - - // Tell the user the address that we are sending the response from. - SocketAddress local_addr = socket_->GetLocalAddress(); - StunAddressAttribute* source_addr = - StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS); - source_addr->SetFamily(1); - source_addr->SetPort(local_addr.port()); - source_addr->SetIP(local_addr.ip()); - response.AddAttribute(source_addr); - - // TODO: Add username and message-integrity. - - // TODO: Add changed-address. (Keep information about three other servers.) - - SendResponse(response, remote_addr); -} - -void StunServer::OnAllocateRequest( - StunMessage* msg, const SocketAddress& addr) { - SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); -} - -void StunServer::OnSharedSecretRequest( - StunMessage* msg, const SocketAddress& addr) { - SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); -} - -void StunServer::OnSendRequest(StunMessage* msg, const SocketAddress& addr) { - SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); -} - -void StunServer::SendErrorResponse( - const StunMessage& msg, const SocketAddress& addr, int error_code, - const char* error_desc) { - - StunMessage err_msg; - err_msg.SetType(GetStunErrorResponseType(msg.type())); - err_msg.SetTransactionID(msg.transaction_id()); - - StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); - err_code->SetErrorClass(error_code / 100); - err_code->SetNumber(error_code % 100); - err_code->SetReason(error_desc); - err_msg.AddAttribute(err_code); - - SendResponse(err_msg, addr); -} - -void StunServer::SendResponse( - const StunMessage& msg, const SocketAddress& addr) { - - ByteBuffer buf; - msg.Write(&buf); - - // TODO: Allow response addr attribute if sent from another stun server. - - if (socket_->SendTo(buf.Data(), buf.Length(), addr) < 0) - std::cerr << "sendto: " << std::strerror(errno) << std::endl; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cpp new file mode 100644 index 00000000..c6d9f9f8 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cpp @@ -0,0 +1,161 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/bytebuffer.h" +#include "talk/p2p/base/stunserver.h" +#include +#include + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +StunServer::StunServer(AsyncUDPSocket* socket) : socket_(socket) { + socket_->SignalReadPacket.connect(this, &StunServer::OnPacket); +} + +StunServer::~StunServer() { + socket_->SignalReadPacket.disconnect(this); +} + +void StunServer::OnPacket( + const char* buf, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + // TODO: If appropriate, look for the magic cookie before parsing. + + // Parse the STUN message. + ByteBuffer bbuf(buf, size); + StunMessage msg; + if (!msg.Read(&bbuf)) { + SendErrorResponse(msg, remote_addr, 400, "Bad Request"); + return; + } + + // TODO: If this is UDP, then we shouldn't allow non-fully-parsed messages. + + // TODO: If unknown non-optiional (<= 0x7fff) attributes are found, send a + // 420 "Unknown Attribute" response. + + // TODO: Check that a message-integrity attribute was given (or send 401 + // "Unauthorized"). Check that a username attribute was given (or send + // 432 "Missing Username"). Look up the username and password. If it + // is missing or the HMAC is wrong, send 431 "Integrity Check Failure". + + // Send the message to the appropriate handler function. + switch (msg.type()) { + case STUN_BINDING_REQUEST: + OnBindingRequest(&msg, remote_addr); + return; + + case STUN_ALLOCATE_REQUEST: + OnAllocateRequest(&msg, remote_addr); + return; + + default: + SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported"); + } +} + +void StunServer::OnBindingRequest( + StunMessage* msg, const SocketAddress& remote_addr) { + StunMessage response; + response.SetType(STUN_BINDING_RESPONSE); + response.SetTransactionID(msg->transaction_id()); + + // Tell the user the address that we received their request from. + StunAddressAttribute* mapped_addr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + mapped_addr->SetFamily(1); + mapped_addr->SetPort(remote_addr.port()); + mapped_addr->SetIP(remote_addr.ip()); + response.AddAttribute(mapped_addr); + + // Tell the user the address that we are sending the response from. + SocketAddress local_addr = socket_->GetLocalAddress(); + StunAddressAttribute* source_addr = + StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS); + source_addr->SetFamily(1); + source_addr->SetPort(local_addr.port()); + source_addr->SetIP(local_addr.ip()); + response.AddAttribute(source_addr); + + // TODO: Add username and message-integrity. + + // TODO: Add changed-address. (Keep information about three other servers.) + + SendResponse(response, remote_addr); +} + +void StunServer::OnAllocateRequest( + StunMessage* msg, const SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::OnSharedSecretRequest( + StunMessage* msg, const SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::OnSendRequest(StunMessage* msg, const SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::SendErrorResponse( + const StunMessage& msg, const SocketAddress& addr, int error_code, + const char* error_desc) { + + StunMessage err_msg; + err_msg.SetType(GetStunErrorResponseType(msg.type())); + err_msg.SetTransactionID(msg.transaction_id()); + + StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); + err_code->SetErrorClass(error_code / 100); + err_code->SetNumber(error_code % 100); + err_code->SetReason(error_desc); + err_msg.AddAttribute(err_code); + + SendResponse(err_msg, addr); +} + +void StunServer::SendResponse( + const StunMessage& msg, const SocketAddress& addr) { + + ByteBuffer buf; + msg.Write(&buf); + + // TODO: Allow response addr attribute if sent from another stun server. + + if (socket_->SendTo(buf.Data(), buf.Length(), addr) < 0) + std::cerr << "sendto: " << std::strerror(errno) << std::endl; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro index dce92ec4..30de4e45 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro @@ -6,9 +6,9 @@ include(../../../../../conf.pri) # Input SOURCES += \ - stunserver.cc \ - stunserver_main.cc \ - ../../base/host.cc #\ -# ../../base/socketaddresspair.cc + stunserver.cpp \ + stunserver_main.cpp \ + ../../base/host.cpp #\ +# ../../base/socketaddresspair.cpp LIBS += ../../../liblibjingle.a diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc deleted file mode 100644 index bac3e35f..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" -#include "talk/base/thread.h" -#include "talk/p2p/base/stunserver.h" -#include -#include - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -using namespace cricket; - -int main(int argc, char* argv[]) { - if (argc != 1) { - std::cerr << "usage: stunserver" << std::endl; - return 1; - } - - SocketAddress server_addr(LocalHost().networks()[1]->ip(), 7000); - - Thread *pthMain = Thread::Current(); - - AsyncUDPSocket* server_socket = CreateAsyncUDPSocket(pthMain->socketserver()); - if (server_socket->Bind(server_addr) < 0) { - std::cerr << "bind: " << std::strerror(errno) << std::endl; - return 1; - } - - StunServer* server = new StunServer(server_socket); - - std::cout << "Listening at " << server_addr.ToString() << std::endl; - - pthMain->Loop(); - - delete server; - delete server_socket; - return 0; -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cpp new file mode 100644 index 00000000..bac3e35f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cpp @@ -0,0 +1,67 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/host.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/stunserver.h" +#include +#include + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +using namespace cricket; + +int main(int argc, char* argv[]) { + if (argc != 1) { + std::cerr << "usage: stunserver" << std::endl; + return 1; + } + + SocketAddress server_addr(LocalHost().networks()[1]->ip(), 7000); + + Thread *pthMain = Thread::Current(); + + AsyncUDPSocket* server_socket = CreateAsyncUDPSocket(pthMain->socketserver()); + if (server_socket->Bind(server_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + StunServer* server = new StunServer(server_socket); + + std::cout << "Listening at " << server_addr.ToString() << std::endl; + + pthMain->Loop(); + + delete server; + delete server_socket; + return 0; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc deleted file mode 100644 index a2d2adc6..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc +++ /dev/null @@ -1,250 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/p2p/base/tcpport.h" -#include "talk/base/logging.h" -#ifdef WIN32 -#include "talk/base/winfirewall.h" -#endif // WIN32 -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -#ifdef WIN32 -static WinFirewall win_firewall; -#endif // WIN32 - -TCPPort::TCPPort(Thread* thread, SocketFactory* factory, Network* network, - const SocketAddress& address) - : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { - incoming_only_ = (address.port() != 0); - socket_ = thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); - socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent); - if (socket_->Bind(address) < 0) - LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); -} - -TCPPort::~TCPPort() { - delete socket_; -} - -Connection* TCPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { - // We only support TCP protocols - if ((address.protocol() != "tcp") && (address.protocol() != "ssltcp")) - return 0; - - // We can't accept TCP connections incoming on other ports - if (origin == ORIGIN_OTHER_PORT) - return 0; - - // Check if we are allowed to make outgoing TCP connections - if (incoming_only_ && (origin == ORIGIN_MESSAGE)) - return 0; - - // We don't know how to act as an ssl server yet - if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT)) - return 0; - - TCPConnection* conn = 0; - if (AsyncTCPSocket * socket = GetIncoming(address.address(), true)) { - socket->SignalReadPacket.disconnect(this); - conn = new TCPConnection(this, address, socket); - } else { - conn = new TCPConnection(this, address); - } - AddConnection(conn); - return conn; -} - -void TCPPort::PrepareAddress() { - assert(socket_); - - bool allow_listen = true; -#ifdef WIN32 - if (win_firewall.Initialize()) { - char module_path[MAX_PATH + 1] = { 0 }; - ::GetModuleFileNameA(NULL, module_path, MAX_PATH); - if (win_firewall.Enabled() && !win_firewall.Authorized(module_path)) { - allow_listen = false; - } - } -#endif // WIN32 - if (allow_listen) { - if (socket_->Listen(5) < 0) - LOG(INFO) << "listen: " << std::strerror(socket_->GetError()); - } else { - LOG(INFO) << "not listening due to firewall restrictions"; - } - // Note: We still add the address, since otherwise the remote side won't recognize - // our incoming TCP connections. - add_address(socket_->GetLocalAddress(), "tcp"); -} - -int TCPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) { - AsyncTCPSocket * socket = 0; - - if (TCPConnection * conn = static_cast(GetConnection(addr))) { - socket = conn->socket(); - } else { - socket = GetIncoming(addr); - } - if (!socket) { - LOG(INFO) << "Unknown destination for SendTo: " << addr.ToString(); - return -1; // TODO: Set error_ - } - - //LOG(INFO) << "TCPPort::SendTo(" << size << ", " << addr.ToString() << ")"; - - int sent = socket->Send(data, size); - if (sent < 0) - error_ = socket->GetError(); - return sent; -} - -int TCPPort::SetOption(Socket::Option opt, int value) { - return socket_->SetOption(opt, value); -} - -int TCPPort::GetError() { - assert(socket_); - return error_; -} - -void TCPPort::OnAcceptEvent(AsyncSocket* socket) { - assert(socket == socket_); - - Incoming incoming; - AsyncSocket * newsocket = static_cast(socket->Accept(&incoming.addr)); - if (!newsocket) { - // TODO: Do something better like forwarding the error to the user. - LOG(INFO) << "accept: " << socket_->GetError() << " " << std::strerror(socket_->GetError()); - return; - } - incoming.socket = new AsyncTCPSocket(newsocket); - incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket); - - LOG(INFO) << "accepted incoming connection from " << incoming.addr.ToString(); - incoming_.push_back(incoming); - - // Prime a read event in case data is waiting - newsocket->SignalReadEvent(newsocket); -} - -AsyncTCPSocket * TCPPort::GetIncoming(const SocketAddress& addr, bool remove) { - AsyncTCPSocket * socket = 0; - for (std::list::iterator it = incoming_.begin(); it != incoming_.end(); ++it) { - if (it->addr == addr) { - socket = it->socket; - if (remove) - incoming_.erase(it); - break; - } - } - return socket; -} - -void TCPPort::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - Port::OnReadPacket(data, size, remote_addr); -} - -TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, AsyncTCPSocket* socket) - : Connection(port, 0, candidate), socket_(socket), error_(0) { - bool outgoing = (socket_ == 0); - if (outgoing) { - socket_ = static_cast(port->CreatePacketSocket( - (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP)); - } - socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); - socket_->SignalClose.connect(this, &TCPConnection::OnClose); - if (outgoing) { - connected_ = false; - socket_->SignalConnect.connect(this, &TCPConnection::OnConnect); - socket_->Connect(candidate.address()); - LOG(INFO) << "Connecting to " << candidate.address().ToString(); - } -} - -TCPConnection::~TCPConnection() { -} - -int TCPConnection::Send(const void* data, size_t size) { - if (write_state() != STATE_WRITABLE) - return 0; - - int sent = socket_->Send(data, size); - if (sent < 0) { - error_ = socket_->GetError(); - } else { - sent_total_bytes_ += sent; - } - return sent; -} - -int TCPConnection::GetError() { - return error_; -} - -TCPPort* TCPConnection::tcpport() { - return static_cast(port_); -} - -void TCPConnection::OnConnect(AsyncTCPSocket* socket) { - assert(socket == socket_); - LOG(INFO) << "tcp connected to " << socket->GetRemoteAddress().ToString(); - set_connected(true); -} - -void TCPConnection::OnClose(AsyncTCPSocket* socket, int error) { - assert(socket == socket_); - LOG(INFO) << "tcp closed with error: " << error; - set_connected(false); -} - -void TCPConnection::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - assert(socket == socket_); - Connection::OnReadPacket(data, size); -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cpp new file mode 100644 index 00000000..a2d2adc6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cpp @@ -0,0 +1,250 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/p2p/base/tcpport.h" +#include "talk/base/logging.h" +#ifdef WIN32 +#include "talk/base/winfirewall.h" +#endif // WIN32 +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +#ifdef WIN32 +static WinFirewall win_firewall; +#endif // WIN32 + +TCPPort::TCPPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& address) + : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { + incoming_only_ = (address.port() != 0); + socket_ = thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); + socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent); + if (socket_->Bind(address) < 0) + LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); +} + +TCPPort::~TCPPort() { + delete socket_; +} + +Connection* TCPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + // We only support TCP protocols + if ((address.protocol() != "tcp") && (address.protocol() != "ssltcp")) + return 0; + + // We can't accept TCP connections incoming on other ports + if (origin == ORIGIN_OTHER_PORT) + return 0; + + // Check if we are allowed to make outgoing TCP connections + if (incoming_only_ && (origin == ORIGIN_MESSAGE)) + return 0; + + // We don't know how to act as an ssl server yet + if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT)) + return 0; + + TCPConnection* conn = 0; + if (AsyncTCPSocket * socket = GetIncoming(address.address(), true)) { + socket->SignalReadPacket.disconnect(this); + conn = new TCPConnection(this, address, socket); + } else { + conn = new TCPConnection(this, address); + } + AddConnection(conn); + return conn; +} + +void TCPPort::PrepareAddress() { + assert(socket_); + + bool allow_listen = true; +#ifdef WIN32 + if (win_firewall.Initialize()) { + char module_path[MAX_PATH + 1] = { 0 }; + ::GetModuleFileNameA(NULL, module_path, MAX_PATH); + if (win_firewall.Enabled() && !win_firewall.Authorized(module_path)) { + allow_listen = false; + } + } +#endif // WIN32 + if (allow_listen) { + if (socket_->Listen(5) < 0) + LOG(INFO) << "listen: " << std::strerror(socket_->GetError()); + } else { + LOG(INFO) << "not listening due to firewall restrictions"; + } + // Note: We still add the address, since otherwise the remote side won't recognize + // our incoming TCP connections. + add_address(socket_->GetLocalAddress(), "tcp"); +} + +int TCPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) { + AsyncTCPSocket * socket = 0; + + if (TCPConnection * conn = static_cast(GetConnection(addr))) { + socket = conn->socket(); + } else { + socket = GetIncoming(addr); + } + if (!socket) { + LOG(INFO) << "Unknown destination for SendTo: " << addr.ToString(); + return -1; // TODO: Set error_ + } + + //LOG(INFO) << "TCPPort::SendTo(" << size << ", " << addr.ToString() << ")"; + + int sent = socket->Send(data, size); + if (sent < 0) + error_ = socket->GetError(); + return sent; +} + +int TCPPort::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int TCPPort::GetError() { + assert(socket_); + return error_; +} + +void TCPPort::OnAcceptEvent(AsyncSocket* socket) { + assert(socket == socket_); + + Incoming incoming; + AsyncSocket * newsocket = static_cast(socket->Accept(&incoming.addr)); + if (!newsocket) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "accept: " << socket_->GetError() << " " << std::strerror(socket_->GetError()); + return; + } + incoming.socket = new AsyncTCPSocket(newsocket); + incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket); + + LOG(INFO) << "accepted incoming connection from " << incoming.addr.ToString(); + incoming_.push_back(incoming); + + // Prime a read event in case data is waiting + newsocket->SignalReadEvent(newsocket); +} + +AsyncTCPSocket * TCPPort::GetIncoming(const SocketAddress& addr, bool remove) { + AsyncTCPSocket * socket = 0; + for (std::list::iterator it = incoming_.begin(); it != incoming_.end(); ++it) { + if (it->addr == addr) { + socket = it->socket; + if (remove) + incoming_.erase(it); + break; + } + } + return socket; +} + +void TCPPort::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + Port::OnReadPacket(data, size, remote_addr); +} + +TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, AsyncTCPSocket* socket) + : Connection(port, 0, candidate), socket_(socket), error_(0) { + bool outgoing = (socket_ == 0); + if (outgoing) { + socket_ = static_cast(port->CreatePacketSocket( + (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP)); + } + socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); + socket_->SignalClose.connect(this, &TCPConnection::OnClose); + if (outgoing) { + connected_ = false; + socket_->SignalConnect.connect(this, &TCPConnection::OnConnect); + socket_->Connect(candidate.address()); + LOG(INFO) << "Connecting to " << candidate.address().ToString(); + } +} + +TCPConnection::~TCPConnection() { +} + +int TCPConnection::Send(const void* data, size_t size) { + if (write_state() != STATE_WRITABLE) + return 0; + + int sent = socket_->Send(data, size); + if (sent < 0) { + error_ = socket_->GetError(); + } else { + sent_total_bytes_ += sent; + } + return sent; +} + +int TCPConnection::GetError() { + return error_; +} + +TCPPort* TCPConnection::tcpport() { + return static_cast(port_); +} + +void TCPConnection::OnConnect(AsyncTCPSocket* socket) { + assert(socket == socket_); + LOG(INFO) << "tcp connected to " << socket->GetRemoteAddress().ToString(); + set_connected(true); +} + +void TCPConnection::OnClose(AsyncTCPSocket* socket, int error) { + assert(socket == socket_); + LOG(INFO) << "tcp closed with error: " << error; + set_connected(false); +} + +void TCPConnection::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + Connection::OnReadPacket(data, size); +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc deleted file mode 100644 index fabbb25b..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc +++ /dev/null @@ -1,117 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/logging.h" -#include "talk/p2p/base/udpport.h" -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::strerror; -} -#endif - -#ifdef POSIX -extern "C" { -#include -} -#endif // POSIX - -namespace cricket { - -const std::string LOCAL_PORT_TYPE("local"); - -UDPPort::UDPPort(Thread* thread, SocketFactory* factory, Network* network, - const SocketAddress& address) - : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { - socket_ = CreatePacketSocket(PROTO_UDP); - socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacketSlot); - if (socket_->Bind(address) < 0) - PLOG(LERROR, socket_->GetError()) << "bind"; -} - -UDPPort::UDPPort(Thread* thread, const std::string &type, - SocketFactory* factory, Network* network) - : Port(thread, type, factory, network), socket_(0), error_(0) { -} - -UDPPort::~UDPPort() { - delete socket_; -} - -void UDPPort::PrepareAddress() { - assert(socket_); - add_address(socket_->GetLocalAddress(), "udp"); -} - -Connection* UDPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { - if (address.protocol() != "udp") - return 0; - - Connection * conn = new ProxyConnection(this, 0, address); - AddConnection(conn); - return conn; -} - -int UDPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) { - assert(socket_); - int sent = socket_->SendTo(data, size, addr); - if (sent < 0) - error_ = socket_->GetError(); - return sent; -} - -int UDPPort::SetOption(Socket::Option opt, int value) { - return socket_->SetOption(opt, value); -} - -int UDPPort::GetError() { - assert(socket_); - return error_; -} - -void UDPPort::OnReadPacketSlot( - const char* data, size_t size, const SocketAddress& remote_addr, - AsyncPacketSocket* socket) { - assert(socket == socket_); - OnReadPacket(data, size, remote_addr); -} - -void UDPPort::OnReadPacket( - const char* data, size_t size, const SocketAddress& remote_addr) { - if (Connection* conn = GetConnection(remote_addr)) { - conn->OnReadPacket(data, size); - } else { - Port::OnReadPacket(data, size, remote_addr); - } -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cpp new file mode 100644 index 00000000..fabbb25b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cpp @@ -0,0 +1,117 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/udpport.h" +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include +} +#endif // POSIX + +namespace cricket { + +const std::string LOCAL_PORT_TYPE("local"); + +UDPPort::UDPPort(Thread* thread, SocketFactory* factory, Network* network, + const SocketAddress& address) + : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { + socket_ = CreatePacketSocket(PROTO_UDP); + socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacketSlot); + if (socket_->Bind(address) < 0) + PLOG(LERROR, socket_->GetError()) << "bind"; +} + +UDPPort::UDPPort(Thread* thread, const std::string &type, + SocketFactory* factory, Network* network) + : Port(thread, type, factory, network), socket_(0), error_(0) { +} + +UDPPort::~UDPPort() { + delete socket_; +} + +void UDPPort::PrepareAddress() { + assert(socket_); + add_address(socket_->GetLocalAddress(), "udp"); +} + +Connection* UDPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + if (address.protocol() != "udp") + return 0; + + Connection * conn = new ProxyConnection(this, 0, address); + AddConnection(conn); + return conn; +} + +int UDPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) { + assert(socket_); + int sent = socket_->SendTo(data, size, addr); + if (sent < 0) + error_ = socket_->GetError(); + return sent; +} + +int UDPPort::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int UDPPort::GetError() { + assert(socket_); + return error_; +} + +void UDPPort::OnReadPacketSlot( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + assert(socket == socket_); + OnReadPacket(data, size, remote_addr); +} + +void UDPPort::OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr) { + if (Connection* conn = GetConnection(remote_addr)) { + conn->OnReadPacket(data, size); + } else { + Port::OnReadPacket(data, size, remote_addr); + } +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt index 7ede9820..8ccce5f8 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/CMakeLists.txt @@ -26,5 +26,5 @@ include_directories( tde_add_library( cricketp2pclient STATIC_PIC SOURCES - sessionclient.cc basicportallocator.cc socketmonitor.cc + sessionclient.cpp basicportallocator.cpp socketmonitor.cpp ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am index 2bdd95ff..4a461b93 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am @@ -1,6 +1,6 @@ -libcricketp2pclient_la_SOURCES = sessionclient.cc \ - basicportallocator.cc \ - socketmonitor.cc +libcricketp2pclient_la_SOURCES = sessionclient.cpp \ + basicportallocator.cpp \ + socketmonitor.cpp noinst_HEADERS = basicportallocator.h \ sessionclient.h \ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc deleted file mode 100644 index 5192595c..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc +++ /dev/null @@ -1,667 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/base/host.h" -#include "talk/base/logging.h" -#include "talk/p2p/client/basicportallocator.h" -#include "talk/p2p/base/port.h" -#include "talk/p2p/base/udpport.h" -#include "talk/p2p/base/tcpport.h" -#include "talk/p2p/base/stunport.h" -#include "talk/p2p/base/relayport.h" -#include "talk/p2p/base/helpers.h" -#include - -namespace { - -const uint32 MSG_CONFIG_START = 1; -const uint32 MSG_CONFIG_READY = 2; -const uint32 MSG_ALLOCATE = 3; -const uint32 MSG_ALLOCATION_PHASE = 4; -const uint32 MSG_SHAKE = 5; - -const uint32 ALLOCATE_DELAY = 250; -const uint32 ALLOCATION_STEP_DELAY = 1 * 1000; - -const int PHASE_UDP = 0; -const int PHASE_RELAY = 1; -const int PHASE_TCP = 2; -const int PHASE_SSLTCP = 3; -const int kNumPhases = 4; - -const float PREF_LOCAL_UDP = 1.0f; -const float PREF_LOCAL_STUN = 0.9f; -const float PREF_LOCAL_TCP = 0.8f; -const float PREF_RELAY = 0.5f; - -const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants -const float RELAY_BACKUP_PREF_MODIFIER = -0.2f; - - -// Returns the phase in which a given local candidate (or rather, the port that -// gave rise to that local candidate) would have been created. -int LocalCandidateToPhase(const cricket::Candidate& candidate) { - cricket::ProtocolType proto; - bool result = cricket::StringToProto(candidate.protocol().c_str(), proto); - if (result) { - if (candidate.type() == cricket::LOCAL_PORT_TYPE) { - switch (proto) { - case cricket::PROTO_UDP: return PHASE_UDP; - case cricket::PROTO_TCP: return PHASE_TCP; - default: assert(false); - } - } else if (candidate.type() == cricket::STUN_PORT_TYPE) { - return PHASE_UDP; - } else if (candidate.type() == cricket::RELAY_PORT_TYPE) { - switch (proto) { - case cricket::PROTO_UDP: return PHASE_RELAY; - case cricket::PROTO_TCP: return PHASE_TCP; - case cricket::PROTO_SSLTCP: return PHASE_SSLTCP; - default: assert(false); - } - } else { - assert(false); - } - } else { - assert(false); - } - return PHASE_UDP; // reached only with assert failure -} - -const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds -const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds - -int ShakeDelay() { - int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1; - return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range; -} - -} - -namespace cricket { - -// Performs the allocation of ports, in a sequenced (timed) manner, for a given -// network and IP address. -class AllocationSequence: public MessageHandler { -public: - AllocationSequence(BasicPortAllocatorSession* session, - Network* network, - PortConfiguration* config); - ~AllocationSequence(); - - // Determines whether this sequence is operating on an equivalent network - // setup to the one given. - bool IsEquivalent(Network* network); - - // Starts and stops the sequence. When started, it will continue allocating - // new ports on its own timed schedule. - void Start(); - void Stop(); - - // MessageHandler: - void OnMessage(Message* msg); - - void EnableProtocol(ProtocolType proto); - bool ProtocolEnabled(ProtocolType proto) const; - -private: - BasicPortAllocatorSession* session_; - Network* network_; - uint32 ip_; - PortConfiguration* config_; - bool running_; - int step_; - int step_of_phase_[kNumPhases]; - - typedef std::vector ProtocolList; - ProtocolList protocols_; - - void CreateUDPPorts(); - void CreateTCPPorts(); - void CreateStunPorts(); - void CreateRelayPorts(); -}; - - -// BasicPortAllocator - -BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager) - : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(NULL), relay_address_(NULL) { -} - -BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager, SocketAddress* stun_address, SocketAddress *relay_address) - : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(stun_address), relay_address_(relay_address) { -} - -BasicPortAllocator::~BasicPortAllocator() { -} - -int BasicPortAllocator::best_writable_phase() const { - // If we are configured with an HTTP proxy, the best bet is to use the relay - if ((best_writable_phase_ == -1) - && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) { - return PHASE_RELAY; - } - return best_writable_phase_; -} - -PortAllocatorSession *BasicPortAllocator::CreateSession(const std::string &name) { - return new BasicPortAllocatorSession(this, name, stun_address_, relay_address_); -} - -void BasicPortAllocator::AddWritablePhase(int phase) { - if ((best_writable_phase_ == -1) || (phase < best_writable_phase_)) - best_writable_phase_ = phase; -} - -// BasicPortAllocatorSession - -BasicPortAllocatorSession::BasicPortAllocatorSession( - BasicPortAllocator *allocator, - const std::string &name) - : allocator_(allocator), name_(name), network_thread_(NULL), - config_thread_(NULL), allocation_started_(false), running_(false), - stun_address_(NULL), relay_address_(NULL) { -} - -BasicPortAllocatorSession::BasicPortAllocatorSession( - BasicPortAllocator *allocator, - const std::string &name, - SocketAddress *stun_address, - SocketAddress *relay_address) - : allocator_(allocator), name_(name), network_thread_(NULL), - config_thread_(NULL), allocation_started_(false), running_(false), - stun_address_(stun_address), relay_address_(relay_address) { -} - -BasicPortAllocatorSession::~BasicPortAllocatorSession() { - if (config_thread_ != NULL) - config_thread_->Clear(this); - if (network_thread_ != NULL) - network_thread_->Clear(this); - - std::vector::iterator it; - for (it = ports_.begin(); it != ports_.end(); it++) - delete it->port; - - for (uint32 i = 0; i < configs_.size(); ++i) - delete configs_[i]; - - for (uint32 i = 0; i < sequences_.size(); ++i) - delete sequences_[i]; -} - -void BasicPortAllocatorSession::GetInitialPorts() { - network_thread_ = Thread::Current(); - if (!config_thread_) - config_thread_ = network_thread_; - - config_thread_->Post(this, MSG_CONFIG_START); - - if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHAKER) - network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); -} - -void BasicPortAllocatorSession::StartGetAllPorts() { - assert(Thread::Current() == network_thread_); - running_ = true; - if (allocation_started_) - network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); - for (uint32 i = 0; i < sequences_.size(); ++i) - sequences_[i]->Start(); - for (size_t i = 0; i < ports_.size(); ++i) - ports_[i].port->Start(); -} - -void BasicPortAllocatorSession::StopGetAllPorts() { - assert(Thread::Current() == network_thread_); - running_ = false; - network_thread_->Clear(this, MSG_ALLOCATE); - for (uint32 i = 0; i < sequences_.size(); ++i) - sequences_[i]->Stop(); -} - -void BasicPortAllocatorSession::OnMessage(Message *message) { - switch (message->message_id) { - case MSG_CONFIG_START: - assert(Thread::Current() == config_thread_); - GetPortConfigurations(); - break; - - case MSG_CONFIG_READY: - assert(Thread::Current() == network_thread_); - OnConfigReady(static_cast(message->pdata)); - break; - - case MSG_ALLOCATE: - assert(Thread::Current() == network_thread_); - OnAllocate(); - break; - - case MSG_SHAKE: - assert(Thread::Current() == network_thread_); - OnShake(); - break; - - default: - assert(false); - } -} - -void BasicPortAllocatorSession::GetPortConfigurations() { - PortConfiguration* config = NULL; - if (stun_address_ != NULL) - config = new PortConfiguration(*stun_address_, - CreateRandomString(16), - CreateRandomString(16), - ""); - PortConfiguration::PortList ports; - if (relay_address_ != NULL) { - ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP)); - config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER); - } - - ConfigReady(config); -} - -void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) { - network_thread_->Post(this, MSG_CONFIG_READY, config); -} - -// Adds a configuration to the list. -void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) { - if (config) - configs_.push_back(config); - - AllocatePorts(); -} - -void BasicPortAllocatorSession::AllocatePorts() { - assert(Thread::Current() == network_thread_); - - if (allocator_->proxy().type != PROXY_NONE) - Port::set_proxy(allocator_->proxy()); - - network_thread_->Post(this, MSG_ALLOCATE); -} - -// For each network, see if we have a sequence that covers it already. If not, -// create a new sequence to create the appropriate ports. -void BasicPortAllocatorSession::OnAllocate() { - std::vector networks; - allocator_->network_manager()->GetNetworks(networks); - - for (uint32 i = 0; i < networks.size(); ++i) { - if (HasEquivalentSequence(networks[i])) - continue; - - PortConfiguration* config = NULL; - if (configs_.size() > 0) - config = configs_.back(); - - AllocationSequence* sequence = - new AllocationSequence(this, networks[i], config); - if (running_) - sequence->Start(); - - sequences_.push_back(sequence); - } - - allocation_started_ = true; - if (running_) - network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); -} - -bool BasicPortAllocatorSession::HasEquivalentSequence(Network* network) { - for (uint32 i = 0; i < sequences_.size(); ++i) - if (sequences_[i]->IsEquivalent(network)) - return true; - return false; -} - -void BasicPortAllocatorSession::AddAllocatedPort(Port* port, - AllocationSequence * seq, - float pref, - bool prepare_address) { - if (!port) - return; - - port->set_name(name_); - port->set_preference(pref); - port->set_generation(generation()); - PortData data; - data.port = port; - data.sequence = seq; - data.ready = false; - ports_.push_back(data); - port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady); - port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated); - port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed); - if (prepare_address) - port->PrepareAddress(); - if (running_) - port->Start(); -} - -void BasicPortAllocatorSession::OnAddressReady(Port *port) { - assert(Thread::Current() == network_thread_); - std::vector::iterator it = std::find(ports_.begin(), ports_.end(), port); - assert(it != ports_.end()); - assert(!it->ready); - it->ready = true; - SignalPortReady(this, port); - - // Only accumulate the candidates whose protocol has been enabled - std::vector candidates; - const std::vector& potentials = port->candidates(); - for (size_t i=0; isequence->ProtocolEnabled(pvalue)) { - candidates.push_back(potentials[i]); - } - } - if (!candidates.empty()) { - SignalCandidatesReady(this, candidates); - } -} - -void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto) { - std::vector candidates; - for (std::vector::iterator it = ports_.begin(); it != ports_.end(); ++it) { - if (!it->ready || (it->sequence != seq)) - continue; - - const std::vector& potentials = it->port->candidates(); - for (size_t i=0; i::iterator iter = - find(ports_.begin(), ports_.end(), port); - assert(iter != ports_.end()); - ports_.erase(iter); - - LOG(INFO) << "Removed port from allocator: " - << static_cast(ports_.size()) << " remaining"; -} - -void BasicPortAllocatorSession::OnConnectionCreated(Port* port, Connection* conn) { - conn->SignalStateChange.connect(this, &BasicPortAllocatorSession::OnConnectionStateChange); -} - -void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) { - if (conn->write_state() == Connection::STATE_WRITABLE) - allocator_->AddWritablePhase(LocalCandidateToPhase(conn->local_candidate())); -} - -void BasicPortAllocatorSession::OnShake() { - LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<"; - - std::vector ports; - std::vector connections; - - for (size_t i = 0; i < ports_.size(); ++i) { - if (ports_[i].ready) - ports.push_back(ports_[i].port); - } - - for (size_t i = 0; i < ports.size(); ++i) { - Port::AddressMap::const_iterator iter; - for (iter = ports[i]->connections().begin(); - iter != ports[i]->connections().end(); - ++iter) { - connections.push_back(iter->second); - } - } - - LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and " - << (int)connections.size() << " connections"; - - for (size_t i = 0; i < connections.size(); ++i) - connections[i]->Destroy(); - - if (running_ || (ports.size() > 0) || (connections.size() > 0)) - network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); -} - -// AllocationSequence - -AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, - Network* network, - PortConfiguration* config) - : session_(session), network_(network), ip_(network->ip()), config_(config), - running_(false), step_(0) { - - // All of the phases up until the best-writable phase so far run in step 0. - // The other phases follow sequentially in the steps after that. If there is - // no best-writable so far, then only phase 0 occurs in step 0. - int last_phase_in_step_zero = - _max(0, session->allocator()->best_writable_phase()); - for (int phase = 0; phase < kNumPhases; ++phase) - step_of_phase_[phase] = _max(0, phase - last_phase_in_step_zero); - - // Immediately perform phase 0. - OnMessage(NULL); -} - -AllocationSequence::~AllocationSequence() { - session_->network_thread()->Clear(this); -} - -bool AllocationSequence::IsEquivalent(Network* network) { - return (network == network_) && (ip_ == network->ip()); -} - -void AllocationSequence::Start() { - running_ = true; - session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, - this, - MSG_ALLOCATION_PHASE); -} - -void AllocationSequence::Stop() { - running_ = false; - session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE); -} - -void AllocationSequence::OnMessage(Message* msg) { - assert(Thread::Current() == session_->network_thread()); - if (msg) - assert(msg->message_id == MSG_ALLOCATION_PHASE); - - // Perform all of the phases in the current step. - for (int phase = 0; phase < kNumPhases; phase++) { - if (step_of_phase_[phase] != step_) - continue; - - switch (phase) { - case PHASE_UDP: - LOG(INFO) << "Phase=UDP Step=" << step_; - CreateUDPPorts(); - CreateStunPorts(); - EnableProtocol(PROTO_UDP); - break; - - case PHASE_RELAY: - LOG(INFO) << "Phase=RELAY Step=" << step_; - CreateRelayPorts(); - break; - - case PHASE_TCP: - LOG(INFO) << "Phase=TCP Step=" << step_; - CreateTCPPorts(); - EnableProtocol(PROTO_TCP); - break; - - case PHASE_SSLTCP: - LOG(INFO) << "Phase=SSLTCP Step=" << step_; - EnableProtocol(PROTO_SSLTCP); - break; - - default: - // Nothing else we can do. - return; - } - } - - // TODO: use different delays for each stage - step_ += 1; - if (running_) { - session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, - this, - MSG_ALLOCATION_PHASE); - } -} - -void AllocationSequence::EnableProtocol(ProtocolType proto) { - if (!ProtocolEnabled(proto)) { - protocols_.push_back(proto); - session_->OnProtocolEnabled(this, proto); - } -} - -bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const { - for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) { - if (*it == proto) - return true; - } - return false; -} - -void AllocationSequence::CreateUDPPorts() { - if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_UDP) - return; - - Port* port = new UDPPort(session_->network_thread(), NULL, network_, - SocketAddress(ip_, 0)); - session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP); -} - -void AllocationSequence::CreateTCPPorts() { - if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_TCP) - return; - - Port* port = new TCPPort(session_->network_thread(), NULL, network_, - SocketAddress(ip_, 0)); - session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP); -} - -void AllocationSequence::CreateStunPorts() { - if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_STUN) - return; - - if (!config_ || config_->stun_address.IsAny()) - return; - - Port* port = new StunPort(session_->network_thread(), NULL, network_, - SocketAddress(ip_, 0), config_->stun_address); - session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN); -} - -void AllocationSequence::CreateRelayPorts() { - if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) - return; - - if (!config_) - return; - - PortConfiguration::RelayList::const_iterator relay; - for (relay = config_->relays.begin(); - relay != config_->relays.end(); - ++relay) { - - RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_, - SocketAddress(ip_, 0), - config_->username, config_->password, - config_->magic_cookie); - // Note: We must add the allocated port before we add addresses because - // the latter will create candidates that need name and preference - // settings. However, we also can't prepare the address (normally - // done by AddAllocatedPort) until we have these addresses. So we - // wait to do that until below. - session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier, false); - - // Add the addresses of this protocol. - PortConfiguration::PortList::const_iterator relay_port; - for (relay_port = relay->ports.begin(); - relay_port != relay->ports.end(); - ++relay_port) { - port->AddServerAddress(*relay_port); - port->AddExternalAddress(*relay_port); - } - - // Start fetching an address for this port. - port->PrepareAddress(); - } -} - -// PortConfiguration - -PortConfiguration::PortConfiguration(const SocketAddress& sa, - const std::string& un, - const std::string& pw, - const std::string& mc) - : stun_address(sa), username(un), password(pw), magic_cookie(mc) { -} - -void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) { - RelayServer relay; - relay.ports = ports; - relay.pref_modifier = pref_modifier; - relays.push_back(relay); -} - -bool PortConfiguration::SupportsProtocol( - const PortConfiguration::RelayServer& relay, ProtocolType type) { - PortConfiguration::PortList::const_iterator relay_port; - for (relay_port = relay.ports.begin(); - relay_port != relay.ports.end(); - ++relay_port) { - if (relay_port->proto == type) - return true; - } - return false; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cpp new file mode 100644 index 00000000..5192595c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cpp @@ -0,0 +1,667 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/p2p/client/basicportallocator.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/udpport.h" +#include "talk/p2p/base/tcpport.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/helpers.h" +#include + +namespace { + +const uint32 MSG_CONFIG_START = 1; +const uint32 MSG_CONFIG_READY = 2; +const uint32 MSG_ALLOCATE = 3; +const uint32 MSG_ALLOCATION_PHASE = 4; +const uint32 MSG_SHAKE = 5; + +const uint32 ALLOCATE_DELAY = 250; +const uint32 ALLOCATION_STEP_DELAY = 1 * 1000; + +const int PHASE_UDP = 0; +const int PHASE_RELAY = 1; +const int PHASE_TCP = 2; +const int PHASE_SSLTCP = 3; +const int kNumPhases = 4; + +const float PREF_LOCAL_UDP = 1.0f; +const float PREF_LOCAL_STUN = 0.9f; +const float PREF_LOCAL_TCP = 0.8f; +const float PREF_RELAY = 0.5f; + +const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants +const float RELAY_BACKUP_PREF_MODIFIER = -0.2f; + + +// Returns the phase in which a given local candidate (or rather, the port that +// gave rise to that local candidate) would have been created. +int LocalCandidateToPhase(const cricket::Candidate& candidate) { + cricket::ProtocolType proto; + bool result = cricket::StringToProto(candidate.protocol().c_str(), proto); + if (result) { + if (candidate.type() == cricket::LOCAL_PORT_TYPE) { + switch (proto) { + case cricket::PROTO_UDP: return PHASE_UDP; + case cricket::PROTO_TCP: return PHASE_TCP; + default: assert(false); + } + } else if (candidate.type() == cricket::STUN_PORT_TYPE) { + return PHASE_UDP; + } else if (candidate.type() == cricket::RELAY_PORT_TYPE) { + switch (proto) { + case cricket::PROTO_UDP: return PHASE_RELAY; + case cricket::PROTO_TCP: return PHASE_TCP; + case cricket::PROTO_SSLTCP: return PHASE_SSLTCP; + default: assert(false); + } + } else { + assert(false); + } + } else { + assert(false); + } + return PHASE_UDP; // reached only with assert failure +} + +const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds +const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds + +int ShakeDelay() { + int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1; + return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range; +} + +} + +namespace cricket { + +// Performs the allocation of ports, in a sequenced (timed) manner, for a given +// network and IP address. +class AllocationSequence: public MessageHandler { +public: + AllocationSequence(BasicPortAllocatorSession* session, + Network* network, + PortConfiguration* config); + ~AllocationSequence(); + + // Determines whether this sequence is operating on an equivalent network + // setup to the one given. + bool IsEquivalent(Network* network); + + // Starts and stops the sequence. When started, it will continue allocating + // new ports on its own timed schedule. + void Start(); + void Stop(); + + // MessageHandler: + void OnMessage(Message* msg); + + void EnableProtocol(ProtocolType proto); + bool ProtocolEnabled(ProtocolType proto) const; + +private: + BasicPortAllocatorSession* session_; + Network* network_; + uint32 ip_; + PortConfiguration* config_; + bool running_; + int step_; + int step_of_phase_[kNumPhases]; + + typedef std::vector ProtocolList; + ProtocolList protocols_; + + void CreateUDPPorts(); + void CreateTCPPorts(); + void CreateStunPorts(); + void CreateRelayPorts(); +}; + + +// BasicPortAllocator + +BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager) + : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(NULL), relay_address_(NULL) { +} + +BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager, SocketAddress* stun_address, SocketAddress *relay_address) + : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(stun_address), relay_address_(relay_address) { +} + +BasicPortAllocator::~BasicPortAllocator() { +} + +int BasicPortAllocator::best_writable_phase() const { + // If we are configured with an HTTP proxy, the best bet is to use the relay + if ((best_writable_phase_ == -1) + && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) { + return PHASE_RELAY; + } + return best_writable_phase_; +} + +PortAllocatorSession *BasicPortAllocator::CreateSession(const std::string &name) { + return new BasicPortAllocatorSession(this, name, stun_address_, relay_address_); +} + +void BasicPortAllocator::AddWritablePhase(int phase) { + if ((best_writable_phase_ == -1) || (phase < best_writable_phase_)) + best_writable_phase_ = phase; +} + +// BasicPortAllocatorSession + +BasicPortAllocatorSession::BasicPortAllocatorSession( + BasicPortAllocator *allocator, + const std::string &name) + : allocator_(allocator), name_(name), network_thread_(NULL), + config_thread_(NULL), allocation_started_(false), running_(false), + stun_address_(NULL), relay_address_(NULL) { +} + +BasicPortAllocatorSession::BasicPortAllocatorSession( + BasicPortAllocator *allocator, + const std::string &name, + SocketAddress *stun_address, + SocketAddress *relay_address) + : allocator_(allocator), name_(name), network_thread_(NULL), + config_thread_(NULL), allocation_started_(false), running_(false), + stun_address_(stun_address), relay_address_(relay_address) { +} + +BasicPortAllocatorSession::~BasicPortAllocatorSession() { + if (config_thread_ != NULL) + config_thread_->Clear(this); + if (network_thread_ != NULL) + network_thread_->Clear(this); + + std::vector::iterator it; + for (it = ports_.begin(); it != ports_.end(); it++) + delete it->port; + + for (uint32 i = 0; i < configs_.size(); ++i) + delete configs_[i]; + + for (uint32 i = 0; i < sequences_.size(); ++i) + delete sequences_[i]; +} + +void BasicPortAllocatorSession::GetInitialPorts() { + network_thread_ = Thread::Current(); + if (!config_thread_) + config_thread_ = network_thread_; + + config_thread_->Post(this, MSG_CONFIG_START); + + if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHAKER) + network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); +} + +void BasicPortAllocatorSession::StartGetAllPorts() { + assert(Thread::Current() == network_thread_); + running_ = true; + if (allocation_started_) + network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); + for (uint32 i = 0; i < sequences_.size(); ++i) + sequences_[i]->Start(); + for (size_t i = 0; i < ports_.size(); ++i) + ports_[i].port->Start(); +} + +void BasicPortAllocatorSession::StopGetAllPorts() { + assert(Thread::Current() == network_thread_); + running_ = false; + network_thread_->Clear(this, MSG_ALLOCATE); + for (uint32 i = 0; i < sequences_.size(); ++i) + sequences_[i]->Stop(); +} + +void BasicPortAllocatorSession::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CONFIG_START: + assert(Thread::Current() == config_thread_); + GetPortConfigurations(); + break; + + case MSG_CONFIG_READY: + assert(Thread::Current() == network_thread_); + OnConfigReady(static_cast(message->pdata)); + break; + + case MSG_ALLOCATE: + assert(Thread::Current() == network_thread_); + OnAllocate(); + break; + + case MSG_SHAKE: + assert(Thread::Current() == network_thread_); + OnShake(); + break; + + default: + assert(false); + } +} + +void BasicPortAllocatorSession::GetPortConfigurations() { + PortConfiguration* config = NULL; + if (stun_address_ != NULL) + config = new PortConfiguration(*stun_address_, + CreateRandomString(16), + CreateRandomString(16), + ""); + PortConfiguration::PortList ports; + if (relay_address_ != NULL) { + ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP)); + config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER); + } + + ConfigReady(config); +} + +void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) { + network_thread_->Post(this, MSG_CONFIG_READY, config); +} + +// Adds a configuration to the list. +void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) { + if (config) + configs_.push_back(config); + + AllocatePorts(); +} + +void BasicPortAllocatorSession::AllocatePorts() { + assert(Thread::Current() == network_thread_); + + if (allocator_->proxy().type != PROXY_NONE) + Port::set_proxy(allocator_->proxy()); + + network_thread_->Post(this, MSG_ALLOCATE); +} + +// For each network, see if we have a sequence that covers it already. If not, +// create a new sequence to create the appropriate ports. +void BasicPortAllocatorSession::OnAllocate() { + std::vector networks; + allocator_->network_manager()->GetNetworks(networks); + + for (uint32 i = 0; i < networks.size(); ++i) { + if (HasEquivalentSequence(networks[i])) + continue; + + PortConfiguration* config = NULL; + if (configs_.size() > 0) + config = configs_.back(); + + AllocationSequence* sequence = + new AllocationSequence(this, networks[i], config); + if (running_) + sequence->Start(); + + sequences_.push_back(sequence); + } + + allocation_started_ = true; + if (running_) + network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); +} + +bool BasicPortAllocatorSession::HasEquivalentSequence(Network* network) { + for (uint32 i = 0; i < sequences_.size(); ++i) + if (sequences_[i]->IsEquivalent(network)) + return true; + return false; +} + +void BasicPortAllocatorSession::AddAllocatedPort(Port* port, + AllocationSequence * seq, + float pref, + bool prepare_address) { + if (!port) + return; + + port->set_name(name_); + port->set_preference(pref); + port->set_generation(generation()); + PortData data; + data.port = port; + data.sequence = seq; + data.ready = false; + ports_.push_back(data); + port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady); + port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated); + port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed); + if (prepare_address) + port->PrepareAddress(); + if (running_) + port->Start(); +} + +void BasicPortAllocatorSession::OnAddressReady(Port *port) { + assert(Thread::Current() == network_thread_); + std::vector::iterator it = std::find(ports_.begin(), ports_.end(), port); + assert(it != ports_.end()); + assert(!it->ready); + it->ready = true; + SignalPortReady(this, port); + + // Only accumulate the candidates whose protocol has been enabled + std::vector candidates; + const std::vector& potentials = port->candidates(); + for (size_t i=0; isequence->ProtocolEnabled(pvalue)) { + candidates.push_back(potentials[i]); + } + } + if (!candidates.empty()) { + SignalCandidatesReady(this, candidates); + } +} + +void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto) { + std::vector candidates; + for (std::vector::iterator it = ports_.begin(); it != ports_.end(); ++it) { + if (!it->ready || (it->sequence != seq)) + continue; + + const std::vector& potentials = it->port->candidates(); + for (size_t i=0; i::iterator iter = + find(ports_.begin(), ports_.end(), port); + assert(iter != ports_.end()); + ports_.erase(iter); + + LOG(INFO) << "Removed port from allocator: " + << static_cast(ports_.size()) << " remaining"; +} + +void BasicPortAllocatorSession::OnConnectionCreated(Port* port, Connection* conn) { + conn->SignalStateChange.connect(this, &BasicPortAllocatorSession::OnConnectionStateChange); +} + +void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) { + if (conn->write_state() == Connection::STATE_WRITABLE) + allocator_->AddWritablePhase(LocalCandidateToPhase(conn->local_candidate())); +} + +void BasicPortAllocatorSession::OnShake() { + LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<"; + + std::vector ports; + std::vector connections; + + for (size_t i = 0; i < ports_.size(); ++i) { + if (ports_[i].ready) + ports.push_back(ports_[i].port); + } + + for (size_t i = 0; i < ports.size(); ++i) { + Port::AddressMap::const_iterator iter; + for (iter = ports[i]->connections().begin(); + iter != ports[i]->connections().end(); + ++iter) { + connections.push_back(iter->second); + } + } + + LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and " + << (int)connections.size() << " connections"; + + for (size_t i = 0; i < connections.size(); ++i) + connections[i]->Destroy(); + + if (running_ || (ports.size() > 0) || (connections.size() > 0)) + network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); +} + +// AllocationSequence + +AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, + Network* network, + PortConfiguration* config) + : session_(session), network_(network), ip_(network->ip()), config_(config), + running_(false), step_(0) { + + // All of the phases up until the best-writable phase so far run in step 0. + // The other phases follow sequentially in the steps after that. If there is + // no best-writable so far, then only phase 0 occurs in step 0. + int last_phase_in_step_zero = + _max(0, session->allocator()->best_writable_phase()); + for (int phase = 0; phase < kNumPhases; ++phase) + step_of_phase_[phase] = _max(0, phase - last_phase_in_step_zero); + + // Immediately perform phase 0. + OnMessage(NULL); +} + +AllocationSequence::~AllocationSequence() { + session_->network_thread()->Clear(this); +} + +bool AllocationSequence::IsEquivalent(Network* network) { + return (network == network_) && (ip_ == network->ip()); +} + +void AllocationSequence::Start() { + running_ = true; + session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, + this, + MSG_ALLOCATION_PHASE); +} + +void AllocationSequence::Stop() { + running_ = false; + session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE); +} + +void AllocationSequence::OnMessage(Message* msg) { + assert(Thread::Current() == session_->network_thread()); + if (msg) + assert(msg->message_id == MSG_ALLOCATION_PHASE); + + // Perform all of the phases in the current step. + for (int phase = 0; phase < kNumPhases; phase++) { + if (step_of_phase_[phase] != step_) + continue; + + switch (phase) { + case PHASE_UDP: + LOG(INFO) << "Phase=UDP Step=" << step_; + CreateUDPPorts(); + CreateStunPorts(); + EnableProtocol(PROTO_UDP); + break; + + case PHASE_RELAY: + LOG(INFO) << "Phase=RELAY Step=" << step_; + CreateRelayPorts(); + break; + + case PHASE_TCP: + LOG(INFO) << "Phase=TCP Step=" << step_; + CreateTCPPorts(); + EnableProtocol(PROTO_TCP); + break; + + case PHASE_SSLTCP: + LOG(INFO) << "Phase=SSLTCP Step=" << step_; + EnableProtocol(PROTO_SSLTCP); + break; + + default: + // Nothing else we can do. + return; + } + } + + // TODO: use different delays for each stage + step_ += 1; + if (running_) { + session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, + this, + MSG_ALLOCATION_PHASE); + } +} + +void AllocationSequence::EnableProtocol(ProtocolType proto) { + if (!ProtocolEnabled(proto)) { + protocols_.push_back(proto); + session_->OnProtocolEnabled(this, proto); + } +} + +bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const { + for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) { + if (*it == proto) + return true; + } + return false; +} + +void AllocationSequence::CreateUDPPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_UDP) + return; + + Port* port = new UDPPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0)); + session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP); +} + +void AllocationSequence::CreateTCPPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_TCP) + return; + + Port* port = new TCPPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0)); + session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP); +} + +void AllocationSequence::CreateStunPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_STUN) + return; + + if (!config_ || config_->stun_address.IsAny()) + return; + + Port* port = new StunPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0), config_->stun_address); + session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN); +} + +void AllocationSequence::CreateRelayPorts() { + if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) + return; + + if (!config_) + return; + + PortConfiguration::RelayList::const_iterator relay; + for (relay = config_->relays.begin(); + relay != config_->relays.end(); + ++relay) { + + RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_, + SocketAddress(ip_, 0), + config_->username, config_->password, + config_->magic_cookie); + // Note: We must add the allocated port before we add addresses because + // the latter will create candidates that need name and preference + // settings. However, we also can't prepare the address (normally + // done by AddAllocatedPort) until we have these addresses. So we + // wait to do that until below. + session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier, false); + + // Add the addresses of this protocol. + PortConfiguration::PortList::const_iterator relay_port; + for (relay_port = relay->ports.begin(); + relay_port != relay->ports.end(); + ++relay_port) { + port->AddServerAddress(*relay_port); + port->AddExternalAddress(*relay_port); + } + + // Start fetching an address for this port. + port->PrepareAddress(); + } +} + +// PortConfiguration + +PortConfiguration::PortConfiguration(const SocketAddress& sa, + const std::string& un, + const std::string& pw, + const std::string& mc) + : stun_address(sa), username(un), password(pw), magic_cookie(mc) { +} + +void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) { + RelayServer relay; + relay.ports = ports; + relay.pref_modifier = pref_modifier; + relays.push_back(relay); +} + +bool PortConfiguration::SupportsProtocol( + const PortConfiguration::RelayServer& relay, ProtocolType type) { + PortConfiguration::PortList::const_iterator relay_port; + for (relay_port = relay.ports.begin(); + relay_port != relay.ports.end(); + ++relay_port) { + if (relay_port->proto == type) + return true; + } + return false; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc deleted file mode 100644 index b64c444a..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc +++ /dev/null @@ -1,545 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif -#include "talk/p2p/client/sessionclient.h" -#include "talk/p2p/base/helpers.h" -#include "talk/base/logging.h" -#include "talk/xmllite/qname.h" -#include "talk/xmpp/constants.h" -#include "talk/xmllite/xmlprinter.h" -#include -#undef SetPort - -namespace { - -// We only allow usernames to be this many characters or fewer. -const size_t kMaxUsernameSize = 16; - -} - -namespace cricket { - -#if 0 ->>>>>> - - - - ... - - - - -<<<<<< - - ->>>>>> - - - - - - -> - -<<<<<< - - -#endif - -const std::string NS_GOOGLESESSION("http://www.google.com/session"); -const buzz::TQName TQN_GOOGLESESSION_SESSION(true, NS_GOOGLESESSION, "session"); -const buzz::TQName TQN_GOOGLESESSION_CANDIDATE(true, NS_GOOGLESESSION, "candidate"); -const buzz::TQName TQN_GOOGLESESSION_TARGET(true, NS_GOOGLESESSION, "target"); -const buzz::TQName TQN_GOOGLESESSION_COOKIE(true, NS_GOOGLESESSION, "cookie"); -const buzz::TQName TQN_GOOGLESESSION_REGARDING(true, NS_GOOGLESESSION, "regarding"); - -const buzz::TQName TQN_TYPE(true, buzz::STR_EMPTY, "type"); -const buzz::TQName TQN_ID(true, buzz::STR_EMPTY, "id"); -const buzz::TQName TQN_INITIATOR(true, buzz::STR_EMPTY, "initiator"); -const buzz::TQName TQN_NAME(true, buzz::STR_EMPTY, "name"); -const buzz::TQName TQN_PORT(true, buzz::STR_EMPTY, "port"); -const buzz::TQName TQN_NETWORK(true, buzz::STR_EMPTY, "network"); -const buzz::TQName TQN_GENERATION(true, buzz::STR_EMPTY, "generation"); -const buzz::TQName TQN_ADDRESS(true, buzz::STR_EMPTY, "address"); -const buzz::TQName TQN_USERNAME(true, buzz::STR_EMPTY, "username"); -const buzz::TQName TQN_PASSWORD(true, buzz::STR_EMPTY, "password"); -const buzz::TQName TQN_PREFERENCE(true, buzz::STR_EMPTY, "preference"); -const buzz::TQName TQN_PROTOCOL(true, buzz::STR_EMPTY, "protocol"); -const buzz::TQName TQN_KEY(true, buzz::STR_EMPTY, "key"); - -class XmlCookie: public SessionMessage::Cookie { -public: - XmlCookie(const buzz::XmlElement* elem) - : elem_(new buzz::XmlElement(*elem)) { - } - - virtual ~XmlCookie() { - delete elem_; - } - - const buzz::XmlElement* elem() const { return elem_; } - - virtual Cookie* Copy() { - return new XmlCookie(elem_); - } - -private: - buzz::XmlElement* elem_; -}; - -SessionClient::SessionClient(SessionManager *session_manager) { - session_manager_ = session_manager; - session_manager_->SignalSessionCreate.connect(this, &SessionClient::OnSessionCreateSlot); - session_manager_->SignalSessionDestroy.connect(this, &SessionClient::OnSessionDestroySlot); -} - -SessionClient::~SessionClient() { -} - -void SessionClient::OnSessionCreateSlot(Session *session, bool received_initiate) { - // Does this session belong to this session client? - if (session->name() == GetSessionDescriptionName()) { - session->SignalOutgoingMessage.connect(this, &SessionClient::OnOutgoingMessage); - OnSessionCreate(session, received_initiate); - } -} - -void SessionClient::OnSessionDestroySlot(Session *session) { - if (session->name() == GetSessionDescriptionName()) { - session->SignalOutgoingMessage.disconnect(this); - OnSessionDestroy(session); - } -} - -bool SessionClient::IsClientStanza(const buzz::XmlElement *stanza) { - // Is it a IQ set stanza? - if (stanza->Name() != buzz::TQN_IQ) - return false; - if (stanza->Attr(buzz::TQN_TYPE) != buzz::STR_SET) - return false; - - // Make sure it has the right child element - const buzz::XmlElement* element - = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); - if (element == NULL) - return false; - - // Is it one of the allowed types? - std::string type; - if (element->HasAttr(TQN_TYPE)) { - type = element->Attr(TQN_TYPE); - if (type != "initiate" && type != "accept" && type != "modify" && - type != "candidates" && type != "reject" && type != "redirect" && - type != "terminate") { - return false; - } - } - - // Does this client own the session description namespace? - buzz::TQName qn_session_desc(GetSessionDescriptionName(), "description"); - const buzz::XmlElement* description = element->FirstNamed(qn_session_desc); - if (type == "initiate" || type == "accept" || type == "modify") { - if (description == NULL) - return false; - } else { - if (description != NULL) - return false; - } - - // It's good - return true; -} - -void SessionClient::OnIncomingStanza(const buzz::XmlElement *stanza) { - SessionMessage message; - if (!ParseIncomingMessage(stanza, message)) - return; - - session_manager_->OnIncomingMessage(message); -} - -void SessionClient::OnFailedSend(const buzz::XmlElement *original_stanza, - const buzz::XmlElement *failure_stanza) { - SessionMessage message; - if (!ParseIncomingMessage(original_stanza, message)) - return; - - // Note the from/to represents the *original* stanza and not the from/to - // on any return path - session_manager_->OnIncomingError(message); -} - -bool SessionClient::ParseIncomingMessage(const buzz::XmlElement *stanza, - SessionMessage& message) { - // Parse stanza into SessionMessage - const buzz::XmlElement* element - = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); - - std::string type = element->Attr(TQN_TYPE); - if (type == "initiate" || type == "accept" || type == "modify") { - ParseInitiateAcceptModify(stanza, message); - } else if (type == "candidates") { - ParseCandidates(stanza, message); - } else if (type == "reject" || type == "terminate") { - ParseRejectTerminate(stanza, message); - } else if (type == "redirect") { - ParseRedirect(stanza, message); - } else { - return false; - } - - return true; -} - -void SessionClient::ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message) { - if (stanza->HasAttr(buzz::TQN_FROM)) - message.set_from(stanza->Attr(buzz::TQN_FROM)); - if (stanza->HasAttr(buzz::TQN_TO)) - message.set_to(stanza->Attr(buzz::TQN_TO)); - - const buzz::XmlElement *element - = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); - if (element->HasAttr(TQN_ID)) - message.session_id().set_id_str(element->Attr(TQN_ID)); - - if (element->HasAttr(TQN_INITIATOR)) - message.session_id().set_initiator(element->Attr(TQN_INITIATOR)); - - std::string type = element->Attr(TQN_TYPE); - if (type == "initiate") { - message.set_type(SessionMessage::TYPE_INITIATE); - } else if (type == "accept") { - message.set_type(SessionMessage::TYPE_ACCEPT); - } else if (type == "modify") { - message.set_type(SessionMessage::TYPE_MODIFY); - } else if (type == "candidates") { - message.set_type(SessionMessage::TYPE_CANDIDATES); - } else if (type == "reject") { - message.set_type(SessionMessage::TYPE_REJECT); - } else if (type == "redirect") { - message.set_type(SessionMessage::TYPE_REDIRECT); - } else if (type == "terminate") { - message.set_type(SessionMessage::TYPE_TERMINATE); - } else { - assert(false); - } -} - -void SessionClient::ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message) { - // Pull the standard header pieces out - ParseHeader(stanza, message); - - // Parse session description - const buzz::XmlElement *session - = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); - buzz::TQName qn_session_desc(GetSessionDescriptionName(), "description"); - const buzz::XmlElement* desc_elem = session->FirstNamed(qn_session_desc); - const SessionDescription *description = NULL; - if (desc_elem) - description = CreateSessionDescription(desc_elem); - message.set_name(GetSessionDescriptionName()); - message.set_description(description); -} - -void SessionClient::ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message) { - // Pull the standard header pieces out - ParseHeader(stanza, message); - - // Parse candidates and session description - std::vector candidates; - const buzz::XmlElement *element - = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); - const buzz::XmlElement *child = element->FirstElement(); - while (child != NULL) { - if (child->Name() == TQN_GOOGLESESSION_CANDIDATE) { - Candidate candidate; - if (ParseCandidate(child, &candidate)) - candidates.push_back(candidate); - } - child = child->NextElement(); - } - message.set_name(GetSessionDescriptionName()); - message.set_candidates(candidates); -} - -void SessionClient::ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message) { - // Reject and terminate are very simple - ParseHeader(stanza, message); -} - -bool SessionClient::ParseCandidate(const buzz::XmlElement *child, - Candidate* candidate) { - // Check for all of the required attributes. - if (!child->HasAttr(TQN_NAME) || - !child->HasAttr(TQN_ADDRESS) || - !child->HasAttr(TQN_PORT) || - !child->HasAttr(TQN_USERNAME) || - !child->HasAttr(TQN_PREFERENCE) || - !child->HasAttr(TQN_PROTOCOL) || - !child->HasAttr(TQN_GENERATION)) { - LOG(LERROR) << "Candidate missing required attribute"; - return false; - } - - SocketAddress address; - address.SetIP(child->Attr(TQN_ADDRESS)); - std::istringstream ist(child->Attr(TQN_PORT)); - int port; - ist >> port; - address.SetPort(port); - - if (address.IsAny()) { - LOG(LERROR) << "Candidate has address 0"; - return false; - } - - // Always disallow addresses that refer to the local host. - if (address.IsLocalIP()) { - LOG(LERROR) << "Candidate has local IP address"; - return false; - } - - // Disallow all ports below 1024, except for 80 and 443 on public addresses. - if (port < 1024) { - if ((port != 80) && (port != 443)) { - LOG(LERROR) << "Candidate has port below 1024, not 80 or 443"; - return false; - } - if (address.IsPrivateIP()) { - LOG(LERROR) << "Candidate has port of 80 or 443 with private IP address"; - return false; - } - } - - candidate->set_name(child->Attr(TQN_NAME)); - candidate->set_address(address); - candidate->set_username(child->Attr(TQN_USERNAME)); - candidate->set_preference_str(child->Attr(TQN_PREFERENCE)); - candidate->set_protocol(child->Attr(TQN_PROTOCOL)); - candidate->set_generation_str(child->Attr(TQN_GENERATION)); - - // Check that the username is not too long and does not use any bad chars. - if (candidate->username().size() > kMaxUsernameSize) { - LOG(LERROR) << "Candidate username is too long"; - return false; - } - if (!IsBase64Encoded(candidate->username())) { - LOG(LERROR) << "Candidate username has non-base64 encoded characters"; - return false; - } - - // Look for the non-required attributes. - if (child->HasAttr(TQN_PASSWORD)) - candidate->set_password(child->Attr(TQN_PASSWORD)); - if (child->HasAttr(TQN_TYPE)) - candidate->set_type(child->Attr(TQN_TYPE)); - if (child->HasAttr(TQN_NETWORK)) - candidate->set_network_name(child->Attr(TQN_NETWORK)); - - return true; -} - -void SessionClient::ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message) { - // Pull the standard header pieces out - ParseHeader(stanza, message); - const buzz::XmlElement *session = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); - - // Parse the target and cookie. - - const buzz::XmlElement* target = session->FirstNamed(TQN_GOOGLESESSION_TARGET); - if (target) - message.set_redirect_target(target->Attr(TQN_NAME)); - - const buzz::XmlElement* cookie = session->FirstNamed(TQN_GOOGLESESSION_COOKIE); - if (cookie) - message.set_redirect_cookie(new XmlCookie(cookie)); -} - -void SessionClient::OnOutgoingMessage(Session *session, const SessionMessage &message) { - // Translate the message into an XMPP stanza - - buzz::XmlElement *result = NULL; - switch (message.type()) { - case SessionMessage::TYPE_INITIATE: - case SessionMessage::TYPE_ACCEPT: - case SessionMessage::TYPE_MODIFY: - result = TranslateInitiateAcceptModify(message); - break; - - case SessionMessage::TYPE_CANDIDATES: - result = TranslateCandidates(message); - break; - - case SessionMessage::TYPE_REJECT: - case SessionMessage::TYPE_TERMINATE: - result = TranslateRejectTerminate(message); - break; - - case SessionMessage::TYPE_REDIRECT: - result = TranslateRedirect(message); - break; - } - - // Send the stanza. Note that SessionClient is passing on ownership - // of result. - if (result != NULL) { - SignalSendStanza(this, result); - } -} - -buzz::XmlElement *SessionClient::TranslateHeader(const SessionMessage &message) { - buzz::XmlElement *result = new buzz::XmlElement(buzz::TQN_IQ); - result->AddAttr(buzz::TQN_TO, message.to()); - result->AddAttr(buzz::TQN_TYPE, buzz::STR_SET); - buzz::XmlElement *session = new buzz::XmlElement(TQN_GOOGLESESSION_SESSION, true); - result->AddElement(session); - switch (message.type()) { - case SessionMessage::TYPE_INITIATE: - session->AddAttr(TQN_TYPE, "initiate"); - break; - case SessionMessage::TYPE_ACCEPT: - session->AddAttr(TQN_TYPE, "accept"); - break; - case SessionMessage::TYPE_MODIFY: - session->AddAttr(TQN_TYPE, "modify"); - break; - case SessionMessage::TYPE_CANDIDATES: - session->AddAttr(TQN_TYPE, "candidates"); - break; - case SessionMessage::TYPE_REJECT: - session->AddAttr(TQN_TYPE, "reject"); - break; - case SessionMessage::TYPE_REDIRECT: - session->AddAttr(TQN_TYPE, "redirect"); - break; - case SessionMessage::TYPE_TERMINATE: - session->AddAttr(TQN_TYPE, "terminate"); - break; - } - session->AddAttr(TQN_ID, message.session_id().id_str()); - session->AddAttr(TQN_INITIATOR, message.session_id().initiator()); - return result; -} - -buzz::XmlElement *SessionClient::TranslateCandidate(const Candidate &candidate) { - buzz::XmlElement *result = new buzz::XmlElement(TQN_GOOGLESESSION_CANDIDATE); - result->AddAttr(TQN_NAME, candidate.name()); - result->AddAttr(TQN_ADDRESS, candidate.address().IPAsString()); - result->AddAttr(TQN_PORT, candidate.address().PortAsString()); - result->AddAttr(TQN_USERNAME, candidate.username()); - result->AddAttr(TQN_PASSWORD, candidate.password()); - result->AddAttr(TQN_PREFERENCE, candidate.preference_str()); - result->AddAttr(TQN_PROTOCOL, candidate.protocol()); - result->AddAttr(TQN_TYPE, candidate.type()); - result->AddAttr(TQN_NETWORK, candidate.network_name()); - result->AddAttr(TQN_GENERATION, candidate.generation_str()); - return result; -} - -buzz::XmlElement *SessionClient::TranslateInitiateAcceptModify(const SessionMessage &message) { - // Header info common to all message types - buzz::XmlElement *result = TranslateHeader(message); - buzz::XmlElement *session = result->FirstNamed(TQN_GOOGLESESSION_SESSION); - - // Candidates - assert(message.candidates().size() == 0); - - // Session Description - buzz::XmlElement* description = TranslateSessionDescription(message.description()); - assert(description->Name().LocalPart() == "description"); - assert(description->Name().Namespace() == GetSessionDescriptionName()); - session->AddElement(description); - - if (message.redirect_cookie() != NULL) { - const buzz::XmlElement* cookie = - reinterpret_cast(message.redirect_cookie())->elem(); - for (const buzz::XmlElement* elem = cookie->FirstElement(); elem; elem = elem->NextElement()) - session->AddElement(new buzz::XmlElement(*elem)); - } - - return result; -} - -buzz::XmlElement *SessionClient::TranslateCandidates(const SessionMessage &message) { - // Header info common to all message types - buzz::XmlElement *result = TranslateHeader(message); - buzz::XmlElement *session = result->FirstNamed(TQN_GOOGLESESSION_SESSION); - - // Candidates - std::vector::const_iterator it; - for (it = message.candidates().begin(); it != message.candidates().end(); it++) - session->AddElement(TranslateCandidate(*it)); - - return result; -} - -buzz::XmlElement *SessionClient::TranslateRejectTerminate(const SessionMessage &message) { - // These messages are simple, and only have a header - return TranslateHeader(message); -} - -buzz::XmlElement *SessionClient::TranslateRedirect(const SessionMessage &message) { - // Header info common to all message types - buzz::XmlElement *result = TranslateHeader(message); - buzz::XmlElement *session = result->FirstNamed(TQN_GOOGLESESSION_SESSION); - - assert(message.candidates().size() == 0); - assert(message.description() == NULL); - - assert(message.redirect_target().size() > 0); - buzz::XmlElement* target = new buzz::XmlElement(TQN_GOOGLESESSION_TARGET); - target->AddAttr(TQN_NAME, message.redirect_target()); - session->AddElement(target); - - buzz::XmlElement* cookie = new buzz::XmlElement(TQN_GOOGLESESSION_COOKIE); - session->AddElement(cookie); - - // If the message does not have a redirect cookie, then this is a redirect - // initiated by us. We will automatically add a regarding cookie. - if (message.redirect_cookie() == NULL) { - buzz::XmlElement* regarding = new buzz::XmlElement(TQN_GOOGLESESSION_REGARDING); - regarding->AddAttr(TQN_NAME, GetJid().BareJid().Str()); - cookie->AddElement(regarding); - } else { - const buzz::XmlElement* cookie_elem = - reinterpret_cast(message.redirect_cookie())->elem(); - const buzz::XmlElement* elem; - for (elem = cookie_elem->FirstElement(); elem; elem = elem->NextElement()) - cookie->AddElement(new buzz::XmlElement(*elem)); - } - - return result; -} - -SessionManager *SessionClient::session_manager() { - return session_manager_; -} - -} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cpp new file mode 100644 index 00000000..b64c444a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cpp @@ -0,0 +1,545 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/p2p/client/sessionclient.h" +#include "talk/p2p/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/xmllite/qname.h" +#include "talk/xmpp/constants.h" +#include "talk/xmllite/xmlprinter.h" +#include +#undef SetPort + +namespace { + +// We only allow usernames to be this many characters or fewer. +const size_t kMaxUsernameSize = 16; + +} + +namespace cricket { + +#if 0 +>>>>>> + + + + ... + + + + +<<<<<< + + +>>>>>> + + + + + + +> + +<<<<<< + + +#endif + +const std::string NS_GOOGLESESSION("http://www.google.com/session"); +const buzz::TQName TQN_GOOGLESESSION_SESSION(true, NS_GOOGLESESSION, "session"); +const buzz::TQName TQN_GOOGLESESSION_CANDIDATE(true, NS_GOOGLESESSION, "candidate"); +const buzz::TQName TQN_GOOGLESESSION_TARGET(true, NS_GOOGLESESSION, "target"); +const buzz::TQName TQN_GOOGLESESSION_COOKIE(true, NS_GOOGLESESSION, "cookie"); +const buzz::TQName TQN_GOOGLESESSION_REGARDING(true, NS_GOOGLESESSION, "regarding"); + +const buzz::TQName TQN_TYPE(true, buzz::STR_EMPTY, "type"); +const buzz::TQName TQN_ID(true, buzz::STR_EMPTY, "id"); +const buzz::TQName TQN_INITIATOR(true, buzz::STR_EMPTY, "initiator"); +const buzz::TQName TQN_NAME(true, buzz::STR_EMPTY, "name"); +const buzz::TQName TQN_PORT(true, buzz::STR_EMPTY, "port"); +const buzz::TQName TQN_NETWORK(true, buzz::STR_EMPTY, "network"); +const buzz::TQName TQN_GENERATION(true, buzz::STR_EMPTY, "generation"); +const buzz::TQName TQN_ADDRESS(true, buzz::STR_EMPTY, "address"); +const buzz::TQName TQN_USERNAME(true, buzz::STR_EMPTY, "username"); +const buzz::TQName TQN_PASSWORD(true, buzz::STR_EMPTY, "password"); +const buzz::TQName TQN_PREFERENCE(true, buzz::STR_EMPTY, "preference"); +const buzz::TQName TQN_PROTOCOL(true, buzz::STR_EMPTY, "protocol"); +const buzz::TQName TQN_KEY(true, buzz::STR_EMPTY, "key"); + +class XmlCookie: public SessionMessage::Cookie { +public: + XmlCookie(const buzz::XmlElement* elem) + : elem_(new buzz::XmlElement(*elem)) { + } + + virtual ~XmlCookie() { + delete elem_; + } + + const buzz::XmlElement* elem() const { return elem_; } + + virtual Cookie* Copy() { + return new XmlCookie(elem_); + } + +private: + buzz::XmlElement* elem_; +}; + +SessionClient::SessionClient(SessionManager *session_manager) { + session_manager_ = session_manager; + session_manager_->SignalSessionCreate.connect(this, &SessionClient::OnSessionCreateSlot); + session_manager_->SignalSessionDestroy.connect(this, &SessionClient::OnSessionDestroySlot); +} + +SessionClient::~SessionClient() { +} + +void SessionClient::OnSessionCreateSlot(Session *session, bool received_initiate) { + // Does this session belong to this session client? + if (session->name() == GetSessionDescriptionName()) { + session->SignalOutgoingMessage.connect(this, &SessionClient::OnOutgoingMessage); + OnSessionCreate(session, received_initiate); + } +} + +void SessionClient::OnSessionDestroySlot(Session *session) { + if (session->name() == GetSessionDescriptionName()) { + session->SignalOutgoingMessage.disconnect(this); + OnSessionDestroy(session); + } +} + +bool SessionClient::IsClientStanza(const buzz::XmlElement *stanza) { + // Is it a IQ set stanza? + if (stanza->Name() != buzz::TQN_IQ) + return false; + if (stanza->Attr(buzz::TQN_TYPE) != buzz::STR_SET) + return false; + + // Make sure it has the right child element + const buzz::XmlElement* element + = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); + if (element == NULL) + return false; + + // Is it one of the allowed types? + std::string type; + if (element->HasAttr(TQN_TYPE)) { + type = element->Attr(TQN_TYPE); + if (type != "initiate" && type != "accept" && type != "modify" && + type != "candidates" && type != "reject" && type != "redirect" && + type != "terminate") { + return false; + } + } + + // Does this client own the session description namespace? + buzz::TQName qn_session_desc(GetSessionDescriptionName(), "description"); + const buzz::XmlElement* description = element->FirstNamed(qn_session_desc); + if (type == "initiate" || type == "accept" || type == "modify") { + if (description == NULL) + return false; + } else { + if (description != NULL) + return false; + } + + // It's good + return true; +} + +void SessionClient::OnIncomingStanza(const buzz::XmlElement *stanza) { + SessionMessage message; + if (!ParseIncomingMessage(stanza, message)) + return; + + session_manager_->OnIncomingMessage(message); +} + +void SessionClient::OnFailedSend(const buzz::XmlElement *original_stanza, + const buzz::XmlElement *failure_stanza) { + SessionMessage message; + if (!ParseIncomingMessage(original_stanza, message)) + return; + + // Note the from/to represents the *original* stanza and not the from/to + // on any return path + session_manager_->OnIncomingError(message); +} + +bool SessionClient::ParseIncomingMessage(const buzz::XmlElement *stanza, + SessionMessage& message) { + // Parse stanza into SessionMessage + const buzz::XmlElement* element + = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); + + std::string type = element->Attr(TQN_TYPE); + if (type == "initiate" || type == "accept" || type == "modify") { + ParseInitiateAcceptModify(stanza, message); + } else if (type == "candidates") { + ParseCandidates(stanza, message); + } else if (type == "reject" || type == "terminate") { + ParseRejectTerminate(stanza, message); + } else if (type == "redirect") { + ParseRedirect(stanza, message); + } else { + return false; + } + + return true; +} + +void SessionClient::ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message) { + if (stanza->HasAttr(buzz::TQN_FROM)) + message.set_from(stanza->Attr(buzz::TQN_FROM)); + if (stanza->HasAttr(buzz::TQN_TO)) + message.set_to(stanza->Attr(buzz::TQN_TO)); + + const buzz::XmlElement *element + = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); + if (element->HasAttr(TQN_ID)) + message.session_id().set_id_str(element->Attr(TQN_ID)); + + if (element->HasAttr(TQN_INITIATOR)) + message.session_id().set_initiator(element->Attr(TQN_INITIATOR)); + + std::string type = element->Attr(TQN_TYPE); + if (type == "initiate") { + message.set_type(SessionMessage::TYPE_INITIATE); + } else if (type == "accept") { + message.set_type(SessionMessage::TYPE_ACCEPT); + } else if (type == "modify") { + message.set_type(SessionMessage::TYPE_MODIFY); + } else if (type == "candidates") { + message.set_type(SessionMessage::TYPE_CANDIDATES); + } else if (type == "reject") { + message.set_type(SessionMessage::TYPE_REJECT); + } else if (type == "redirect") { + message.set_type(SessionMessage::TYPE_REDIRECT); + } else if (type == "terminate") { + message.set_type(SessionMessage::TYPE_TERMINATE); + } else { + assert(false); + } +} + +void SessionClient::ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message) { + // Pull the standard header pieces out + ParseHeader(stanza, message); + + // Parse session description + const buzz::XmlElement *session + = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); + buzz::TQName qn_session_desc(GetSessionDescriptionName(), "description"); + const buzz::XmlElement* desc_elem = session->FirstNamed(qn_session_desc); + const SessionDescription *description = NULL; + if (desc_elem) + description = CreateSessionDescription(desc_elem); + message.set_name(GetSessionDescriptionName()); + message.set_description(description); +} + +void SessionClient::ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message) { + // Pull the standard header pieces out + ParseHeader(stanza, message); + + // Parse candidates and session description + std::vector candidates; + const buzz::XmlElement *element + = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); + const buzz::XmlElement *child = element->FirstElement(); + while (child != NULL) { + if (child->Name() == TQN_GOOGLESESSION_CANDIDATE) { + Candidate candidate; + if (ParseCandidate(child, &candidate)) + candidates.push_back(candidate); + } + child = child->NextElement(); + } + message.set_name(GetSessionDescriptionName()); + message.set_candidates(candidates); +} + +void SessionClient::ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message) { + // Reject and terminate are very simple + ParseHeader(stanza, message); +} + +bool SessionClient::ParseCandidate(const buzz::XmlElement *child, + Candidate* candidate) { + // Check for all of the required attributes. + if (!child->HasAttr(TQN_NAME) || + !child->HasAttr(TQN_ADDRESS) || + !child->HasAttr(TQN_PORT) || + !child->HasAttr(TQN_USERNAME) || + !child->HasAttr(TQN_PREFERENCE) || + !child->HasAttr(TQN_PROTOCOL) || + !child->HasAttr(TQN_GENERATION)) { + LOG(LERROR) << "Candidate missing required attribute"; + return false; + } + + SocketAddress address; + address.SetIP(child->Attr(TQN_ADDRESS)); + std::istringstream ist(child->Attr(TQN_PORT)); + int port; + ist >> port; + address.SetPort(port); + + if (address.IsAny()) { + LOG(LERROR) << "Candidate has address 0"; + return false; + } + + // Always disallow addresses that refer to the local host. + if (address.IsLocalIP()) { + LOG(LERROR) << "Candidate has local IP address"; + return false; + } + + // Disallow all ports below 1024, except for 80 and 443 on public addresses. + if (port < 1024) { + if ((port != 80) && (port != 443)) { + LOG(LERROR) << "Candidate has port below 1024, not 80 or 443"; + return false; + } + if (address.IsPrivateIP()) { + LOG(LERROR) << "Candidate has port of 80 or 443 with private IP address"; + return false; + } + } + + candidate->set_name(child->Attr(TQN_NAME)); + candidate->set_address(address); + candidate->set_username(child->Attr(TQN_USERNAME)); + candidate->set_preference_str(child->Attr(TQN_PREFERENCE)); + candidate->set_protocol(child->Attr(TQN_PROTOCOL)); + candidate->set_generation_str(child->Attr(TQN_GENERATION)); + + // Check that the username is not too long and does not use any bad chars. + if (candidate->username().size() > kMaxUsernameSize) { + LOG(LERROR) << "Candidate username is too long"; + return false; + } + if (!IsBase64Encoded(candidate->username())) { + LOG(LERROR) << "Candidate username has non-base64 encoded characters"; + return false; + } + + // Look for the non-required attributes. + if (child->HasAttr(TQN_PASSWORD)) + candidate->set_password(child->Attr(TQN_PASSWORD)); + if (child->HasAttr(TQN_TYPE)) + candidate->set_type(child->Attr(TQN_TYPE)); + if (child->HasAttr(TQN_NETWORK)) + candidate->set_network_name(child->Attr(TQN_NETWORK)); + + return true; +} + +void SessionClient::ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message) { + // Pull the standard header pieces out + ParseHeader(stanza, message); + const buzz::XmlElement *session = stanza->FirstNamed(TQN_GOOGLESESSION_SESSION); + + // Parse the target and cookie. + + const buzz::XmlElement* target = session->FirstNamed(TQN_GOOGLESESSION_TARGET); + if (target) + message.set_redirect_target(target->Attr(TQN_NAME)); + + const buzz::XmlElement* cookie = session->FirstNamed(TQN_GOOGLESESSION_COOKIE); + if (cookie) + message.set_redirect_cookie(new XmlCookie(cookie)); +} + +void SessionClient::OnOutgoingMessage(Session *session, const SessionMessage &message) { + // Translate the message into an XMPP stanza + + buzz::XmlElement *result = NULL; + switch (message.type()) { + case SessionMessage::TYPE_INITIATE: + case SessionMessage::TYPE_ACCEPT: + case SessionMessage::TYPE_MODIFY: + result = TranslateInitiateAcceptModify(message); + break; + + case SessionMessage::TYPE_CANDIDATES: + result = TranslateCandidates(message); + break; + + case SessionMessage::TYPE_REJECT: + case SessionMessage::TYPE_TERMINATE: + result = TranslateRejectTerminate(message); + break; + + case SessionMessage::TYPE_REDIRECT: + result = TranslateRedirect(message); + break; + } + + // Send the stanza. Note that SessionClient is passing on ownership + // of result. + if (result != NULL) { + SignalSendStanza(this, result); + } +} + +buzz::XmlElement *SessionClient::TranslateHeader(const SessionMessage &message) { + buzz::XmlElement *result = new buzz::XmlElement(buzz::TQN_IQ); + result->AddAttr(buzz::TQN_TO, message.to()); + result->AddAttr(buzz::TQN_TYPE, buzz::STR_SET); + buzz::XmlElement *session = new buzz::XmlElement(TQN_GOOGLESESSION_SESSION, true); + result->AddElement(session); + switch (message.type()) { + case SessionMessage::TYPE_INITIATE: + session->AddAttr(TQN_TYPE, "initiate"); + break; + case SessionMessage::TYPE_ACCEPT: + session->AddAttr(TQN_TYPE, "accept"); + break; + case SessionMessage::TYPE_MODIFY: + session->AddAttr(TQN_TYPE, "modify"); + break; + case SessionMessage::TYPE_CANDIDATES: + session->AddAttr(TQN_TYPE, "candidates"); + break; + case SessionMessage::TYPE_REJECT: + session->AddAttr(TQN_TYPE, "reject"); + break; + case SessionMessage::TYPE_REDIRECT: + session->AddAttr(TQN_TYPE, "redirect"); + break; + case SessionMessage::TYPE_TERMINATE: + session->AddAttr(TQN_TYPE, "terminate"); + break; + } + session->AddAttr(TQN_ID, message.session_id().id_str()); + session->AddAttr(TQN_INITIATOR, message.session_id().initiator()); + return result; +} + +buzz::XmlElement *SessionClient::TranslateCandidate(const Candidate &candidate) { + buzz::XmlElement *result = new buzz::XmlElement(TQN_GOOGLESESSION_CANDIDATE); + result->AddAttr(TQN_NAME, candidate.name()); + result->AddAttr(TQN_ADDRESS, candidate.address().IPAsString()); + result->AddAttr(TQN_PORT, candidate.address().PortAsString()); + result->AddAttr(TQN_USERNAME, candidate.username()); + result->AddAttr(TQN_PASSWORD, candidate.password()); + result->AddAttr(TQN_PREFERENCE, candidate.preference_str()); + result->AddAttr(TQN_PROTOCOL, candidate.protocol()); + result->AddAttr(TQN_TYPE, candidate.type()); + result->AddAttr(TQN_NETWORK, candidate.network_name()); + result->AddAttr(TQN_GENERATION, candidate.generation_str()); + return result; +} + +buzz::XmlElement *SessionClient::TranslateInitiateAcceptModify(const SessionMessage &message) { + // Header info common to all message types + buzz::XmlElement *result = TranslateHeader(message); + buzz::XmlElement *session = result->FirstNamed(TQN_GOOGLESESSION_SESSION); + + // Candidates + assert(message.candidates().size() == 0); + + // Session Description + buzz::XmlElement* description = TranslateSessionDescription(message.description()); + assert(description->Name().LocalPart() == "description"); + assert(description->Name().Namespace() == GetSessionDescriptionName()); + session->AddElement(description); + + if (message.redirect_cookie() != NULL) { + const buzz::XmlElement* cookie = + reinterpret_cast(message.redirect_cookie())->elem(); + for (const buzz::XmlElement* elem = cookie->FirstElement(); elem; elem = elem->NextElement()) + session->AddElement(new buzz::XmlElement(*elem)); + } + + return result; +} + +buzz::XmlElement *SessionClient::TranslateCandidates(const SessionMessage &message) { + // Header info common to all message types + buzz::XmlElement *result = TranslateHeader(message); + buzz::XmlElement *session = result->FirstNamed(TQN_GOOGLESESSION_SESSION); + + // Candidates + std::vector::const_iterator it; + for (it = message.candidates().begin(); it != message.candidates().end(); it++) + session->AddElement(TranslateCandidate(*it)); + + return result; +} + +buzz::XmlElement *SessionClient::TranslateRejectTerminate(const SessionMessage &message) { + // These messages are simple, and only have a header + return TranslateHeader(message); +} + +buzz::XmlElement *SessionClient::TranslateRedirect(const SessionMessage &message) { + // Header info common to all message types + buzz::XmlElement *result = TranslateHeader(message); + buzz::XmlElement *session = result->FirstNamed(TQN_GOOGLESESSION_SESSION); + + assert(message.candidates().size() == 0); + assert(message.description() == NULL); + + assert(message.redirect_target().size() > 0); + buzz::XmlElement* target = new buzz::XmlElement(TQN_GOOGLESESSION_TARGET); + target->AddAttr(TQN_NAME, message.redirect_target()); + session->AddElement(target); + + buzz::XmlElement* cookie = new buzz::XmlElement(TQN_GOOGLESESSION_COOKIE); + session->AddElement(cookie); + + // If the message does not have a redirect cookie, then this is a redirect + // initiated by us. We will automatically add a regarding cookie. + if (message.redirect_cookie() == NULL) { + buzz::XmlElement* regarding = new buzz::XmlElement(TQN_GOOGLESESSION_REGARDING); + regarding->AddAttr(TQN_NAME, GetJid().BareJid().Str()); + cookie->AddElement(regarding); + } else { + const buzz::XmlElement* cookie_elem = + reinterpret_cast(message.redirect_cookie())->elem(); + const buzz::XmlElement* elem; + for (elem = cookie_elem->FirstElement(); elem; elem = elem->NextElement()) + cookie->AddElement(new buzz::XmlElement(*elem)); + } + + return result; +} + +SessionManager *SessionClient::session_manager() { + return session_manager_; +} + +} // namespace cricket diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc deleted file mode 100644 index dd9fa67c..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc +++ /dev/null @@ -1,149 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "socketmonitor.h" -#include - -namespace cricket { - -const uint32 MSG_MONITOR_POLL = 1; -const uint32 MSG_MONITOR_START = 2; -const uint32 MSG_MONITOR_STOP = 3; -const uint32 MSG_MONITOR_SIGNAL = 4; - -SocketMonitor::SocketMonitor(P2PSocket *socket, Thread *monitor_thread) { - socket_ = socket; - monitoring_thread_ = monitor_thread; - monitoring_ = false; -} - -SocketMonitor::~SocketMonitor() { - socket_->thread()->Clear(this); - monitoring_thread_->Clear(this); -} - -void SocketMonitor::Start(int milliseconds) { - rate_ = milliseconds; - if (rate_ < 250) - rate_ = 250; - socket_->thread()->Post(this, MSG_MONITOR_START); -} - -void SocketMonitor::Stop() { - socket_->thread()->Post(this, MSG_MONITOR_STOP); -} - -void SocketMonitor::OnMessage(Message *message) { - CritScope cs(&crit_); - - switch (message->message_id) { - case MSG_MONITOR_START: - assert(Thread::Current() == socket_->thread()); - if (!monitoring_) { - monitoring_ = true; - socket_->SignalConnectionMonitor.connect(this, &SocketMonitor::OnConnectionMonitor); - PollSocket(true); - } - break; - - case MSG_MONITOR_STOP: - assert(Thread::Current() == socket_->thread()); - if (monitoring_) { - monitoring_ = false; - socket_->SignalConnectionMonitor.disconnect(this); - socket_->thread()->Clear(this); - } - break; - - case MSG_MONITOR_POLL: - assert(Thread::Current() == socket_->thread()); - PollSocket(true); - break; - - case MSG_MONITOR_SIGNAL: - { - assert(Thread::Current() == monitoring_thread_); - std::vector infos = connection_infos_; - crit_.Leave(); - SignalUpdate(this, infos); - crit_.Enter(); - } - break; - } -} - -void SocketMonitor::OnConnectionMonitor(P2PSocket *socket) { - CritScope cs(&crit_); - if (monitoring_) - PollSocket(false); -} - -void SocketMonitor::PollSocket(bool poll) { - CritScope cs(&crit_); - assert(Thread::Current() == socket_->thread()); - - // Gather connection infos - - connection_infos_.clear(); - const std::vector &connections = socket_->connections(); - std::vector::const_iterator it; - for (it = connections.begin(); it != connections.end(); it++) { - Connection *connection = *it; - ConnectionInfo info; - info.best_connection = socket_->best_connection() == connection; - info.readable = connection->read_state() == Connection::STATE_READABLE; - info.writable = connection->write_state() == Connection::STATE_WRITABLE; - info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT; - info.new_connection = false; // connection->new_connection(); - info.rtt = connection->rtt(); - info.sent_total_bytes = connection->sent_total_bytes(); - info.sent_bytes_second = connection->sent_bytes_second(); - info.recv_total_bytes = connection->recv_total_bytes(); - info.recv_bytes_second = connection->recv_bytes_second(); - info.local_candidate = connection->local_candidate(); - info.remote_candidate = connection->remote_candidate(); - info.est_quality = connection->port()->network()->quality(); - info.key = reinterpret_cast(connection); - connection_infos_.push_back(info); - } - - // Signal the monitoring thread, start another poll timer - - monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); - if (poll) - socket_->thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); -} - -P2PSocket *SocketMonitor::socket() { - return socket_; -} - -Thread *SocketMonitor::monitor_thread() { - return monitoring_thread_; -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cpp new file mode 100644 index 00000000..dd9fa67c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cpp @@ -0,0 +1,149 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "socketmonitor.h" +#include + +namespace cricket { + +const uint32 MSG_MONITOR_POLL = 1; +const uint32 MSG_MONITOR_START = 2; +const uint32 MSG_MONITOR_STOP = 3; +const uint32 MSG_MONITOR_SIGNAL = 4; + +SocketMonitor::SocketMonitor(P2PSocket *socket, Thread *monitor_thread) { + socket_ = socket; + monitoring_thread_ = monitor_thread; + monitoring_ = false; +} + +SocketMonitor::~SocketMonitor() { + socket_->thread()->Clear(this); + monitoring_thread_->Clear(this); +} + +void SocketMonitor::Start(int milliseconds) { + rate_ = milliseconds; + if (rate_ < 250) + rate_ = 250; + socket_->thread()->Post(this, MSG_MONITOR_START); +} + +void SocketMonitor::Stop() { + socket_->thread()->Post(this, MSG_MONITOR_STOP); +} + +void SocketMonitor::OnMessage(Message *message) { + CritScope cs(&crit_); + + switch (message->message_id) { + case MSG_MONITOR_START: + assert(Thread::Current() == socket_->thread()); + if (!monitoring_) { + monitoring_ = true; + socket_->SignalConnectionMonitor.connect(this, &SocketMonitor::OnConnectionMonitor); + PollSocket(true); + } + break; + + case MSG_MONITOR_STOP: + assert(Thread::Current() == socket_->thread()); + if (monitoring_) { + monitoring_ = false; + socket_->SignalConnectionMonitor.disconnect(this); + socket_->thread()->Clear(this); + } + break; + + case MSG_MONITOR_POLL: + assert(Thread::Current() == socket_->thread()); + PollSocket(true); + break; + + case MSG_MONITOR_SIGNAL: + { + assert(Thread::Current() == monitoring_thread_); + std::vector infos = connection_infos_; + crit_.Leave(); + SignalUpdate(this, infos); + crit_.Enter(); + } + break; + } +} + +void SocketMonitor::OnConnectionMonitor(P2PSocket *socket) { + CritScope cs(&crit_); + if (monitoring_) + PollSocket(false); +} + +void SocketMonitor::PollSocket(bool poll) { + CritScope cs(&crit_); + assert(Thread::Current() == socket_->thread()); + + // Gather connection infos + + connection_infos_.clear(); + const std::vector &connections = socket_->connections(); + std::vector::const_iterator it; + for (it = connections.begin(); it != connections.end(); it++) { + Connection *connection = *it; + ConnectionInfo info; + info.best_connection = socket_->best_connection() == connection; + info.readable = connection->read_state() == Connection::STATE_READABLE; + info.writable = connection->write_state() == Connection::STATE_WRITABLE; + info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT; + info.new_connection = false; // connection->new_connection(); + info.rtt = connection->rtt(); + info.sent_total_bytes = connection->sent_total_bytes(); + info.sent_bytes_second = connection->sent_bytes_second(); + info.recv_total_bytes = connection->recv_total_bytes(); + info.recv_bytes_second = connection->recv_bytes_second(); + info.local_candidate = connection->local_candidate(); + info.remote_candidate = connection->remote_candidate(); + info.est_quality = connection->port()->network()->quality(); + info.key = reinterpret_cast(connection); + connection_infos_.push_back(info); + } + + // Signal the monitoring thread, start another poll timer + + monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); + if (poll) + socket_->thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); +} + +P2PSocket *SocketMonitor::socket() { + return socket_; +} + +Thread *SocketMonitor::monitor_thread() { + return monitoring_thread_; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt index 3f22e535..164ce587 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/CMakeLists.txt @@ -29,6 +29,6 @@ include_directories( tde_add_library( cricketsessionphone STATIC_PIC SOURCES - audiomonitor.cc channelmanager.cc voicechannel.cc call.cc - phonesessionclient.cc linphonemediaengine.cc + audiomonitor.cpp channelmanager.cpp voicechannel.cpp call.cpp + phonesessionclient.cpp linphonemediaengine.cpp ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am index aef28293..695fd497 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am @@ -1,9 +1,9 @@ -libcricketsessionphone_la_SOURCES = audiomonitor.cc \ - channelmanager.cc \ - voicechannel.cc \ - call.cc \ - phonesessionclient.cc \ - linphonemediaengine.cc +libcricketsessionphone_la_SOURCES = audiomonitor.cpp \ + channelmanager.cpp \ + voicechannel.cpp \ + call.cpp \ + phonesessionclient.cpp \ + linphonemediaengine.cpp noinst_HEADERS = audiomonitor.h \ channelmanager.h \ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc deleted file mode 100644 index c1b63d1b..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/session/phone/audiomonitor.h" -#include "talk/session/phone/voicechannel.h" -#include - -namespace cricket { - -const uint32 MSG_MONITOR_POLL = 1; -const uint32 MSG_MONITOR_START = 2; -const uint32 MSG_MONITOR_STOP = 3; -const uint32 MSG_MONITOR_SIGNAL = 4; - -AudioMonitor::AudioMonitor(VoiceChannel *voice_channel, Thread *monitor_thread) { - voice_channel_ = voice_channel; - monitoring_thread_ = monitor_thread; - monitoring_ = false; -} - -AudioMonitor::~AudioMonitor() { - voice_channel_->worker_thread()->Clear(this); - monitoring_thread_->Clear(this); -} - -void AudioMonitor::Start(int milliseconds) { - rate_ = milliseconds; - if (rate_ < 100) - rate_ = 100; - voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START); -} - -void AudioMonitor::Stop() { - voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP); -} - -void AudioMonitor::OnMessage(Message *message) { - CritScope cs(&crit_); - - switch (message->message_id) { - case MSG_MONITOR_START: - assert(Thread::Current() == voice_channel_->worker_thread()); - if (!monitoring_) { - monitoring_ = true; - PollVoiceChannel(); - } - break; - - case MSG_MONITOR_STOP: - assert(Thread::Current() == voice_channel_->worker_thread()); - if (monitoring_) { - monitoring_ = false; - voice_channel_->worker_thread()->Clear(this); - } - break; - - case MSG_MONITOR_POLL: - assert(Thread::Current() == voice_channel_->worker_thread()); - PollVoiceChannel(); - break; - - case MSG_MONITOR_SIGNAL: - { - assert(Thread::Current() == monitoring_thread_); - AudioInfo info = audio_info_; - crit_.Leave(); - SignalUpdate(this, audio_info_); - crit_.Enter(); - } - break; - } -} - -void AudioMonitor::PollVoiceChannel() { - CritScope cs(&crit_); - assert(Thread::Current() == voice_channel_->worker_thread()); - - // Gather connection infos - audio_info_.input_level = voice_channel_->GetInputLevel_w(); - audio_info_.output_level = voice_channel_->GetOutputLevel_w(); - - // Signal the monitoring thread, start another poll timer - monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); - voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); -} - -VoiceChannel *AudioMonitor::voice_channel() { - return voice_channel_; -} - -Thread *AudioMonitor::monitor_thread() { - return monitoring_thread_; -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cpp new file mode 100644 index 00000000..c1b63d1b --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cpp @@ -0,0 +1,119 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/session/phone/audiomonitor.h" +#include "talk/session/phone/voicechannel.h" +#include + +namespace cricket { + +const uint32 MSG_MONITOR_POLL = 1; +const uint32 MSG_MONITOR_START = 2; +const uint32 MSG_MONITOR_STOP = 3; +const uint32 MSG_MONITOR_SIGNAL = 4; + +AudioMonitor::AudioMonitor(VoiceChannel *voice_channel, Thread *monitor_thread) { + voice_channel_ = voice_channel; + monitoring_thread_ = monitor_thread; + monitoring_ = false; +} + +AudioMonitor::~AudioMonitor() { + voice_channel_->worker_thread()->Clear(this); + monitoring_thread_->Clear(this); +} + +void AudioMonitor::Start(int milliseconds) { + rate_ = milliseconds; + if (rate_ < 100) + rate_ = 100; + voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START); +} + +void AudioMonitor::Stop() { + voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP); +} + +void AudioMonitor::OnMessage(Message *message) { + CritScope cs(&crit_); + + switch (message->message_id) { + case MSG_MONITOR_START: + assert(Thread::Current() == voice_channel_->worker_thread()); + if (!monitoring_) { + monitoring_ = true; + PollVoiceChannel(); + } + break; + + case MSG_MONITOR_STOP: + assert(Thread::Current() == voice_channel_->worker_thread()); + if (monitoring_) { + monitoring_ = false; + voice_channel_->worker_thread()->Clear(this); + } + break; + + case MSG_MONITOR_POLL: + assert(Thread::Current() == voice_channel_->worker_thread()); + PollVoiceChannel(); + break; + + case MSG_MONITOR_SIGNAL: + { + assert(Thread::Current() == monitoring_thread_); + AudioInfo info = audio_info_; + crit_.Leave(); + SignalUpdate(this, audio_info_); + crit_.Enter(); + } + break; + } +} + +void AudioMonitor::PollVoiceChannel() { + CritScope cs(&crit_); + assert(Thread::Current() == voice_channel_->worker_thread()); + + // Gather connection infos + audio_info_.input_level = voice_channel_->GetInputLevel_w(); + audio_info_.output_level = voice_channel_->GetOutputLevel_w(); + + // Signal the monitoring thread, start another poll timer + monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); + voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); +} + +VoiceChannel *AudioMonitor::voice_channel() { + return voice_channel_; +} + +Thread *AudioMonitor::monitor_thread() { + return monitoring_thread_; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc deleted file mode 100644 index 31b12e92..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc +++ /dev/null @@ -1,258 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/thread.h" -#include "talk/p2p/base/helpers.h" -#include "talk/session/phone/call.h" - -namespace cricket { - -const uint32 MSG_CHECKAUTODESTROY = 1; - -Call::Call(PhoneSessionClient *session_client) : muted_(false) { - session_client_ = session_client; - id_ = CreateRandomId(); -} - -Call::~Call() { - while (sessions_.begin() != sessions_.end()) { - Session *session = sessions_[0]; - RemoveSession(session); - session_client_->session_manager()->DestroySession(session); - } - Thread::Current()->Clear(this); -} - -Session *Call::InitiateSession(const buzz::Jid &jid) { - Session *session = session_client_->CreateSession(this); - AddSession(session); - session->Initiate(jid.Str(), session_client_->CreateOfferSessionDescription()); - return session; -} - -void Call::AcceptSession(Session *session) { - std::vector::iterator it; - it = std::find(sessions_.begin(), sessions_.end(), session); - assert(it != sessions_.end()); - if (it != sessions_.end()) - session->Accept(session_client_->CreateAcceptSessionDescription(session->remote_description())); -} - -void Call::RedirectSession(Session *session, const buzz::Jid &to) { - std::vector::iterator it; - it = std::find(sessions_.begin(), sessions_.end(), session); - assert(it != sessions_.end()); - if (it != sessions_.end()) - session->Redirect(to.Str()); -} - -void Call::RejectSession(Session *session) { - std::vector::iterator it; - it = std::find(sessions_.begin(), sessions_.end(), session); - assert(it != sessions_.end()); - if (it != sessions_.end()) - session->Reject(); -} - -void Call::TerminateSession(Session *session) { - assert(std::find(sessions_.begin(), sessions_.end(), session) != sessions_.end()); - std::vector::iterator it; - it = std::find(sessions_.begin(), sessions_.end(), session); - if (it != sessions_.end()) - (*it)->Terminate(); -} - -void Call::Terminate() { - // There may be more than one session to terminate - std::vector::iterator it = sessions_.begin(); - for (it = sessions_.begin(); it != sessions_.end(); it++) - TerminateSession(*it); -} - -void Call::OnMessage(Message *message) { - switch (message->message_id) { - case MSG_CHECKAUTODESTROY: - // If no more sessions for this call, delete it - if (sessions_.size() == 0) - session_client_->DestroyCall(this); - break; - } -} - -const std::vector &Call::sessions() { - return sessions_; -} - -void Call::AddSession(Session *session) { - // Add session to list, create voice channel for this session - sessions_.push_back(session); - session->SignalState.connect(this, &Call::OnSessionState); - session->SignalError.connect(this, &Call::OnSessionError); - - VoiceChannel *channel = session_client_->channel_manager()->CreateVoiceChannel(session); - channel_map_[session->id()] = channel; - - // If this call has the focus, enable this channel - if (session_client_->GetFocus() == this) - channel->Enable(true); - - // Signal client - SignalAddSession(this, session); -} - -void Call::RemoveSession(Session *session) { - // Remove session from list - std::vector::iterator it_session; - it_session = std::find(sessions_.begin(), sessions_.end(), session); - if (it_session == sessions_.end()) - return; - sessions_.erase(it_session); - - // Destroy session channel - std::map::iterator it_channel; - it_channel = channel_map_.find(session->id()); - if (it_channel != channel_map_.end()) { - VoiceChannel *channel = it_channel->second; - channel_map_.erase(it_channel); - session_client_->channel_manager()->DestroyVoiceChannel(channel); - } - - // Signal client - SignalRemoveSession(this, session); - - // The call auto destroys when the lass session is removed - Thread::Current()->Post(this, MSG_CHECKAUTODESTROY); -} - -VoiceChannel* Call::GetChannel(Session* session) { - std::map::iterator it = channel_map_.find(session->id()); - assert(it != channel_map_.end()); - return it->second; -} - -void Call::EnableChannels(bool enable) { - std::vector::iterator it; - for (it = sessions_.begin(); it != sessions_.end(); it++) { - VoiceChannel *channel = channel_map_[(*it)->id()]; - if (channel != NULL) - channel->Enable(enable); - } -} - -void Call::Mute(bool mute) { - muted_ = mute; - std::vector::iterator it; - for (it = sessions_.begin(); it != sessions_.end(); it++) { - VoiceChannel *channel = channel_map_[(*it)->id()]; - if (channel != NULL) - channel->Mute(mute); - } -} - -void Call::Join(Call *call, bool enable) { - while (call->sessions_.size() != 0) { - // Move session - Session *session = call->sessions_[0]; - call->sessions_.erase(call->sessions_.begin()); - sessions_.push_back(session); - session->SignalState.connect(this, &Call::OnSessionState); - session->SignalError.connect(this, &Call::OnSessionError); - - // Move channel - std::map::iterator it_channel; - it_channel = call->channel_map_.find(session->id()); - if (it_channel != call->channel_map_.end()) { - VoiceChannel *channel = (*it_channel).second; - call->channel_map_.erase(it_channel); - channel_map_[session->id()] = channel; - channel->Enable(enable); - } - } -} - -void Call::StartConnectionMonitor(Session *session, int cms) { - std::map::iterator it_channel; - it_channel = channel_map_.find(session->id()); - if (it_channel != channel_map_.end()) { - VoiceChannel *channel = (*it_channel).second; - channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor); - channel->StartConnectionMonitor(cms); - } -} - -void Call::StopConnectionMonitor(Session *session) { - std::map::iterator it_channel; - it_channel = channel_map_.find(session->id()); - if (it_channel != channel_map_.end()) { - VoiceChannel *channel = (*it_channel).second; - channel->StopConnectionMonitor(); - channel->SignalConnectionMonitor.disconnect(this); - } -} - -void Call::StartAudioMonitor(Session *session, int cms) { - std::map::iterator it_channel; - it_channel = channel_map_.find(session->id()); - if (it_channel != channel_map_.end()) { - VoiceChannel *channel = (*it_channel).second; - channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor); - channel->StartAudioMonitor(cms); - } -} - -void Call::StopAudioMonitor(Session *session) { - std::map::iterator it_channel; - it_channel = channel_map_.find(session->id()); - if (it_channel != channel_map_.end()) { - VoiceChannel *channel = (*it_channel).second; - channel->StopAudioMonitor(); - channel->SignalAudioMonitor.disconnect(this); - } -} - - -void Call::OnConnectionMonitor(VoiceChannel *channel, const std::vector &infos) { - SignalConnectionMonitor(this, channel->session(), infos); -} - -void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) { - SignalAudioMonitor(this, channel->session(), info); -} - -uint32 Call::id() { - return id_; -} - -void Call::OnSessionState(Session *session, Session::State state) { - SignalSessionState(this, session, state); -} - -void Call::OnSessionError(Session *session, Session::Error error) { - SignalSessionError(this, session, error); -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cpp new file mode 100644 index 00000000..31b12e92 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cpp @@ -0,0 +1,258 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/thread.h" +#include "talk/p2p/base/helpers.h" +#include "talk/session/phone/call.h" + +namespace cricket { + +const uint32 MSG_CHECKAUTODESTROY = 1; + +Call::Call(PhoneSessionClient *session_client) : muted_(false) { + session_client_ = session_client; + id_ = CreateRandomId(); +} + +Call::~Call() { + while (sessions_.begin() != sessions_.end()) { + Session *session = sessions_[0]; + RemoveSession(session); + session_client_->session_manager()->DestroySession(session); + } + Thread::Current()->Clear(this); +} + +Session *Call::InitiateSession(const buzz::Jid &jid) { + Session *session = session_client_->CreateSession(this); + AddSession(session); + session->Initiate(jid.Str(), session_client_->CreateOfferSessionDescription()); + return session; +} + +void Call::AcceptSession(Session *session) { + std::vector::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Accept(session_client_->CreateAcceptSessionDescription(session->remote_description())); +} + +void Call::RedirectSession(Session *session, const buzz::Jid &to) { + std::vector::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Redirect(to.Str()); +} + +void Call::RejectSession(Session *session) { + std::vector::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Reject(); +} + +void Call::TerminateSession(Session *session) { + assert(std::find(sessions_.begin(), sessions_.end(), session) != sessions_.end()); + std::vector::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + if (it != sessions_.end()) + (*it)->Terminate(); +} + +void Call::Terminate() { + // There may be more than one session to terminate + std::vector::iterator it = sessions_.begin(); + for (it = sessions_.begin(); it != sessions_.end(); it++) + TerminateSession(*it); +} + +void Call::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CHECKAUTODESTROY: + // If no more sessions for this call, delete it + if (sessions_.size() == 0) + session_client_->DestroyCall(this); + break; + } +} + +const std::vector &Call::sessions() { + return sessions_; +} + +void Call::AddSession(Session *session) { + // Add session to list, create voice channel for this session + sessions_.push_back(session); + session->SignalState.connect(this, &Call::OnSessionState); + session->SignalError.connect(this, &Call::OnSessionError); + + VoiceChannel *channel = session_client_->channel_manager()->CreateVoiceChannel(session); + channel_map_[session->id()] = channel; + + // If this call has the focus, enable this channel + if (session_client_->GetFocus() == this) + channel->Enable(true); + + // Signal client + SignalAddSession(this, session); +} + +void Call::RemoveSession(Session *session) { + // Remove session from list + std::vector::iterator it_session; + it_session = std::find(sessions_.begin(), sessions_.end(), session); + if (it_session == sessions_.end()) + return; + sessions_.erase(it_session); + + // Destroy session channel + std::map::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = it_channel->second; + channel_map_.erase(it_channel); + session_client_->channel_manager()->DestroyVoiceChannel(channel); + } + + // Signal client + SignalRemoveSession(this, session); + + // The call auto destroys when the lass session is removed + Thread::Current()->Post(this, MSG_CHECKAUTODESTROY); +} + +VoiceChannel* Call::GetChannel(Session* session) { + std::map::iterator it = channel_map_.find(session->id()); + assert(it != channel_map_.end()); + return it->second; +} + +void Call::EnableChannels(bool enable) { + std::vector::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); it++) { + VoiceChannel *channel = channel_map_[(*it)->id()]; + if (channel != NULL) + channel->Enable(enable); + } +} + +void Call::Mute(bool mute) { + muted_ = mute; + std::vector::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); it++) { + VoiceChannel *channel = channel_map_[(*it)->id()]; + if (channel != NULL) + channel->Mute(mute); + } +} + +void Call::Join(Call *call, bool enable) { + while (call->sessions_.size() != 0) { + // Move session + Session *session = call->sessions_[0]; + call->sessions_.erase(call->sessions_.begin()); + sessions_.push_back(session); + session->SignalState.connect(this, &Call::OnSessionState); + session->SignalError.connect(this, &Call::OnSessionError); + + // Move channel + std::map::iterator it_channel; + it_channel = call->channel_map_.find(session->id()); + if (it_channel != call->channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + call->channel_map_.erase(it_channel); + channel_map_[session->id()] = channel; + channel->Enable(enable); + } + } +} + +void Call::StartConnectionMonitor(Session *session, int cms) { + std::map::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor); + channel->StartConnectionMonitor(cms); + } +} + +void Call::StopConnectionMonitor(Session *session) { + std::map::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->StopConnectionMonitor(); + channel->SignalConnectionMonitor.disconnect(this); + } +} + +void Call::StartAudioMonitor(Session *session, int cms) { + std::map::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor); + channel->StartAudioMonitor(cms); + } +} + +void Call::StopAudioMonitor(Session *session) { + std::map::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->StopAudioMonitor(); + channel->SignalAudioMonitor.disconnect(this); + } +} + + +void Call::OnConnectionMonitor(VoiceChannel *channel, const std::vector &infos) { + SignalConnectionMonitor(this, channel->session(), infos); +} + +void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) { + SignalAudioMonitor(this, channel->session(), info); +} + +uint32 Call::id() { + return id_; +} + +void Call::OnSessionState(Session *session, Session::State state) { + SignalSessionState(this, session, state); +} + +void Call::OnSessionError(Session *session, Session::Error error) { + SignalSessionError(this, session, error); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc deleted file mode 100644 index 98634b12..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc +++ /dev/null @@ -1,203 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#ifdef HAVE_GIPS -#include "talk/session/phone/gipsmediaengine.h" -#else -#include "talk/session/phone/linphonemediaengine.h" -#endif -#include "channelmanager.h" -#include -#include -namespace cricket { - -const uint32 MSG_CREATEVOICECHANNEL = 1; -const uint32 MSG_DESTROYVOICECHANNEL = 2; -const uint32 MSG_SETAUDIOOPTIONS = 3; - -ChannelManager::ChannelManager(Thread *worker_thread) { -#ifdef HAVE_GIPS - media_engine_ = new GipsMediaEngine(); -#else - media_engine_ = new LinphoneMediaEngine(); -#endif - worker_thread_ = worker_thread; - initialized_ = false; - Init(); -} - -ChannelManager::~ChannelManager() { - Exit(); -} - -MediaEngine *ChannelManager::media_engine() { - return media_engine_; -} - -bool ChannelManager::Init() { - initialized_ = media_engine_->Init(); - return initialized_; -} - -void ChannelManager::Exit() { - if (!initialized_) - return; - - // Need to destroy the voice channels - - while (true) { - crit_.Enter(); - VoiceChannel *channel = NULL; - if (channels_.begin() != channels_.end()) - channel = channels_[0]; - crit_.Leave(); - if (channel == NULL) - break; - delete channel; - } - media_engine_->Terminate(); -} - -struct CreateParams { - Session *session; - VoiceChannel *channel; -}; - -VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) { - CreateParams params; - params.session = session; - params.channel = NULL; - TypedMessageData data(¶ms); - worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data); - return params.channel; -} - -VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) { - CritScope cs(&crit_); - - // This is ok to alloc from a thread other than the worker thread - assert(initialized_); - MediaChannel *channel = media_engine_->CreateChannel(); - if (channel == NULL) - return NULL; - - VoiceChannel *voice_channel = new VoiceChannel(this, session, channel); - channels_.push_back(voice_channel); - return voice_channel; -} - -void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) { - TypedMessageData data(voice_channel); - worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data); -} - -void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) { - CritScope cs(&crit_); - // Destroy voice channel. - assert(initialized_); - std::vector::iterator it = std::find(channels_.begin(), - channels_.end(), voice_channel); - assert(it != channels_.end()); - if (it == channels_.end()) - return; - - channels_.erase(it); - MediaChannel *channel = voice_channel->channel(); - delete voice_channel; - delete channel; -} - -void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device, - int wave_out_device) { - AudioOptions options; - options.auto_gain_control = auto_gain_control; - options.wave_in_device = wave_in_device; - options.wave_out_device = wave_out_device; - TypedMessageData data(options); - worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data); -} - -void ChannelManager::SetAudioOptions_w(AudioOptions options) { - assert(worker_thread_ == Thread::Current()); - - // Set auto gain control on - if (media_engine_->SetAudioOptions(options.auto_gain_control?MediaEngine::AUTO_GAIN_CONTROL:0) != 0) { - // TODO: We need to log these failures. - } - - // Set the audio devices - // This will fail if audio is already playing. Stop all of the media - // start it up again after changing the setting. - { - CritScope cs(&crit_); - for (VoiceChannels::iterator it = channels_.begin(); - it < channels_.end(); - ++it) { - (*it)->PauseMedia_w(); - } - - if (media_engine_->SetSoundDevices(options.wave_in_device, options.wave_out_device) == -1) { - // TODO: We need to log these failures. - } - - for (VoiceChannels::iterator it = channels_.begin(); - it < channels_.end(); - ++it) { - (*it)->UnpauseMedia_w(); - } - } -} - -Thread *ChannelManager::worker_thread() { - return worker_thread_; -} - -void ChannelManager::OnMessage(Message *message) { - switch (message->message_id) { - case MSG_CREATEVOICECHANNEL: - { - TypedMessageData *data = static_cast *>(message->pdata); - data->data()->channel = CreateVoiceChannel_w(data->data()->session); - } - break; - - case MSG_DESTROYVOICECHANNEL: - { - TypedMessageData *data = static_cast *>(message->pdata); - DestroyVoiceChannel_w(data->data()); - } - break; - case MSG_SETAUDIOOPTIONS: - { - TypedMessageData *data = static_cast *>(message->pdata); - SetAudioOptions_w(data->data()); - } - break; - } -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cpp new file mode 100644 index 00000000..98634b12 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cpp @@ -0,0 +1,203 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#ifdef HAVE_GIPS +#include "talk/session/phone/gipsmediaengine.h" +#else +#include "talk/session/phone/linphonemediaengine.h" +#endif +#include "channelmanager.h" +#include +#include +namespace cricket { + +const uint32 MSG_CREATEVOICECHANNEL = 1; +const uint32 MSG_DESTROYVOICECHANNEL = 2; +const uint32 MSG_SETAUDIOOPTIONS = 3; + +ChannelManager::ChannelManager(Thread *worker_thread) { +#ifdef HAVE_GIPS + media_engine_ = new GipsMediaEngine(); +#else + media_engine_ = new LinphoneMediaEngine(); +#endif + worker_thread_ = worker_thread; + initialized_ = false; + Init(); +} + +ChannelManager::~ChannelManager() { + Exit(); +} + +MediaEngine *ChannelManager::media_engine() { + return media_engine_; +} + +bool ChannelManager::Init() { + initialized_ = media_engine_->Init(); + return initialized_; +} + +void ChannelManager::Exit() { + if (!initialized_) + return; + + // Need to destroy the voice channels + + while (true) { + crit_.Enter(); + VoiceChannel *channel = NULL; + if (channels_.begin() != channels_.end()) + channel = channels_[0]; + crit_.Leave(); + if (channel == NULL) + break; + delete channel; + } + media_engine_->Terminate(); +} + +struct CreateParams { + Session *session; + VoiceChannel *channel; +}; + +VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) { + CreateParams params; + params.session = session; + params.channel = NULL; + TypedMessageData data(¶ms); + worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data); + return params.channel; +} + +VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) { + CritScope cs(&crit_); + + // This is ok to alloc from a thread other than the worker thread + assert(initialized_); + MediaChannel *channel = media_engine_->CreateChannel(); + if (channel == NULL) + return NULL; + + VoiceChannel *voice_channel = new VoiceChannel(this, session, channel); + channels_.push_back(voice_channel); + return voice_channel; +} + +void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) { + TypedMessageData data(voice_channel); + worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data); +} + +void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) { + CritScope cs(&crit_); + // Destroy voice channel. + assert(initialized_); + std::vector::iterator it = std::find(channels_.begin(), + channels_.end(), voice_channel); + assert(it != channels_.end()); + if (it == channels_.end()) + return; + + channels_.erase(it); + MediaChannel *channel = voice_channel->channel(); + delete voice_channel; + delete channel; +} + +void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device) { + AudioOptions options; + options.auto_gain_control = auto_gain_control; + options.wave_in_device = wave_in_device; + options.wave_out_device = wave_out_device; + TypedMessageData data(options); + worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data); +} + +void ChannelManager::SetAudioOptions_w(AudioOptions options) { + assert(worker_thread_ == Thread::Current()); + + // Set auto gain control on + if (media_engine_->SetAudioOptions(options.auto_gain_control?MediaEngine::AUTO_GAIN_CONTROL:0) != 0) { + // TODO: We need to log these failures. + } + + // Set the audio devices + // This will fail if audio is already playing. Stop all of the media + // start it up again after changing the setting. + { + CritScope cs(&crit_); + for (VoiceChannels::iterator it = channels_.begin(); + it < channels_.end(); + ++it) { + (*it)->PauseMedia_w(); + } + + if (media_engine_->SetSoundDevices(options.wave_in_device, options.wave_out_device) == -1) { + // TODO: We need to log these failures. + } + + for (VoiceChannels::iterator it = channels_.begin(); + it < channels_.end(); + ++it) { + (*it)->UnpauseMedia_w(); + } + } +} + +Thread *ChannelManager::worker_thread() { + return worker_thread_; +} + +void ChannelManager::OnMessage(Message *message) { + switch (message->message_id) { + case MSG_CREATEVOICECHANNEL: + { + TypedMessageData *data = static_cast *>(message->pdata); + data->data()->channel = CreateVoiceChannel_w(data->data()->session); + } + break; + + case MSG_DESTROYVOICECHANNEL: + { + TypedMessageData *data = static_cast *>(message->pdata); + DestroyVoiceChannel_w(data->data()); + } + break; + case MSG_SETAUDIOOPTIONS: + { + TypedMessageData *data = static_cast *>(message->pdata); + SetAudioOptions_w(data->data()); + } + break; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc deleted file mode 100644 index 756456d3..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Jingle call example - * Copyright 2004--2005, Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -// LinphoneMediaEngine is a Linphone implementation of MediaEngine -extern "C" { -#include "talk/third_party/mediastreamer/mediastream.h" -#ifdef HAVE_ILBC -#include "talk/third_party/mediastreamer/msilbcdec.h" -#endif -#ifdef HAVE_SPEEX -#include "talk/third_party/mediastreamer/msspeexdec.h" -#endif -} -#include -#include -#include -#include -#include -#include "talk/session/phone/linphonemediaengine.h" - -using namespace cricket; - -void *thread_function(void *data) -{ - LinphoneMediaChannel *mc =(LinphoneMediaChannel*) data; - while (mc->dying() == false) { - MediaChannel::NetworkInterface *iface = mc->network_interface(); - char *buf[2048]; - int len; - len = read(mc->fd(), buf, sizeof(buf)); - if (iface && (mc->mute()==FALSE)) - iface->SendPacket(buf, len); - } - return NULL; -} - -LinphoneMediaChannel::LinphoneMediaChannel() { - pt_ = 102; - dying_ = false; - pthread_attr_t attr; - audio_stream_ = NULL; - - struct sockaddr_in sockaddr; - sockaddr.sin_family = AF_INET; - sockaddr.sin_addr.s_addr = INADDR_ANY; - sockaddr.sin_port = htons(3000); - fd_ = socket(PF_INET, SOCK_DGRAM, 0); - fcntl(fd_, F_SETFL, 0, O_NONBLOCK); - bind (fd_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); - - pthread_attr_init(&attr); - pthread_create(&thread_, &attr, &thread_function, this); - pthread_attr_destroy(&attr); -} - -LinphoneMediaChannel::~LinphoneMediaChannel() { - dying_ = true; - pthread_join(thread_, NULL); - audio_stream_stop(audio_stream_); - close(fd_); -} - -void LinphoneMediaChannel::SetCodec(const char *codec) { - if (!strcmp(codec, "iLBC")) - pt_ = 102; - else if (!strcmp(codec, "speex")) - pt_ = 110; - else - pt_ = 0; - if (audio_stream_) - audio_stream_stop(audio_stream_); - audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, pt_, 250); -} - -void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) { - struct sockaddr_in sockaddr; - sockaddr.sin_family = AF_INET; - struct hostent *host = gethostbyname("localhost"); - memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); - sockaddr.sin_port = htons(2000); - - char buf[2048]; - memcpy(buf, data, len); - - if (buf[1] == pt_) { - } else if (buf[1] == 13) { - } else if (buf[1] == 102) { - SetCodec("iLBC"); - } else if (buf[1] == 110) { - SetCodec("speex"); - } else if (buf[1] == 0) { - SetCodec("PCMU"); - } - - if (play_ && buf[1] != 13) - sendto(fd_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); -} - -void LinphoneMediaChannel::SetPlayout(bool playout) { - play_ = playout; -} - -void LinphoneMediaChannel::SetSend(bool send) { - mute_ = !send; -} - -float LinphoneMediaChannel::GetCurrentQuality() { return 0; } -int LinphoneMediaChannel::GetOutputLevel() { return 0; } - -LinphoneMediaEngine::LinphoneMediaEngine() {} -LinphoneMediaEngine::~LinphoneMediaEngine() {} - -static void null_log_handler(const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) { -} - -bool LinphoneMediaEngine::Init() { - g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, NULL); - g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, NULL); - g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, NULL); - ortp_init(); - ms_init(); - -#ifdef HAVE_SPEEX - ms_speex_codec_init(); - rtp_profile_set_payload(&av_profile, 110, &speex_wb); - codecs_.push_back(Codec(110, "speex", 8)); -#endif - -#ifdef HAVE_ILBC - ms_ilbc_codec_init(); - rtp_profile_set_payload(&av_profile, 102, &payload_type_ilbc); - codecs_.push_back(Codec(102, "iLBC", 4)); -#endif - - rtp_profile_set_payload(&av_profile, 0, &pcmu8000); - codecs_.push_back(Codec(0, "PCMU", 2)); - -return true; -} - -void LinphoneMediaEngine::Terminate() { - -} - -MediaChannel *LinphoneMediaEngine::CreateChannel() { - return new LinphoneMediaChannel(); -} - -int LinphoneMediaEngine::SetAudioOptions(int options) { return 0; } -int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { return 0; } - -float LinphoneMediaEngine::GetCurrentQuality() { return 0; } -int LinphoneMediaEngine::GetInputLevel() { return 0; } diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cpp new file mode 100644 index 00000000..756456d3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cpp @@ -0,0 +1,172 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// LinphoneMediaEngine is a Linphone implementation of MediaEngine +extern "C" { +#include "talk/third_party/mediastreamer/mediastream.h" +#ifdef HAVE_ILBC +#include "talk/third_party/mediastreamer/msilbcdec.h" +#endif +#ifdef HAVE_SPEEX +#include "talk/third_party/mediastreamer/msspeexdec.h" +#endif +} +#include +#include +#include +#include +#include +#include "talk/session/phone/linphonemediaengine.h" + +using namespace cricket; + +void *thread_function(void *data) +{ + LinphoneMediaChannel *mc =(LinphoneMediaChannel*) data; + while (mc->dying() == false) { + MediaChannel::NetworkInterface *iface = mc->network_interface(); + char *buf[2048]; + int len; + len = read(mc->fd(), buf, sizeof(buf)); + if (iface && (mc->mute()==FALSE)) + iface->SendPacket(buf, len); + } + return NULL; +} + +LinphoneMediaChannel::LinphoneMediaChannel() { + pt_ = 102; + dying_ = false; + pthread_attr_t attr; + audio_stream_ = NULL; + + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = htons(3000); + fd_ = socket(PF_INET, SOCK_DGRAM, 0); + fcntl(fd_, F_SETFL, 0, O_NONBLOCK); + bind (fd_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); + + pthread_attr_init(&attr); + pthread_create(&thread_, &attr, &thread_function, this); + pthread_attr_destroy(&attr); +} + +LinphoneMediaChannel::~LinphoneMediaChannel() { + dying_ = true; + pthread_join(thread_, NULL); + audio_stream_stop(audio_stream_); + close(fd_); +} + +void LinphoneMediaChannel::SetCodec(const char *codec) { + if (!strcmp(codec, "iLBC")) + pt_ = 102; + else if (!strcmp(codec, "speex")) + pt_ = 110; + else + pt_ = 0; + if (audio_stream_) + audio_stream_stop(audio_stream_); + audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, pt_, 250); +} + +void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) { + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + struct hostent *host = gethostbyname("localhost"); + memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); + sockaddr.sin_port = htons(2000); + + char buf[2048]; + memcpy(buf, data, len); + + if (buf[1] == pt_) { + } else if (buf[1] == 13) { + } else if (buf[1] == 102) { + SetCodec("iLBC"); + } else if (buf[1] == 110) { + SetCodec("speex"); + } else if (buf[1] == 0) { + SetCodec("PCMU"); + } + + if (play_ && buf[1] != 13) + sendto(fd_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); +} + +void LinphoneMediaChannel::SetPlayout(bool playout) { + play_ = playout; +} + +void LinphoneMediaChannel::SetSend(bool send) { + mute_ = !send; +} + +float LinphoneMediaChannel::GetCurrentQuality() { return 0; } +int LinphoneMediaChannel::GetOutputLevel() { return 0; } + +LinphoneMediaEngine::LinphoneMediaEngine() {} +LinphoneMediaEngine::~LinphoneMediaEngine() {} + +static void null_log_handler(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) { +} + +bool LinphoneMediaEngine::Init() { + g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, NULL); + g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, NULL); + g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, NULL); + ortp_init(); + ms_init(); + +#ifdef HAVE_SPEEX + ms_speex_codec_init(); + rtp_profile_set_payload(&av_profile, 110, &speex_wb); + codecs_.push_back(Codec(110, "speex", 8)); +#endif + +#ifdef HAVE_ILBC + ms_ilbc_codec_init(); + rtp_profile_set_payload(&av_profile, 102, &payload_type_ilbc); + codecs_.push_back(Codec(102, "iLBC", 4)); +#endif + + rtp_profile_set_payload(&av_profile, 0, &pcmu8000); + codecs_.push_back(Codec(0, "PCMU", 2)); + +return true; +} + +void LinphoneMediaEngine::Terminate() { + +} + +MediaChannel *LinphoneMediaEngine::CreateChannel() { + return new LinphoneMediaChannel(); +} + +int LinphoneMediaEngine::SetAudioOptions(int options) { return 0; } +int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { return 0; } + +float LinphoneMediaEngine::GetCurrentQuality() { return 0; } +int LinphoneMediaEngine::GetInputLevel() { return 0; } diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc deleted file mode 100644 index f9eb071c..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc +++ /dev/null @@ -1,269 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 - -#include "talk/base/logging.h" -#include "talk/session/receiver.h" -#include "talk/session/phone/phonesessionclient.h" -#include "talk/xmllite/qname.h" -namespace { - -const std::string NS_PHONE("http://www.google.com/session/phone"); -const std::string NS_EMPTY(""); - -const buzz::TQName TQN_PHONE_DESCRIPTION(true, NS_PHONE, "description"); -const buzz::TQName TQN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type"); -const buzz::TQName TQN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id"); -const buzz::TQName TQN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name"); - -} - -namespace cricket { - -PhoneSessionClient::PhoneSessionClient(const buzz::Jid& jid, - SessionManager *manager) : jid_(jid), SessionClient(manager) { - - // No call to start, and certainly no call with focus - focus_call_ = NULL; - - // Start up the channel manager on a worker thread - channel_manager_ = new ChannelManager(session_manager_->worker_thread()); -} - -PhoneSessionClient::~PhoneSessionClient() { - // Destroy all calls - std::map::iterator it; - while (calls_.begin() != calls_.end()) { - std::map::iterator it = calls_.begin(); - DestroyCall((*it).second); - } - - // Delete channel manager. This will wait for the channels to exit - delete channel_manager_; -} - -const std::string &PhoneSessionClient::GetSessionDescriptionName() { - return NS_PHONE; -} - -PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() { - PhoneSessionDescription* session_desc = new PhoneSessionDescription(); - - MediaEngine *me = channel_manager_->media_engine(); - std::vector codecs = me->codecs(); - std::vector::iterator i; - for (i = codecs.begin(); i < codecs.end(); i++) - session_desc->AddCodec(*i); - - session_desc->Sort(); - return session_desc; -} - -PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) { - const PhoneSessionDescription* offer_desc = - static_cast(offer); - PhoneSessionDescription* accept_desc = new PhoneSessionDescription(); - std::vector codecs = channel_manager_->media_engine()->codecs(); - std::vector::iterator iter; - for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) { - for (iter = codecs.begin(); iter < codecs.end(); iter++) { - if ((*iter).name == offer_desc->codecs()[i].name) - accept_desc->AddCodec(*iter); - } - } - - accept_desc->Sort(); - return accept_desc; -} - -bool PhoneSessionClient::FindMediaCodec(MediaEngine* me, - const PhoneSessionDescription* desc, - const char** codec) { - for (size_t i = 0; i < desc->codecs().size(); ++i) { - if (me->FindCodec(desc->codecs()[i].name.c_str())) - *codec = desc->codecs()[i].name.c_str(); - return true; - } - - return false; -} - -const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) { - PhoneSessionDescription* desc = new PhoneSessionDescription(); - - const buzz::XmlElement* payload_type = element->FirstNamed(TQN_PHONE_PAYLOADTYPE); - int num_payload_types = 0; - - while (payload_type) { - if (payload_type->HasAttr(TQN_PHONE_PAYLOADTYPE_ID) && - payload_type->HasAttr(TQN_PHONE_PAYLOADTYPE_NAME)) { - int id = atoi(payload_type->Attr(TQN_PHONE_PAYLOADTYPE_ID).c_str()); - int pref = 0; - std::string name = payload_type->Attr(TQN_PHONE_PAYLOADTYPE_NAME); - desc->AddCodec(MediaEngine::Codec(id, name, 0)); - } - - payload_type = payload_type->NextNamed(TQN_PHONE_PAYLOADTYPE); - num_payload_types += 1; - } - - // For backward compatability, we can assume the other client is (an old - // version of Talk) if it has no payload types at all. - if (num_payload_types == 0) { - desc->AddCodec(MediaEngine::Codec(103, "ISAC", 1)); - desc->AddCodec(MediaEngine::Codec(0, "PCMU", 0)); - } - - return desc; -} - -buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) { - const PhoneSessionDescription* session_desc = - static_cast(_session_desc); - buzz::XmlElement* description = new buzz::XmlElement(TQN_PHONE_DESCRIPTION, true); - - for (size_t i = 0; i < session_desc->codecs().size(); ++i) { - buzz::XmlElement* payload_type = new buzz::XmlElement(TQN_PHONE_PAYLOADTYPE, true); - - char buf[32]; - sprintf(buf, "%d", session_desc->codecs()[i].id); - payload_type->AddAttr(TQN_PHONE_PAYLOADTYPE_ID, buf); - - payload_type->AddAttr(TQN_PHONE_PAYLOADTYPE_NAME, - session_desc->codecs()[i].name.c_str()); - - description->AddElement(payload_type); - } - - return description; -} - -Call *PhoneSessionClient::CreateCall() { - Call *call = new Call(this); - calls_[call->id()] = call; - SignalCallCreate(call); - return call; -} - -void PhoneSessionClient::OnSessionCreate(Session *session, bool received_initiate) { - if (received_initiate) { - session->SignalState.connect(this, &PhoneSessionClient::OnSessionState); - - Call *call = CreateCall(); - session_map_[session->id()] = call; - call->AddSession(session); - } -} - -void PhoneSessionClient::OnSessionState(Session *session, Session::State state) { - if (state == Session::STATE_RECEIVEDINITIATE) { - // If our accept would have no codecs, then we must reject this call. - PhoneSessionDescription* accept_desc = - CreateAcceptSessionDescription(session->remote_description()); - if (accept_desc->codecs().size() == 0) { - // TODO: include an error description with the rejection. - session->Reject(); - } - delete accept_desc; - } -} - -void PhoneSessionClient::DestroyCall(Call *call) { - // Change focus away, signal destruction - - if (call == focus_call_) - SetFocus(NULL); - SignalCallDestroy(call); - - // Remove it from calls_ map and delete - - std::map::iterator it = calls_.find(call->id()); - if (it != calls_.end()) - calls_.erase(it); - - delete call; -} - -void PhoneSessionClient::OnSessionDestroy(Session *session) { - // Find the call this session is in, remove it - - std::map::iterator it = session_map_.find(session->id()); - assert(it != session_map_.end()); - if (it != session_map_.end()) { - Call *call = (*it).second; - session_map_.erase(it); - call->RemoveSession(session); - } -} - -Call *PhoneSessionClient::GetFocus() { - return focus_call_; -} - -void PhoneSessionClient::SetFocus(Call *call) { - Call *old_focus_call = focus_call_; - if (focus_call_ != call) { - if (focus_call_ != NULL) - focus_call_->EnableChannels(false); - focus_call_ = call; - if (focus_call_ != NULL) - focus_call_->EnableChannels(true); - SignalFocus(focus_call_, old_focus_call); - } -} - -void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) { - // Move all sessions from call to call_to_join, delete call. - // If call_to_join has focus, added sessions should have enabled channels. - - if (focus_call_ == call) - SetFocus(NULL); - call_to_join->Join(call, focus_call_ == call_to_join); - DestroyCall(call); -} - -Session *PhoneSessionClient::CreateSession(Call *call) { - Session *session = session_manager_->CreateSession( - GetSessionDescriptionName(), jid().Str()); - session_map_[session->id()] = call; - return session; -} - -ChannelManager *PhoneSessionClient::channel_manager() { - return channel_manager_; -} - -const buzz::Jid &PhoneSessionClient::jid() const { - return jid_; -} - -const buzz::Jid &PhoneSessionClient::GetJid() const { - return jid_; -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cpp new file mode 100644 index 00000000..f9eb071c --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cpp @@ -0,0 +1,269 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 + +#include "talk/base/logging.h" +#include "talk/session/receiver.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/xmllite/qname.h" +namespace { + +const std::string NS_PHONE("http://www.google.com/session/phone"); +const std::string NS_EMPTY(""); + +const buzz::TQName TQN_PHONE_DESCRIPTION(true, NS_PHONE, "description"); +const buzz::TQName TQN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type"); +const buzz::TQName TQN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id"); +const buzz::TQName TQN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name"); + +} + +namespace cricket { + +PhoneSessionClient::PhoneSessionClient(const buzz::Jid& jid, + SessionManager *manager) : jid_(jid), SessionClient(manager) { + + // No call to start, and certainly no call with focus + focus_call_ = NULL; + + // Start up the channel manager on a worker thread + channel_manager_ = new ChannelManager(session_manager_->worker_thread()); +} + +PhoneSessionClient::~PhoneSessionClient() { + // Destroy all calls + std::map::iterator it; + while (calls_.begin() != calls_.end()) { + std::map::iterator it = calls_.begin(); + DestroyCall((*it).second); + } + + // Delete channel manager. This will wait for the channels to exit + delete channel_manager_; +} + +const std::string &PhoneSessionClient::GetSessionDescriptionName() { + return NS_PHONE; +} + +PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() { + PhoneSessionDescription* session_desc = new PhoneSessionDescription(); + + MediaEngine *me = channel_manager_->media_engine(); + std::vector codecs = me->codecs(); + std::vector::iterator i; + for (i = codecs.begin(); i < codecs.end(); i++) + session_desc->AddCodec(*i); + + session_desc->Sort(); + return session_desc; +} + +PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) { + const PhoneSessionDescription* offer_desc = + static_cast(offer); + PhoneSessionDescription* accept_desc = new PhoneSessionDescription(); + std::vector codecs = channel_manager_->media_engine()->codecs(); + std::vector::iterator iter; + for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) { + for (iter = codecs.begin(); iter < codecs.end(); iter++) { + if ((*iter).name == offer_desc->codecs()[i].name) + accept_desc->AddCodec(*iter); + } + } + + accept_desc->Sort(); + return accept_desc; +} + +bool PhoneSessionClient::FindMediaCodec(MediaEngine* me, + const PhoneSessionDescription* desc, + const char** codec) { + for (size_t i = 0; i < desc->codecs().size(); ++i) { + if (me->FindCodec(desc->codecs()[i].name.c_str())) + *codec = desc->codecs()[i].name.c_str(); + return true; + } + + return false; +} + +const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) { + PhoneSessionDescription* desc = new PhoneSessionDescription(); + + const buzz::XmlElement* payload_type = element->FirstNamed(TQN_PHONE_PAYLOADTYPE); + int num_payload_types = 0; + + while (payload_type) { + if (payload_type->HasAttr(TQN_PHONE_PAYLOADTYPE_ID) && + payload_type->HasAttr(TQN_PHONE_PAYLOADTYPE_NAME)) { + int id = atoi(payload_type->Attr(TQN_PHONE_PAYLOADTYPE_ID).c_str()); + int pref = 0; + std::string name = payload_type->Attr(TQN_PHONE_PAYLOADTYPE_NAME); + desc->AddCodec(MediaEngine::Codec(id, name, 0)); + } + + payload_type = payload_type->NextNamed(TQN_PHONE_PAYLOADTYPE); + num_payload_types += 1; + } + + // For backward compatability, we can assume the other client is (an old + // version of Talk) if it has no payload types at all. + if (num_payload_types == 0) { + desc->AddCodec(MediaEngine::Codec(103, "ISAC", 1)); + desc->AddCodec(MediaEngine::Codec(0, "PCMU", 0)); + } + + return desc; +} + +buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) { + const PhoneSessionDescription* session_desc = + static_cast(_session_desc); + buzz::XmlElement* description = new buzz::XmlElement(TQN_PHONE_DESCRIPTION, true); + + for (size_t i = 0; i < session_desc->codecs().size(); ++i) { + buzz::XmlElement* payload_type = new buzz::XmlElement(TQN_PHONE_PAYLOADTYPE, true); + + char buf[32]; + sprintf(buf, "%d", session_desc->codecs()[i].id); + payload_type->AddAttr(TQN_PHONE_PAYLOADTYPE_ID, buf); + + payload_type->AddAttr(TQN_PHONE_PAYLOADTYPE_NAME, + session_desc->codecs()[i].name.c_str()); + + description->AddElement(payload_type); + } + + return description; +} + +Call *PhoneSessionClient::CreateCall() { + Call *call = new Call(this); + calls_[call->id()] = call; + SignalCallCreate(call); + return call; +} + +void PhoneSessionClient::OnSessionCreate(Session *session, bool received_initiate) { + if (received_initiate) { + session->SignalState.connect(this, &PhoneSessionClient::OnSessionState); + + Call *call = CreateCall(); + session_map_[session->id()] = call; + call->AddSession(session); + } +} + +void PhoneSessionClient::OnSessionState(Session *session, Session::State state) { + if (state == Session::STATE_RECEIVEDINITIATE) { + // If our accept would have no codecs, then we must reject this call. + PhoneSessionDescription* accept_desc = + CreateAcceptSessionDescription(session->remote_description()); + if (accept_desc->codecs().size() == 0) { + // TODO: include an error description with the rejection. + session->Reject(); + } + delete accept_desc; + } +} + +void PhoneSessionClient::DestroyCall(Call *call) { + // Change focus away, signal destruction + + if (call == focus_call_) + SetFocus(NULL); + SignalCallDestroy(call); + + // Remove it from calls_ map and delete + + std::map::iterator it = calls_.find(call->id()); + if (it != calls_.end()) + calls_.erase(it); + + delete call; +} + +void PhoneSessionClient::OnSessionDestroy(Session *session) { + // Find the call this session is in, remove it + + std::map::iterator it = session_map_.find(session->id()); + assert(it != session_map_.end()); + if (it != session_map_.end()) { + Call *call = (*it).second; + session_map_.erase(it); + call->RemoveSession(session); + } +} + +Call *PhoneSessionClient::GetFocus() { + return focus_call_; +} + +void PhoneSessionClient::SetFocus(Call *call) { + Call *old_focus_call = focus_call_; + if (focus_call_ != call) { + if (focus_call_ != NULL) + focus_call_->EnableChannels(false); + focus_call_ = call; + if (focus_call_ != NULL) + focus_call_->EnableChannels(true); + SignalFocus(focus_call_, old_focus_call); + } +} + +void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) { + // Move all sessions from call to call_to_join, delete call. + // If call_to_join has focus, added sessions should have enabled channels. + + if (focus_call_ == call) + SetFocus(NULL); + call_to_join->Join(call, focus_call_ == call_to_join); + DestroyCall(call); +} + +Session *PhoneSessionClient::CreateSession(Call *call) { + Session *session = session_manager_->CreateSession( + GetSessionDescriptionName(), jid().Str()); + session_map_[session->id()] = call; + return session; +} + +ChannelManager *PhoneSessionClient::channel_manager() { + return channel_manager_; +} + +const buzz::Jid &PhoneSessionClient::jid() const { + return jid_; +} + +const buzz::Jid &PhoneSessionClient::GetJid() const { + return jid_; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc deleted file mode 100644 index 93cc24c3..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc +++ /dev/null @@ -1,331 +0,0 @@ -#include -#include -#include - -// Socket stuff -#ifndef _WIN32 -#ifdef INET6 -#include -#endif -#include -#include -#include -#else -#include -#endif - -#include "talk/session/phone/mediaengine.h" -#include "talk/session/phone/portaudiomediaengine.h" - -// Engine settings -#define ENGINE_BUFFER_SIZE 2048 - -// PortAudio settings -#define FRAMES_PER_BUFFER 256 -#define SAMPLE_RATE 1 - -// Speex settings -//#define SPEEX_QUALITY 8 - -// ORTP settings -#define MAX_RTP_SIZE 1500 // From mediastreamer - - -// ----------------------------------------------------------------------------- - -static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *channel_p ) -{ - PortAudioMediaChannel* channel = (PortAudioMediaChannel*) channel_p; - channel->readOutput((float*) outputBuffer, framesPerBuffer); - channel->writeInput((float*) inputBuffer, framesPerBuffer); - return 0; -} - -// ----------------------------------------------------------------------------- - -PortAudioMediaChannel::PortAudioMediaChannel() : mute_(false), play_(false), stream_(NULL), out_buffer_(NULL), in_buffer_(NULL), speex_frame_(NULL) -{ - // Initialize buffers - out_buffer_ = new float[ENGINE_BUFFER_SIZE]; - out_buffer_read_ = out_buffer_write_ = (float*) out_buffer_; - out_buffer_end_ = (float*) out_buffer_ + ENGINE_BUFFER_SIZE; - in_buffer_ = new float[ENGINE_BUFFER_SIZE]; - in_buffer_read_ = in_buffer_write_ = (float*) in_buffer_; - in_buffer_end_ = (float*) in_buffer_ + ENGINE_BUFFER_SIZE; - - // Initialize PortAudio - int err = Pa_OpenDefaultStream(&stream_, 1, 1, paFloat32, SAMPLE_RATE, FRAMES_PER_BUFFER, 0, portAudioCallback, this ); - if (err != paNoError) - fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err)); - - // Initialize Speex - speex_bits_init(&speex_bits_); - speex_enc_state_ = speex_encoder_init(&speex_nb_mode); - speex_dec_state_ = speex_decoder_init(&speex_nb_mode); - speex_decoder_ctl(speex_dec_state_, SPEEX_GET_FRAME_SIZE, &speex_frame_size_); - speex_frame_ = new float[speex_frame_size_]; - - // int quality = SPEEX_QUALITY; - // speex_encoder_ctl(state, SPEEX_SET_QUALITY, &quality); - - // Initialize ORTP socket - struct sockaddr_in sockaddr; - sockaddr.sin_family = AF_INET; - sockaddr.sin_addr.s_addr = INADDR_ANY; - sockaddr.sin_port = htons(3000); - rtp_socket_ = socket(PF_INET, SOCK_DGRAM, 0); - fcntl(rtp_socket_, F_SETFL, 0, O_NONBLOCK); - bind (rtp_socket_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); - - // Initialize ORTP Session - rtp_session_ = rtp_session_new(RTP_SESSION_SENDRECV); - rtp_session_max_buf_size_set(rtp_session_, MAX_RTP_SIZE); - rtp_session_set_profile(rtp_session_, &av_profile); - rtp_session_set_local_addr(rtp_session_, "127.0.0.1", 2000); - rtp_session_set_remote_addr(rtp_session_, "127.0.0.1", 3000); - rtp_session_set_scheduling_mode(rtp_session_, 0); - rtp_session_set_blocking_mode(rtp_session_, 0); - rtp_session_set_payload_type(rtp_session_, 110); - rtp_session_set_jitter_compensation(rtp_session_, 250); - rtp_session_enable_adaptive_jitter_compensation(rtp_session_, TRUE); - rtp_timestamp_ = 0; - //rtp_session_signal_connect(rtp_session_, "telephone-event", (RtpCallback) ortpTelephoneCallback,this); -} - -PortAudioMediaChannel::~PortAudioMediaChannel() -{ - if (stream_) { - Pa_CloseStream(stream_); - } - - // Clean up other allocated pointers - - close(rtp_socket_); -} - -void PortAudioMediaChannel::SetCodec(const char *codec) -{ - if (strcmp(codec, "speex")) - printf("Unsupported codec: %s\n", codec); -} - -void PortAudioMediaChannel::OnPacketReceived(const void *data, int len) -{ - struct sockaddr_in sockaddr; - sockaddr.sin_family = AF_INET; - struct hostent *host = gethostbyname("localhost"); - memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); - sockaddr.sin_port = htons(2000); - - char buf[2048]; - memcpy(buf, data, len); - - // Pass packet on to ORTP - if (play_) { - sendto(rtp_socket_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); - } -} - -void PortAudioMediaChannel::SetPlayout(bool playout) -{ - if (!stream_) - return; - - if (play_ && !playout) { - int err = Pa_StopStream(stream_); - if (err != paNoError) { - fprintf(stderr, "Error stopping PortAudio stream: %s\n", Pa_GetErrorText(err)); - return; - } - play_ = false; - } - else if (!play_ && playout) { - int err = Pa_StartStream(stream_); - if (err != paNoError) { - fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err)); - return; - } - play_ = true; - } -} - -void PortAudioMediaChannel::SetSend(bool send) -{ - mute_ = !send; -} - - -float PortAudioMediaChannel::GetCurrentQuality() -{ - return 0; -} - -int PortAudioMediaChannel::GetOutputLevel() -{ - return 0; -} - -void PortAudioMediaChannel::readOutput(float* buf, int len) -{ - //readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); - - // Receive a packet (if there is one) - mblk_t *mp; - mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); - while (mp != NULL) { - gint in_len = mp->b_cont->b_wptr-mp->b_cont->b_rptr; - - // Decode speex stream - speex_bits_read_from(&speex_bits_,mp->b_cont->b_rptr, in_len); - speex_decode(speex_dec_state_, &speex_bits_, speex_frame_); - writeBuffer(out_buffer_, out_buffer_read_, &out_buffer_write_, out_buffer_end_, speex_frame_, speex_frame_size_); - rtp_timestamp_++; - mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); - } - - // Read output - readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); -} - -void PortAudioMediaChannel::writeInput(float* buf, int len) -{ - //writeBuffer(in_buffer_, in_buffer_read_, &in_buffer_write_, in_buffer_end_, buf, len); -} - - -void PortAudioMediaChannel::readBuffer(float* buffer, float** buffer_read_p, float*buffer_write, float* buffer_end, float* target_buffer, int target_len) -{ - float *end, *tmp, *buffer_read = *buffer_read_p; - int remaining; - - // First phase - tmp = buffer_read + target_len; - if (buffer_write < buffer_read && tmp > buffer_end) { - end = buffer_end; - remaining = tmp - buffer_end; - } - else { - end = (tmp > buffer_write ? buffer_write : tmp); - remaining = 0; - } - - while (buffer_read < end) { - *target_buffer++ = *buffer_read++; - } - - // Second phase - if (remaining > 0) { - buffer_read = buffer; - tmp = buffer_read + remaining; - end = (tmp > buffer_write ? buffer_write : tmp); - while (buffer_read < end) { - *target_buffer++ = *buffer_read++; - } - } - - // Finish up - *buffer_read_p = buffer_read; -} - -void PortAudioMediaChannel::writeBuffer(float* buffer, float* buffer_read, float**buffer_write_p, float* buffer_end, float* source_buffer, int source_len) -{ - float *end, *tmp, *buffer_write = *buffer_write_p; - int remaining; - - // First phase - tmp = buffer_write + source_len; - if (buffer_write > buffer_read) { - if (tmp > buffer_end) { - end = buffer_end; - remaining = tmp - buffer_end; - } - else { - end = tmp; - remaining = 0; - } - } - else { - if (tmp > buffer_read) { - printf("Warning: Dropping frame(s)\n"); - end = buffer_read; - remaining = 0; - } - else { - end = tmp; - remaining = 0; - } - } - - while (buffer_write < end) { - *buffer_write++ = *source_buffer++; - } - - // Second phase - if (remaining > 0) { - buffer_write = buffer; - tmp = buffer_write + remaining; - if (tmp > buffer_read) { - printf("Warning: Dropping frame(s)\n"); - end = buffer_read; - } - else { - end = tmp; - } - while (buffer_write < end) { - *buffer_write++ = *source_buffer++; - } - } - - // Finish up - *buffer_write_p = buffer_write; -} - -// ----------------------------------------------------------------------------- - -PortAudioMediaEngine::PortAudioMediaEngine() -{ -} - -PortAudioMediaEngine::~PortAudioMediaEngine() -{ - Pa_Terminate(); -} - -bool PortAudioMediaEngine::Init() -{ - ortp_init(); - - int err = Pa_Initialize(); - if (err != paNoError) { - fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err)); - return false; - } - - // Speex - rtp_profile_set_payload(&av_profile, 110, &speex_wb); - codecs_.push_back(Codec(110, "speex", 8)); - - return true; -} - -void PortAudioMediaEngine::Terminate() -{ -} - - -cricket::MediaChannel* PortAudioMediaEngine::CreateChannel() -{ - return new PortAudioMediaChannel(); -} - -int PortAudioMediaEngine::SetAudioOptions(int options) -{ -} - -int PortAudioMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) -{ -} - -int PortAudioMediaEngine::GetInputLevel() -{ -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cpp new file mode 100644 index 00000000..93cc24c3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cpp @@ -0,0 +1,331 @@ +#include +#include +#include + +// Socket stuff +#ifndef _WIN32 +#ifdef INET6 +#include +#endif +#include +#include +#include +#else +#include +#endif + +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/portaudiomediaengine.h" + +// Engine settings +#define ENGINE_BUFFER_SIZE 2048 + +// PortAudio settings +#define FRAMES_PER_BUFFER 256 +#define SAMPLE_RATE 1 + +// Speex settings +//#define SPEEX_QUALITY 8 + +// ORTP settings +#define MAX_RTP_SIZE 1500 // From mediastreamer + + +// ----------------------------------------------------------------------------- + +static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *channel_p ) +{ + PortAudioMediaChannel* channel = (PortAudioMediaChannel*) channel_p; + channel->readOutput((float*) outputBuffer, framesPerBuffer); + channel->writeInput((float*) inputBuffer, framesPerBuffer); + return 0; +} + +// ----------------------------------------------------------------------------- + +PortAudioMediaChannel::PortAudioMediaChannel() : mute_(false), play_(false), stream_(NULL), out_buffer_(NULL), in_buffer_(NULL), speex_frame_(NULL) +{ + // Initialize buffers + out_buffer_ = new float[ENGINE_BUFFER_SIZE]; + out_buffer_read_ = out_buffer_write_ = (float*) out_buffer_; + out_buffer_end_ = (float*) out_buffer_ + ENGINE_BUFFER_SIZE; + in_buffer_ = new float[ENGINE_BUFFER_SIZE]; + in_buffer_read_ = in_buffer_write_ = (float*) in_buffer_; + in_buffer_end_ = (float*) in_buffer_ + ENGINE_BUFFER_SIZE; + + // Initialize PortAudio + int err = Pa_OpenDefaultStream(&stream_, 1, 1, paFloat32, SAMPLE_RATE, FRAMES_PER_BUFFER, 0, portAudioCallback, this ); + if (err != paNoError) + fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err)); + + // Initialize Speex + speex_bits_init(&speex_bits_); + speex_enc_state_ = speex_encoder_init(&speex_nb_mode); + speex_dec_state_ = speex_decoder_init(&speex_nb_mode); + speex_decoder_ctl(speex_dec_state_, SPEEX_GET_FRAME_SIZE, &speex_frame_size_); + speex_frame_ = new float[speex_frame_size_]; + + // int quality = SPEEX_QUALITY; + // speex_encoder_ctl(state, SPEEX_SET_QUALITY, &quality); + + // Initialize ORTP socket + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = htons(3000); + rtp_socket_ = socket(PF_INET, SOCK_DGRAM, 0); + fcntl(rtp_socket_, F_SETFL, 0, O_NONBLOCK); + bind (rtp_socket_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); + + // Initialize ORTP Session + rtp_session_ = rtp_session_new(RTP_SESSION_SENDRECV); + rtp_session_max_buf_size_set(rtp_session_, MAX_RTP_SIZE); + rtp_session_set_profile(rtp_session_, &av_profile); + rtp_session_set_local_addr(rtp_session_, "127.0.0.1", 2000); + rtp_session_set_remote_addr(rtp_session_, "127.0.0.1", 3000); + rtp_session_set_scheduling_mode(rtp_session_, 0); + rtp_session_set_blocking_mode(rtp_session_, 0); + rtp_session_set_payload_type(rtp_session_, 110); + rtp_session_set_jitter_compensation(rtp_session_, 250); + rtp_session_enable_adaptive_jitter_compensation(rtp_session_, TRUE); + rtp_timestamp_ = 0; + //rtp_session_signal_connect(rtp_session_, "telephone-event", (RtpCallback) ortpTelephoneCallback,this); +} + +PortAudioMediaChannel::~PortAudioMediaChannel() +{ + if (stream_) { + Pa_CloseStream(stream_); + } + + // Clean up other allocated pointers + + close(rtp_socket_); +} + +void PortAudioMediaChannel::SetCodec(const char *codec) +{ + if (strcmp(codec, "speex")) + printf("Unsupported codec: %s\n", codec); +} + +void PortAudioMediaChannel::OnPacketReceived(const void *data, int len) +{ + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + struct hostent *host = gethostbyname("localhost"); + memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); + sockaddr.sin_port = htons(2000); + + char buf[2048]; + memcpy(buf, data, len); + + // Pass packet on to ORTP + if (play_) { + sendto(rtp_socket_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + } +} + +void PortAudioMediaChannel::SetPlayout(bool playout) +{ + if (!stream_) + return; + + if (play_ && !playout) { + int err = Pa_StopStream(stream_); + if (err != paNoError) { + fprintf(stderr, "Error stopping PortAudio stream: %s\n", Pa_GetErrorText(err)); + return; + } + play_ = false; + } + else if (!play_ && playout) { + int err = Pa_StartStream(stream_); + if (err != paNoError) { + fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err)); + return; + } + play_ = true; + } +} + +void PortAudioMediaChannel::SetSend(bool send) +{ + mute_ = !send; +} + + +float PortAudioMediaChannel::GetCurrentQuality() +{ + return 0; +} + +int PortAudioMediaChannel::GetOutputLevel() +{ + return 0; +} + +void PortAudioMediaChannel::readOutput(float* buf, int len) +{ + //readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); + + // Receive a packet (if there is one) + mblk_t *mp; + mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); + while (mp != NULL) { + gint in_len = mp->b_cont->b_wptr-mp->b_cont->b_rptr; + + // Decode speex stream + speex_bits_read_from(&speex_bits_,mp->b_cont->b_rptr, in_len); + speex_decode(speex_dec_state_, &speex_bits_, speex_frame_); + writeBuffer(out_buffer_, out_buffer_read_, &out_buffer_write_, out_buffer_end_, speex_frame_, speex_frame_size_); + rtp_timestamp_++; + mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); + } + + // Read output + readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); +} + +void PortAudioMediaChannel::writeInput(float* buf, int len) +{ + //writeBuffer(in_buffer_, in_buffer_read_, &in_buffer_write_, in_buffer_end_, buf, len); +} + + +void PortAudioMediaChannel::readBuffer(float* buffer, float** buffer_read_p, float*buffer_write, float* buffer_end, float* target_buffer, int target_len) +{ + float *end, *tmp, *buffer_read = *buffer_read_p; + int remaining; + + // First phase + tmp = buffer_read + target_len; + if (buffer_write < buffer_read && tmp > buffer_end) { + end = buffer_end; + remaining = tmp - buffer_end; + } + else { + end = (tmp > buffer_write ? buffer_write : tmp); + remaining = 0; + } + + while (buffer_read < end) { + *target_buffer++ = *buffer_read++; + } + + // Second phase + if (remaining > 0) { + buffer_read = buffer; + tmp = buffer_read + remaining; + end = (tmp > buffer_write ? buffer_write : tmp); + while (buffer_read < end) { + *target_buffer++ = *buffer_read++; + } + } + + // Finish up + *buffer_read_p = buffer_read; +} + +void PortAudioMediaChannel::writeBuffer(float* buffer, float* buffer_read, float**buffer_write_p, float* buffer_end, float* source_buffer, int source_len) +{ + float *end, *tmp, *buffer_write = *buffer_write_p; + int remaining; + + // First phase + tmp = buffer_write + source_len; + if (buffer_write > buffer_read) { + if (tmp > buffer_end) { + end = buffer_end; + remaining = tmp - buffer_end; + } + else { + end = tmp; + remaining = 0; + } + } + else { + if (tmp > buffer_read) { + printf("Warning: Dropping frame(s)\n"); + end = buffer_read; + remaining = 0; + } + else { + end = tmp; + remaining = 0; + } + } + + while (buffer_write < end) { + *buffer_write++ = *source_buffer++; + } + + // Second phase + if (remaining > 0) { + buffer_write = buffer; + tmp = buffer_write + remaining; + if (tmp > buffer_read) { + printf("Warning: Dropping frame(s)\n"); + end = buffer_read; + } + else { + end = tmp; + } + while (buffer_write < end) { + *buffer_write++ = *source_buffer++; + } + } + + // Finish up + *buffer_write_p = buffer_write; +} + +// ----------------------------------------------------------------------------- + +PortAudioMediaEngine::PortAudioMediaEngine() +{ +} + +PortAudioMediaEngine::~PortAudioMediaEngine() +{ + Pa_Terminate(); +} + +bool PortAudioMediaEngine::Init() +{ + ortp_init(); + + int err = Pa_Initialize(); + if (err != paNoError) { + fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err)); + return false; + } + + // Speex + rtp_profile_set_payload(&av_profile, 110, &speex_wb); + codecs_.push_back(Codec(110, "speex", 8)); + + return true; +} + +void PortAudioMediaEngine::Terminate() +{ +} + + +cricket::MediaChannel* PortAudioMediaEngine::CreateChannel() +{ + return new PortAudioMediaChannel(); +} + +int PortAudioMediaEngine::SetAudioOptions(int options) +{ +} + +int PortAudioMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) +{ +} + +int PortAudioMediaEngine::GetInputLevel() +{ +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc deleted file mode 100644 index 58e1db60..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc +++ /dev/null @@ -1,331 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/session/phone/voicechannel.h" -#include "talk/session/phone/channelmanager.h" -#include "talk/session/phone/phonesessionclient.h" -#include "talk/base/logging.h" -#include -#undef SetPort - -namespace { - -// Delay before quality estimate is meaningful. -uint32 kQualityDelay = 5000; // in ms - -} - -namespace cricket { - -VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session, MediaChannel *channel) { - channel_manager_ = manager; - assert(channel_manager_->worker_thread() == Thread::Current()); - channel_ = channel; - session_ = session; - socket_monitor_ = NULL; - audio_monitor_ = NULL; - socket_ = session_->CreateSocket("rtp"); - socket_->SignalState.connect(this, &VoiceChannel::OnSocketState); - socket_->SignalReadPacket.connect(this, &VoiceChannel::OnSocketRead); - channel->SetInterface(this); - enabled_ = false; - paused_ = false; - socket_writable_ = false; - muted_ = false; - LOG(INFO) << "Created voice channel"; - start_time_ = 0xFFFFFFFF - kQualityDelay; - - session->SignalState.connect(this, &VoiceChannel::OnSessionState); - OnSessionState(session, session->state()); -} - -VoiceChannel::~VoiceChannel() { - assert(channel_manager_->worker_thread() == Thread::Current()); - enabled_ = false; - ChangeState(); - delete socket_monitor_; - delete audio_monitor_; - Thread::Current()->Clear(this); - if (socket_ != NULL) - session_->DestroySocket(socket_); - LOG(INFO) << "Destroyed voice channel"; -} - -void VoiceChannel::OnMessage(Message *pmsg) { - switch (pmsg->message_id) { - case MSG_ENABLE: - EnableMedia_w(); - break; - - case MSG_DISABLE: - DisableMedia_w(); - break; - - case MSG_MUTE: - MuteMedia_w(); - break; - - case MSG_UNMUTE: - UnmuteMedia_w(); - break; - - case MSG_SETSENDCODEC: - SetSendCodec_w(); - break; - } -} - -void VoiceChannel::Enable(bool enable) { - // Can be called from thread other than worker thread - channel_manager_->worker_thread()->Post(this, enable ? MSG_ENABLE : MSG_DISABLE); -} - -void VoiceChannel::Mute(bool mute) { - // Can be called from thread other than worker thread - channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE); -} - -MediaChannel * VoiceChannel::channel() { - return channel_; -} - -void VoiceChannel::OnSessionState(Session* session, Session::State state) { - if ((state == Session::STATE_RECEIVEDACCEPT) || - (state == Session::STATE_RECEIVEDINITIATE)) { - channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC); - } -} - -void VoiceChannel::SetSendCodec_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - - const PhoneSessionDescription* desc = - static_cast(session()->remote_description()); - - const char *codec = NULL; - - if (desc->codecs().size() > 0) - PhoneSessionClient::FindMediaCodec(channel_manager_->media_engine(), desc, &codec); - - // The other client should have returned one of the codecs that we offered. - // If they could not, they should have rejected the session. So, if we get - // into this state, we're dealing with a bad client, so we may as well just - // pick the mostt common format there is: payload type zero. - if (codec == NULL) - codec = "PCMU"; - - channel_->SetCodec(codec); -} - -void VoiceChannel::OnSocketState(P2PSocket *socket, P2PSocket::State state) { - switch (state) { - case P2PSocket::STATE_WRITABLE: - SocketWritable_w(); - break; - - default: - SocketNotWritable_w(); - break; - } -} - -void VoiceChannel::OnSocketRead(P2PSocket *socket, const char *data, size_t len) { - assert(channel_manager_->worker_thread() == Thread::Current()); - // OnSocketRead gets called from P2PSocket; now pass data to MediaEngine - channel_->OnPacketReceived(data, (int)len); -} - -void VoiceChannel::SendPacket(const void *data, size_t len) { - // SendPacket gets called from MediaEngine; send to socket - // MediaEngine will call us on a random thread. The Send operation on the socket is - // special in that it can handle this. - socket_->Send(static_cast(data), len); -} - -void VoiceChannel::ChangeState() { - if (paused_ || !enabled_ || !socket_writable_) { - channel_->SetPlayout(false); - channel_->SetSend(false); - } else { - if (muted_) { - channel_->SetSend(false); - channel_->SetPlayout(true); - } else { - channel_->SetSend(true); - channel_->SetPlayout(true); - } - } -} - -void VoiceChannel::PauseMedia_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - assert(!paused_); - - LOG(INFO) << "Voice channel paused"; - paused_ = true; - ChangeState(); -} - -void VoiceChannel::UnpauseMedia_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - assert(paused_); - - LOG(INFO) << "Voice channel unpaused"; - paused_ = false; - ChangeState(); -} - -void VoiceChannel::EnableMedia_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - if (enabled_) - return; - - LOG(INFO) << "Voice channel enabled"; - enabled_ = true; - start_time_ = Time(); - ChangeState(); -} - -void VoiceChannel::DisableMedia_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - if (!enabled_) - return; - - LOG(INFO) << "Voice channel disabled"; - enabled_ = false; - ChangeState(); -} - -void VoiceChannel::MuteMedia_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - if (muted_) - return; - - LOG(INFO) << "Voice channel muted"; - muted_ = true; - ChangeState(); -} - -void VoiceChannel::UnmuteMedia_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - if (!muted_) - return; - - LOG(INFO) << "Voice channel unmuted"; - muted_ = false; - ChangeState(); -} - -void VoiceChannel::SocketWritable_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - if (socket_writable_) - return; - - LOG(INFO) << "Voice channel socket writable"; - socket_writable_ = true; - ChangeState(); -} - -void VoiceChannel::SocketNotWritable_w() { - assert(channel_manager_->worker_thread() == Thread::Current()); - if (!socket_writable_) - return; - - LOG(INFO) << "Voice channel socket not writable"; - socket_writable_ = false; - ChangeState(); -} - -void VoiceChannel::StartConnectionMonitor(int cms) { - delete socket_monitor_; - socket_monitor_ = new SocketMonitor(socket_, Thread::Current()); - socket_monitor_ - ->SignalUpdate.connect(this, &VoiceChannel::OnConnectionMonitorUpdate); - socket_monitor_->Start(cms); -} - -void VoiceChannel::StopConnectionMonitor() { - if (socket_monitor_ != NULL) { - socket_monitor_->Stop(); - socket_monitor_->SignalUpdate.disconnect(this); - delete socket_monitor_; - socket_monitor_ = NULL; - } -} - -void VoiceChannel::OnConnectionMonitorUpdate(SocketMonitor *monitor, - const std::vector &infos) { - SignalConnectionMonitor(this, infos); -} - -void VoiceChannel::StartAudioMonitor(int cms) { - delete audio_monitor_; - audio_monitor_ = new AudioMonitor(this, Thread::Current()); - audio_monitor_ - ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate); - audio_monitor_->Start(cms); -} - -void VoiceChannel::StopAudioMonitor() { - if (audio_monitor_ != NULL) { - audio_monitor_ ->Stop(); - audio_monitor_ ->SignalUpdate.disconnect(this); - delete audio_monitor_ ; - audio_monitor_ = NULL; - } -} - -void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor, - const AudioInfo& info) { - SignalAudioMonitor(this, info); -} - -Session *VoiceChannel::session() { - return session_; -} - -bool VoiceChannel::HasQuality() { - return Time() >= start_time_ + kQualityDelay; -} - -float VoiceChannel::GetCurrentQuality() { - return channel_->GetCurrentQuality(); -} - -int VoiceChannel::GetInputLevel_w() { - return channel_manager_->media_engine()->GetInputLevel(); -} - -int VoiceChannel::GetOutputLevel_w() { - return channel_->GetOutputLevel(); -} - -Thread* VoiceChannel::worker_thread() { - return channel_manager_->worker_thread(); -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cpp new file mode 100644 index 00000000..58e1db60 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cpp @@ -0,0 +1,331 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/session/phone/voicechannel.h" +#include "talk/session/phone/channelmanager.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/base/logging.h" +#include +#undef SetPort + +namespace { + +// Delay before quality estimate is meaningful. +uint32 kQualityDelay = 5000; // in ms + +} + +namespace cricket { + +VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session, MediaChannel *channel) { + channel_manager_ = manager; + assert(channel_manager_->worker_thread() == Thread::Current()); + channel_ = channel; + session_ = session; + socket_monitor_ = NULL; + audio_monitor_ = NULL; + socket_ = session_->CreateSocket("rtp"); + socket_->SignalState.connect(this, &VoiceChannel::OnSocketState); + socket_->SignalReadPacket.connect(this, &VoiceChannel::OnSocketRead); + channel->SetInterface(this); + enabled_ = false; + paused_ = false; + socket_writable_ = false; + muted_ = false; + LOG(INFO) << "Created voice channel"; + start_time_ = 0xFFFFFFFF - kQualityDelay; + + session->SignalState.connect(this, &VoiceChannel::OnSessionState); + OnSessionState(session, session->state()); +} + +VoiceChannel::~VoiceChannel() { + assert(channel_manager_->worker_thread() == Thread::Current()); + enabled_ = false; + ChangeState(); + delete socket_monitor_; + delete audio_monitor_; + Thread::Current()->Clear(this); + if (socket_ != NULL) + session_->DestroySocket(socket_); + LOG(INFO) << "Destroyed voice channel"; +} + +void VoiceChannel::OnMessage(Message *pmsg) { + switch (pmsg->message_id) { + case MSG_ENABLE: + EnableMedia_w(); + break; + + case MSG_DISABLE: + DisableMedia_w(); + break; + + case MSG_MUTE: + MuteMedia_w(); + break; + + case MSG_UNMUTE: + UnmuteMedia_w(); + break; + + case MSG_SETSENDCODEC: + SetSendCodec_w(); + break; + } +} + +void VoiceChannel::Enable(bool enable) { + // Can be called from thread other than worker thread + channel_manager_->worker_thread()->Post(this, enable ? MSG_ENABLE : MSG_DISABLE); +} + +void VoiceChannel::Mute(bool mute) { + // Can be called from thread other than worker thread + channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE); +} + +MediaChannel * VoiceChannel::channel() { + return channel_; +} + +void VoiceChannel::OnSessionState(Session* session, Session::State state) { + if ((state == Session::STATE_RECEIVEDACCEPT) || + (state == Session::STATE_RECEIVEDINITIATE)) { + channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC); + } +} + +void VoiceChannel::SetSendCodec_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + + const PhoneSessionDescription* desc = + static_cast(session()->remote_description()); + + const char *codec = NULL; + + if (desc->codecs().size() > 0) + PhoneSessionClient::FindMediaCodec(channel_manager_->media_engine(), desc, &codec); + + // The other client should have returned one of the codecs that we offered. + // If they could not, they should have rejected the session. So, if we get + // into this state, we're dealing with a bad client, so we may as well just + // pick the mostt common format there is: payload type zero. + if (codec == NULL) + codec = "PCMU"; + + channel_->SetCodec(codec); +} + +void VoiceChannel::OnSocketState(P2PSocket *socket, P2PSocket::State state) { + switch (state) { + case P2PSocket::STATE_WRITABLE: + SocketWritable_w(); + break; + + default: + SocketNotWritable_w(); + break; + } +} + +void VoiceChannel::OnSocketRead(P2PSocket *socket, const char *data, size_t len) { + assert(channel_manager_->worker_thread() == Thread::Current()); + // OnSocketRead gets called from P2PSocket; now pass data to MediaEngine + channel_->OnPacketReceived(data, (int)len); +} + +void VoiceChannel::SendPacket(const void *data, size_t len) { + // SendPacket gets called from MediaEngine; send to socket + // MediaEngine will call us on a random thread. The Send operation on the socket is + // special in that it can handle this. + socket_->Send(static_cast(data), len); +} + +void VoiceChannel::ChangeState() { + if (paused_ || !enabled_ || !socket_writable_) { + channel_->SetPlayout(false); + channel_->SetSend(false); + } else { + if (muted_) { + channel_->SetSend(false); + channel_->SetPlayout(true); + } else { + channel_->SetSend(true); + channel_->SetPlayout(true); + } + } +} + +void VoiceChannel::PauseMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + assert(!paused_); + + LOG(INFO) << "Voice channel paused"; + paused_ = true; + ChangeState(); +} + +void VoiceChannel::UnpauseMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + assert(paused_); + + LOG(INFO) << "Voice channel unpaused"; + paused_ = false; + ChangeState(); +} + +void VoiceChannel::EnableMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (enabled_) + return; + + LOG(INFO) << "Voice channel enabled"; + enabled_ = true; + start_time_ = Time(); + ChangeState(); +} + +void VoiceChannel::DisableMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (!enabled_) + return; + + LOG(INFO) << "Voice channel disabled"; + enabled_ = false; + ChangeState(); +} + +void VoiceChannel::MuteMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (muted_) + return; + + LOG(INFO) << "Voice channel muted"; + muted_ = true; + ChangeState(); +} + +void VoiceChannel::UnmuteMedia_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (!muted_) + return; + + LOG(INFO) << "Voice channel unmuted"; + muted_ = false; + ChangeState(); +} + +void VoiceChannel::SocketWritable_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (socket_writable_) + return; + + LOG(INFO) << "Voice channel socket writable"; + socket_writable_ = true; + ChangeState(); +} + +void VoiceChannel::SocketNotWritable_w() { + assert(channel_manager_->worker_thread() == Thread::Current()); + if (!socket_writable_) + return; + + LOG(INFO) << "Voice channel socket not writable"; + socket_writable_ = false; + ChangeState(); +} + +void VoiceChannel::StartConnectionMonitor(int cms) { + delete socket_monitor_; + socket_monitor_ = new SocketMonitor(socket_, Thread::Current()); + socket_monitor_ + ->SignalUpdate.connect(this, &VoiceChannel::OnConnectionMonitorUpdate); + socket_monitor_->Start(cms); +} + +void VoiceChannel::StopConnectionMonitor() { + if (socket_monitor_ != NULL) { + socket_monitor_->Stop(); + socket_monitor_->SignalUpdate.disconnect(this); + delete socket_monitor_; + socket_monitor_ = NULL; + } +} + +void VoiceChannel::OnConnectionMonitorUpdate(SocketMonitor *monitor, + const std::vector &infos) { + SignalConnectionMonitor(this, infos); +} + +void VoiceChannel::StartAudioMonitor(int cms) { + delete audio_monitor_; + audio_monitor_ = new AudioMonitor(this, Thread::Current()); + audio_monitor_ + ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate); + audio_monitor_->Start(cms); +} + +void VoiceChannel::StopAudioMonitor() { + if (audio_monitor_ != NULL) { + audio_monitor_ ->Stop(); + audio_monitor_ ->SignalUpdate.disconnect(this); + delete audio_monitor_ ; + audio_monitor_ = NULL; + } +} + +void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor, + const AudioInfo& info) { + SignalAudioMonitor(this, info); +} + +Session *VoiceChannel::session() { + return session_; +} + +bool VoiceChannel::HasQuality() { + return Time() >= start_time_ + kQualityDelay; +} + +float VoiceChannel::GetCurrentQuality() { + return channel_->GetCurrentQuality(); +} + +int VoiceChannel::GetInputLevel_w() { + return channel_manager_->media_engine()->GetInputLevel(); +} + +int VoiceChannel::GetOutputLevel_w() { + return channel_->GetOutputLevel(); +} + +Thread* VoiceChannel::worker_thread() { + return channel_manager_->worker_thread(); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt index 8b2e72d4..6c5db1cf 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/CMakeLists.txt @@ -23,6 +23,6 @@ include_directories( tde_add_library( cricketxmllite STATIC_PIC SOURCES - qname.cc xmlbuilder.cc xmlconstants.cc xmlelement.cc xmlnsstack.cc - xmlparser.cc xmlprinter.cc + qname.cpp xmlbuilder.cpp xmlconstants.cpp xmlelement.cpp xmlnsstack.cpp + xmlparser.cpp xmlprinter.cpp ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am index 1e7abcfd..c7876f77 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am @@ -1,10 +1,10 @@ -libcricketxmllite_la_SOURCES = qname.cc \ - xmlbuilder.cc \ - xmlconstants.cc \ - xmlelement.cc \ - xmlnsstack.cc \ - xmlparser.cc \ - xmlprinter.cc +libcricketxmllite_la_SOURCES = qname.cpp \ + xmlbuilder.cpp \ + xmlconstants.cpp \ + xmlelement.cpp \ + xmlnsstack.cpp \ + xmlparser.cpp \ + xmlprinter.cpp noinst_HEADERS = qname.h \ xmlbuilder.h \ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc deleted file mode 100644 index e20b57e3..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include "talk/base/common.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/xmllite/qname.h" -#include "talk/xmllite/xmlconstants.h" - -//#define new TRACK_NEW - -namespace buzz { - -static int TQName_Hash(const std::string & ns, const char * local) { - int result = ns.size() * 101; - while (*local) { - result *= 19; - result += *local; - local += 1; - } - return result; -} - -static const int bits = 9; -static TQName::Data * get_qname_table() { - static TQName::Data qname_table[1 << bits]; - return qname_table; -} - -static TQName::Data * -AllocateOrFind(const std::string & ns, const char * local) { - int index = TQName_Hash(ns, local); - int increment = index >> (bits - 1) | 1; - TQName::Data * qname_table = get_qname_table(); - for (;;) { - index &= ((1 << bits) - 1); - if (!qname_table[index].Occupied()) { - return new TQName::Data(ns, local); - } - if (qname_table[index].localPart_ == local && - qname_table[index].namespace_ == ns) { - qname_table[index].AddRef(); - return qname_table + index; - } - index += increment; - } -} - -static TQName::Data * -Add(const std::string & ns, const char * local) { - int index = TQName_Hash(ns, local); - int increment = index >> (bits - 1) | 1; - TQName::Data * qname_table = get_qname_table(); - for (;;) { - index &= ((1 << bits) - 1); - if (!qname_table[index].Occupied()) { - qname_table[index].namespace_ = ns; - qname_table[index].localPart_ = local; - qname_table[index].AddRef(); // AddRef twice so it's never deleted - qname_table[index].AddRef(); - return qname_table + index; - } - if (qname_table[index].localPart_ == local && - qname_table[index].namespace_ == ns) { - qname_table[index].AddRef(); - return qname_table + index; - } - index += increment; - } -} - -TQName::~TQName() { - data_->Release(); -} - -TQName::TQName() : data_(TQN_EMPTY.data_) { - data_->AddRef(); -} - -TQName::TQName(bool add, const std::string & ns, const char * local) : - data_(add ? Add(ns, local) : AllocateOrFind(ns, local)) {} - -TQName::TQName(bool add, const std::string & ns, const std::string & local) : - data_(add ? Add(ns, local.c_str()) : AllocateOrFind(ns, local.c_str())) {} - -TQName::TQName(const std::string & ns, const char * local) : - data_(AllocateOrFind(ns, local)) {} - -static std::string -TQName_LocalPart(const std::string & name) { - size_t i = name.rfind(':'); - if (i == std::string::npos) - return name; - return name.substr(i + 1); -} - -static std::string -TQName_Namespace(const std::string & name) { - size_t i = name.rfind(':'); - if (i == std::string::npos) - return STR_EMPTY; - return name.substr(0, i); -} - -TQName::TQName(const std::string & mergedOrLocal) : - data_(AllocateOrFind(TQName_Namespace(mergedOrLocal), - TQName_LocalPart(mergedOrLocal).c_str())) {} - -std::string -TQName::Merged() const { - if (data_->namespace_ == STR_EMPTY) - return data_->localPart_; - - std::string result(data_->namespace_); - result.reserve(result.length() + 1 + data_->localPart_.length()); - result += ':'; - result += data_->localPart_; - return result; -} - -bool -TQName::operator==(const TQName & other) const { - return other.data_ == data_ || - data_->localPart_ == other.data_->localPart_ && - data_->namespace_ == other.data_->namespace_; -} - -int -TQName::Compare(const TQName & other) const { - if (data_ == other.data_) - return 0; - - int result = data_->localPart_.compare(other.data_->localPart_); - if (result) - return result; - - return data_->namespace_.compare(other.data_->namespace_); -} - -} - - - diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cpp new file mode 100644 index 00000000..e20b57e3 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cpp @@ -0,0 +1,167 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlconstants.h" + +//#define new TRACK_NEW + +namespace buzz { + +static int TQName_Hash(const std::string & ns, const char * local) { + int result = ns.size() * 101; + while (*local) { + result *= 19; + result += *local; + local += 1; + } + return result; +} + +static const int bits = 9; +static TQName::Data * get_qname_table() { + static TQName::Data qname_table[1 << bits]; + return qname_table; +} + +static TQName::Data * +AllocateOrFind(const std::string & ns, const char * local) { + int index = TQName_Hash(ns, local); + int increment = index >> (bits - 1) | 1; + TQName::Data * qname_table = get_qname_table(); + for (;;) { + index &= ((1 << bits) - 1); + if (!qname_table[index].Occupied()) { + return new TQName::Data(ns, local); + } + if (qname_table[index].localPart_ == local && + qname_table[index].namespace_ == ns) { + qname_table[index].AddRef(); + return qname_table + index; + } + index += increment; + } +} + +static TQName::Data * +Add(const std::string & ns, const char * local) { + int index = TQName_Hash(ns, local); + int increment = index >> (bits - 1) | 1; + TQName::Data * qname_table = get_qname_table(); + for (;;) { + index &= ((1 << bits) - 1); + if (!qname_table[index].Occupied()) { + qname_table[index].namespace_ = ns; + qname_table[index].localPart_ = local; + qname_table[index].AddRef(); // AddRef twice so it's never deleted + qname_table[index].AddRef(); + return qname_table + index; + } + if (qname_table[index].localPart_ == local && + qname_table[index].namespace_ == ns) { + qname_table[index].AddRef(); + return qname_table + index; + } + index += increment; + } +} + +TQName::~TQName() { + data_->Release(); +} + +TQName::TQName() : data_(TQN_EMPTY.data_) { + data_->AddRef(); +} + +TQName::TQName(bool add, const std::string & ns, const char * local) : + data_(add ? Add(ns, local) : AllocateOrFind(ns, local)) {} + +TQName::TQName(bool add, const std::string & ns, const std::string & local) : + data_(add ? Add(ns, local.c_str()) : AllocateOrFind(ns, local.c_str())) {} + +TQName::TQName(const std::string & ns, const char * local) : + data_(AllocateOrFind(ns, local)) {} + +static std::string +TQName_LocalPart(const std::string & name) { + size_t i = name.rfind(':'); + if (i == std::string::npos) + return name; + return name.substr(i + 1); +} + +static std::string +TQName_Namespace(const std::string & name) { + size_t i = name.rfind(':'); + if (i == std::string::npos) + return STR_EMPTY; + return name.substr(0, i); +} + +TQName::TQName(const std::string & mergedOrLocal) : + data_(AllocateOrFind(TQName_Namespace(mergedOrLocal), + TQName_LocalPart(mergedOrLocal).c_str())) {} + +std::string +TQName::Merged() const { + if (data_->namespace_ == STR_EMPTY) + return data_->localPart_; + + std::string result(data_->namespace_); + result.reserve(result.length() + 1 + data_->localPart_.length()); + result += ':'; + result += data_->localPart_; + return result; +} + +bool +TQName::operator==(const TQName & other) const { + return other.data_ == data_ || + data_->localPart_ == other.data_->localPart_ && + data_->namespace_ == other.data_->namespace_; +} + +int +TQName::Compare(const TQName & other) const { + if (data_ == other.data_) + return 0; + + int result = data_->localPart_.compare(other.data_->localPart_); + if (result) + return result; + + return data_->namespace_.compare(other.data_->namespace_); +} + +} + + + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc deleted file mode 100644 index 7b1b10f0..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" -#include -#include -#include -#include "talk/base/common.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/xmllite/xmlbuilder.h" - -#define new TRACK_NEW - -namespace buzz { - -XmlBuilder::XmlBuilder() : - pelCurrent_(NULL), - pelRoot_(NULL), - pvParents_(new std::vector()) { -} - -void -XmlBuilder::Reset() { - pelRoot_.reset(); - pelCurrent_ = NULL; - pvParents_->clear(); -} - -XmlElement * -XmlBuilder::BuildElement(XmlParseContext * pctx, - const char * name, const char ** atts) { - TQName tagName(pctx->ResolveTQName(name, false)); - if (tagName == TQN_EMPTY) - return NULL; - - XmlElement * pelNew = new XmlElement(tagName); - - if (!*atts) - return pelNew; - - std::set seenNonlocalAtts; - - while (*atts) { - TQName attName(pctx->ResolveTQName(*atts, true)); - if (attName == TQN_EMPTY) { - delete pelNew; - return NULL; - } - - // verify that namespaced names are unique - if (!attName.Namespace().empty()) { - if (seenNonlocalAtts.count(attName)) { - delete pelNew; - return NULL; - } - seenNonlocalAtts.insert(attName); - } - - pelNew->AddAttr(attName, std::string(*(atts + 1))); - atts += 2; - } - - return pelNew; -} - -void -XmlBuilder::StartElement(XmlParseContext * pctx, - const char * name, const char ** atts) { - XmlElement * pelNew = BuildElement(pctx, name, atts); - if (pelNew == NULL) { - pctx->RaiseError(XML_ERROR_SYNTAX); - return; - } - - if (!pelCurrent_) { - pelCurrent_ = pelNew; - pelRoot_.reset(pelNew); - pvParents_->push_back(NULL); - } else { - pelCurrent_->AddElement(pelNew); - pvParents_->push_back(pelCurrent_); - pelCurrent_ = pelNew; - } -} - -void -XmlBuilder::EndElement(XmlParseContext * pctx, const char * name) { - UNUSED(pctx); - UNUSED(name); - pelCurrent_ = pvParents_->back(); - pvParents_->pop_back(); -} - -void -XmlBuilder::CharacterData(XmlParseContext * pctx, - const char * text, int len) { - UNUSED(pctx); - if (pelCurrent_) { - pelCurrent_->AddParsedText(text, len); - } -} - -void -XmlBuilder::Error(XmlParseContext * pctx, XML_Error err) { - UNUSED(pctx); - UNUSED(err); - pelRoot_.reset(NULL); - pelCurrent_ = NULL; - pvParents_->clear(); -} - -XmlElement * -XmlBuilder::CreateElement() { - return pelRoot_.release(); -} - -XmlElement * -XmlBuilder::BuiltElement() { - return pelRoot_.get(); -} - -XmlBuilder::~XmlBuilder() { -} - - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cpp new file mode 100644 index 00000000..7b1b10f0 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cpp @@ -0,0 +1,151 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include +#include +#include +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlbuilder.h" + +#define new TRACK_NEW + +namespace buzz { + +XmlBuilder::XmlBuilder() : + pelCurrent_(NULL), + pelRoot_(NULL), + pvParents_(new std::vector()) { +} + +void +XmlBuilder::Reset() { + pelRoot_.reset(); + pelCurrent_ = NULL; + pvParents_->clear(); +} + +XmlElement * +XmlBuilder::BuildElement(XmlParseContext * pctx, + const char * name, const char ** atts) { + TQName tagName(pctx->ResolveTQName(name, false)); + if (tagName == TQN_EMPTY) + return NULL; + + XmlElement * pelNew = new XmlElement(tagName); + + if (!*atts) + return pelNew; + + std::set seenNonlocalAtts; + + while (*atts) { + TQName attName(pctx->ResolveTQName(*atts, true)); + if (attName == TQN_EMPTY) { + delete pelNew; + return NULL; + } + + // verify that namespaced names are unique + if (!attName.Namespace().empty()) { + if (seenNonlocalAtts.count(attName)) { + delete pelNew; + return NULL; + } + seenNonlocalAtts.insert(attName); + } + + pelNew->AddAttr(attName, std::string(*(atts + 1))); + atts += 2; + } + + return pelNew; +} + +void +XmlBuilder::StartElement(XmlParseContext * pctx, + const char * name, const char ** atts) { + XmlElement * pelNew = BuildElement(pctx, name, atts); + if (pelNew == NULL) { + pctx->RaiseError(XML_ERROR_SYNTAX); + return; + } + + if (!pelCurrent_) { + pelCurrent_ = pelNew; + pelRoot_.reset(pelNew); + pvParents_->push_back(NULL); + } else { + pelCurrent_->AddElement(pelNew); + pvParents_->push_back(pelCurrent_); + pelCurrent_ = pelNew; + } +} + +void +XmlBuilder::EndElement(XmlParseContext * pctx, const char * name) { + UNUSED(pctx); + UNUSED(name); + pelCurrent_ = pvParents_->back(); + pvParents_->pop_back(); +} + +void +XmlBuilder::CharacterData(XmlParseContext * pctx, + const char * text, int len) { + UNUSED(pctx); + if (pelCurrent_) { + pelCurrent_->AddParsedText(text, len); + } +} + +void +XmlBuilder::Error(XmlParseContext * pctx, XML_Error err) { + UNUSED(pctx); + UNUSED(err); + pelRoot_.reset(NULL); + pelCurrent_ = NULL; + pvParents_->clear(); +} + +XmlElement * +XmlBuilder::CreateElement() { + return pelRoot_.release(); +} + +XmlElement * +XmlBuilder::BuiltElement() { + return pelRoot_.get(); +} + +XmlBuilder::~XmlBuilder() { +} + + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc deleted file mode 100644 index 503f832f..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "xmlconstants.h" - -using namespace buzz; - -const std::string & XmlConstants::str_empty() { - static const std::string str_empty_; - return str_empty_; -} - -const std::string & XmlConstants::ns_xml() { - static const std::string ns_xml_("http://www.w3.org/XML/1998/namespace"); - return ns_xml_; -} - -const std::string & XmlConstants::ns_xmlns() { - static const std::string ns_xmlns_("http://www.w3.org/2000/xmlns/"); - return ns_xmlns_; -} - -const std::string & XmlConstants::str_xmlns() { - static const std::string str_xmlns_("xmlns"); - return str_xmlns_; -} - -const std::string & XmlConstants::str_xml() { - static const std::string str_xml_("xml"); - return str_xml_; -} - -const std::string & XmlConstants::str_version() { - static const std::string str_version_("version"); - return str_version_; -} - -const std::string & XmlConstants::str_encoding() { - static const std::string str_encoding_("encoding"); - return str_encoding_; -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cpp new file mode 100644 index 00000000..503f832f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cpp @@ -0,0 +1,65 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "xmlconstants.h" + +using namespace buzz; + +const std::string & XmlConstants::str_empty() { + static const std::string str_empty_; + return str_empty_; +} + +const std::string & XmlConstants::ns_xml() { + static const std::string ns_xml_("http://www.w3.org/XML/1998/namespace"); + return ns_xml_; +} + +const std::string & XmlConstants::ns_xmlns() { + static const std::string ns_xmlns_("http://www.w3.org/2000/xmlns/"); + return ns_xmlns_; +} + +const std::string & XmlConstants::str_xmlns() { + static const std::string str_xmlns_("xmlns"); + return str_xmlns_; +} + +const std::string & XmlConstants::str_xml() { + static const std::string str_xml_("xml"); + return str_xml_; +} + +const std::string & XmlConstants::str_version() { + static const std::string str_version_("version"); + return str_version_; +} + +const std::string & XmlConstants::str_encoding() { + static const std::string str_encoding_("encoding"); + return str_encoding_; +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc deleted file mode 100644 index de0ccddb..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc +++ /dev/null @@ -1,491 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include -#include -#include - -#include "talk/base/common.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/xmllite/qname.h" -#include "talk/xmllite/xmlparser.h" -#include "talk/xmllite/xmlbuilder.h" -#include "talk/xmllite/xmlprinter.h" -#include "talk/xmllite/xmlconstants.h" - -#define new TRACK_NEW - -namespace buzz { - -const TQName TQN_EMPTY(true, STR_EMPTY, STR_EMPTY); -const TQName TQN_XMLNS(true, STR_EMPTY, STR_XMLNS); - - -XmlChild::~XmlChild() { -} - -bool -XmlText::IsTextImpl() const { - return true; -} - -XmlElement * -XmlText::AsElementImpl() const { - return NULL; -} - -XmlText * -XmlText::AsTextImpl() const { - return const_cast(this); -} - -void -XmlText::SetText(const std::string & text) { - text_ = text; -} - -void -XmlText::AddParsedText(const char * buf, int len) { - text_.append(buf, len); -} - -void -XmlText::AddText(const std::string & text) { - text_ += text; -} - -XmlText::~XmlText() { -} - -XmlElement::XmlElement(const TQName & name) : - name_(name), - pFirstAttr_(NULL), - pLastAttr_(NULL), - pFirstChild_(NULL), - pLastChild_(NULL) { -} - -XmlElement::XmlElement(const XmlElement & elt) : - XmlChild(), - name_(elt.name_), - pFirstAttr_(NULL), - pLastAttr_(NULL), - pFirstChild_(NULL), - pLastChild_(NULL) { - - // copy attributes - XmlAttr * pAttr; - XmlAttr ** ppLastAttr = &pFirstAttr_; - XmlAttr * newAttr = NULL; - for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) { - newAttr = new XmlAttr(*pAttr); - *ppLastAttr = newAttr; - ppLastAttr = &(newAttr->pNextAttr_); - } - pLastAttr_ = newAttr; - - // copy children - XmlChild * pChild; - XmlChild ** ppLast = &pFirstChild_; - XmlChild * newChild = NULL; - - for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) { - if (pChild->IsText()) { - newChild = new XmlText(*(pChild->AsText())); - } else { - newChild = new XmlElement(*(pChild->AsElement())); - } - *ppLast = newChild; - ppLast = &(newChild->pNextChild_); - } - pLastChild_ = newChild; - -} - -XmlElement::XmlElement(const TQName & name, bool useDefaultNs) : - name_(name), - pFirstAttr_(useDefaultNs ? new XmlAttr(TQN_XMLNS, name.Namespace()) : NULL), - pLastAttr_(pFirstAttr_), - pFirstChild_(NULL), - pLastChild_(NULL) { -} - -bool -XmlElement::IsTextImpl() const { - return false; -} - -XmlElement * -XmlElement::AsElementImpl() const { - return const_cast(this); -} - -XmlText * -XmlElement::AsTextImpl() const { - return NULL; -} - -const std::string & -XmlElement::BodyText() const { - if (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { - return pFirstChild_->AsText()->Text(); - } - - return STR_EMPTY; -} - -void -XmlElement::SetBodyText(const std::string & text) { - if (text == STR_EMPTY) { - ClearChildren(); - } else if (pFirstChild_ == NULL) { - AddText(text); - } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { - pFirstChild_->AsText()->SetText(text); - } else { - ClearChildren(); - AddText(text); - } -} - -const TQName & -XmlElement::FirstElementName() const { - const XmlElement * element = FirstElement(); - if (element == NULL) - return TQN_EMPTY; - return element->Name(); -} - -XmlAttr * -XmlElement::FirstAttr() { - return pFirstAttr_; -} - -const std::string & -XmlElement::Attr(const TQName & name) const { - XmlAttr * pattr; - for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { - if (pattr->name_ == name) - return pattr->value_; - } - return STR_EMPTY; -} - -bool -XmlElement::HasAttr(const TQName & name) const { - XmlAttr * pattr; - for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { - if (pattr->name_ == name) - return true; - } - return false; -} - -void -XmlElement::SetAttr(const TQName & name, const std::string & value) { - XmlAttr * pattr; - for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { - if (pattr->name_ == name) - break; - } - if (!pattr) { - pattr = new XmlAttr(name, value); - if (pLastAttr_) - pLastAttr_->pNextAttr_ = pattr; - else - pFirstAttr_ = pattr; - pLastAttr_ = pattr; - return; - } - pattr->value_ = value; -} - -void -XmlElement::ClearAttr(const TQName & name) { - XmlAttr * pattr; - XmlAttr *pLastAttr = NULL; - for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { - if (pattr->name_ == name) - break; - pLastAttr = pattr; - } - if (!pattr) - return; - if (!pLastAttr) - pFirstAttr_ = pattr->pNextAttr_; - else - pLastAttr->pNextAttr_ = pattr->pNextAttr_; - if (pLastAttr_ == pattr) - pLastAttr_ = pLastAttr; - delete pattr; -} - -XmlChild * -XmlElement::FirstChild() { - return pFirstChild_; -} - -XmlElement * -XmlElement::FirstElement() { - XmlChild * pChild; - for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText()) - return pChild->AsElement(); - } - return NULL; -} - -XmlElement * -XmlElement::NextElement() { - XmlChild * pChild; - for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText()) - return pChild->AsElement(); - } - return NULL; -} - -XmlElement * -XmlElement::FirstWithNamespace(const std::string & ns) { - XmlChild * pChild; - for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) - return pChild->AsElement(); - } - return NULL; -} - -XmlElement * -XmlElement::NextWithNamespace(const std::string & ns) { - XmlChild * pChild; - for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) - return pChild->AsElement(); - } - return NULL; -} - -XmlElement * -XmlElement::FirstNamed(const TQName & name) { - XmlChild * pChild; - for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText() && pChild->AsElement()->Name() == name) - return pChild->AsElement(); - } - return NULL; -} - -XmlElement * -XmlElement::NextNamed(const TQName & name) { - XmlChild * pChild; - for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText() && pChild->AsElement()->Name() == name) - return pChild->AsElement(); - } - return NULL; -} - -const std::string & -XmlElement::TextNamed(const TQName & name) const { - XmlChild * pChild; - for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { - if (!pChild->IsText() && pChild->AsElement()->Name() == name) - return pChild->AsElement()->BodyText(); - } - return STR_EMPTY; -} - -void -XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) { - if (pPredecessor == NULL) { - pNext->pNextChild_ = pFirstChild_; - pFirstChild_ = pNext; - } - else { - pNext->pNextChild_ = pPredecessor->pNextChild_; - pPredecessor->pNextChild_ = pNext; - } -} - -void -XmlElement::RemoveChildAfter(XmlChild * pPredecessor) { - XmlChild * pNext; - - if (pPredecessor == NULL) { - pNext = pFirstChild_; - pFirstChild_ = pNext->pNextChild_; - } - else { - pNext = pPredecessor->pNextChild_; - pPredecessor->pNextChild_ = pNext->pNextChild_; - } - - if (pLastChild_ == pNext) - pLastChild_ = pPredecessor; - - delete pNext; -} - -void -XmlElement::AddAttr(const TQName & name, const std::string & value) { - ASSERT(!HasAttr(name)); - - XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_; - pLastAttr_ = (*pprev = new XmlAttr(name, value)); -} - -void -XmlElement::AddAttr(const TQName & name, const std::string & value, - int depth) { - XmlElement * element = this; - while (depth--) { - element = element->pLastChild_->AsElement(); - } - element->AddAttr(name, value); -} - -void -XmlElement::AddParsedText(const char * cstr, int len) { - if (len == 0) - return; - - if (pLastChild_ && pLastChild_->IsText()) { - pLastChild_->AsText()->AddParsedText(cstr, len); - return; - } - XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; - pLastChild_ = *pprev = new XmlText(cstr, len); -} - -void -XmlElement::AddText(const std::string & text) { - if (text == STR_EMPTY) - return; - - if (pLastChild_ && pLastChild_->IsText()) { - pLastChild_->AsText()->AddText(text); - return; - } - XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; - pLastChild_ = *pprev = new XmlText(text); -} - -void -XmlElement::AddText(const std::string & text, int depth) { - // note: the first syntax is ambigious for msvc 6 - // XmlElement * pel(this); - XmlElement * element = this; - while (depth--) { - element = element->pLastChild_->AsElement(); - } - element->AddText(text); -} - -void -XmlElement::AddElement(XmlElement *pelChild) { - if (pelChild == NULL) - return; - - XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; - pLastChild_ = *pprev = pelChild; - pelChild->pNextChild_ = NULL; -} - -void -XmlElement::AddElement(XmlElement *pelChild, int depth) { - XmlElement * element = this; - while (depth--) { - element = element->pLastChild_->AsElement(); - } - element->AddElement(pelChild); -} - -void -XmlElement::ClearNamedChildren(const TQName & name) { - XmlChild * prev_child = NULL; - XmlChild * next_child; - XmlChild * child; - for (child = FirstChild(); child; child = next_child) { - next_child = child->NextChild(); - if (!child->IsText() && child->AsElement()->Name() == name) - { - RemoveChildAfter(prev_child); - continue; - } - prev_child = child; - } -} - -void -XmlElement::ClearChildren() { - XmlChild * pchild; - for (pchild = pFirstChild_; pchild; ) { - XmlChild * pToDelete = pchild; - pchild = pchild->pNextChild_; - delete pToDelete; - } - pFirstChild_ = pLastChild_ = NULL; -} - -std::string -XmlElement::Str() const { - std::stringstream ss; - Print(&ss, NULL, 0); - return ss.str(); -} - -XmlElement * -XmlElement::ForStr(const std::string & str) { - XmlBuilder builder; - XmlParser::ParseXml(&builder, str); - return builder.CreateElement(); -} - -void -XmlElement::Print( - std::ostream * pout, std::string xmlns[], int xmlnsCount) const { - XmlPrinter::PrintXml(pout, this, xmlns, xmlnsCount); -} - -XmlElement::~XmlElement() { - XmlAttr * pattr; - for (pattr = pFirstAttr_; pattr; ) { - XmlAttr * pToDelete = pattr; - pattr = pattr->pNextAttr_; - delete pToDelete; - } - - XmlChild * pchild; - for (pchild = pFirstChild_; pchild; ) { - XmlChild * pToDelete = pchild; - pchild = pchild->pNextChild_; - delete pToDelete; - } -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cpp new file mode 100644 index 00000000..de0ccddb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cpp @@ -0,0 +1,491 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include +#include +#include + +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlparser.h" +#include "talk/xmllite/xmlbuilder.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmllite/xmlconstants.h" + +#define new TRACK_NEW + +namespace buzz { + +const TQName TQN_EMPTY(true, STR_EMPTY, STR_EMPTY); +const TQName TQN_XMLNS(true, STR_EMPTY, STR_XMLNS); + + +XmlChild::~XmlChild() { +} + +bool +XmlText::IsTextImpl() const { + return true; +} + +XmlElement * +XmlText::AsElementImpl() const { + return NULL; +} + +XmlText * +XmlText::AsTextImpl() const { + return const_cast(this); +} + +void +XmlText::SetText(const std::string & text) { + text_ = text; +} + +void +XmlText::AddParsedText(const char * buf, int len) { + text_.append(buf, len); +} + +void +XmlText::AddText(const std::string & text) { + text_ += text; +} + +XmlText::~XmlText() { +} + +XmlElement::XmlElement(const TQName & name) : + name_(name), + pFirstAttr_(NULL), + pLastAttr_(NULL), + pFirstChild_(NULL), + pLastChild_(NULL) { +} + +XmlElement::XmlElement(const XmlElement & elt) : + XmlChild(), + name_(elt.name_), + pFirstAttr_(NULL), + pLastAttr_(NULL), + pFirstChild_(NULL), + pLastChild_(NULL) { + + // copy attributes + XmlAttr * pAttr; + XmlAttr ** ppLastAttr = &pFirstAttr_; + XmlAttr * newAttr = NULL; + for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) { + newAttr = new XmlAttr(*pAttr); + *ppLastAttr = newAttr; + ppLastAttr = &(newAttr->pNextAttr_); + } + pLastAttr_ = newAttr; + + // copy children + XmlChild * pChild; + XmlChild ** ppLast = &pFirstChild_; + XmlChild * newChild = NULL; + + for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) { + if (pChild->IsText()) { + newChild = new XmlText(*(pChild->AsText())); + } else { + newChild = new XmlElement(*(pChild->AsElement())); + } + *ppLast = newChild; + ppLast = &(newChild->pNextChild_); + } + pLastChild_ = newChild; + +} + +XmlElement::XmlElement(const TQName & name, bool useDefaultNs) : + name_(name), + pFirstAttr_(useDefaultNs ? new XmlAttr(TQN_XMLNS, name.Namespace()) : NULL), + pLastAttr_(pFirstAttr_), + pFirstChild_(NULL), + pLastChild_(NULL) { +} + +bool +XmlElement::IsTextImpl() const { + return false; +} + +XmlElement * +XmlElement::AsElementImpl() const { + return const_cast(this); +} + +XmlText * +XmlElement::AsTextImpl() const { + return NULL; +} + +const std::string & +XmlElement::BodyText() const { + if (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { + return pFirstChild_->AsText()->Text(); + } + + return STR_EMPTY; +} + +void +XmlElement::SetBodyText(const std::string & text) { + if (text == STR_EMPTY) { + ClearChildren(); + } else if (pFirstChild_ == NULL) { + AddText(text); + } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { + pFirstChild_->AsText()->SetText(text); + } else { + ClearChildren(); + AddText(text); + } +} + +const TQName & +XmlElement::FirstElementName() const { + const XmlElement * element = FirstElement(); + if (element == NULL) + return TQN_EMPTY; + return element->Name(); +} + +XmlAttr * +XmlElement::FirstAttr() { + return pFirstAttr_; +} + +const std::string & +XmlElement::Attr(const TQName & name) const { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + return pattr->value_; + } + return STR_EMPTY; +} + +bool +XmlElement::HasAttr(const TQName & name) const { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + return true; + } + return false; +} + +void +XmlElement::SetAttr(const TQName & name, const std::string & value) { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + break; + } + if (!pattr) { + pattr = new XmlAttr(name, value); + if (pLastAttr_) + pLastAttr_->pNextAttr_ = pattr; + else + pFirstAttr_ = pattr; + pLastAttr_ = pattr; + return; + } + pattr->value_ = value; +} + +void +XmlElement::ClearAttr(const TQName & name) { + XmlAttr * pattr; + XmlAttr *pLastAttr = NULL; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + break; + pLastAttr = pattr; + } + if (!pattr) + return; + if (!pLastAttr) + pFirstAttr_ = pattr->pNextAttr_; + else + pLastAttr->pNextAttr_ = pattr->pNextAttr_; + if (pLastAttr_ == pattr) + pLastAttr_ = pLastAttr; + delete pattr; +} + +XmlChild * +XmlElement::FirstChild() { + return pFirstChild_; +} + +XmlElement * +XmlElement::FirstElement() { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText()) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextElement() { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText()) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::FirstWithNamespace(const std::string & ns) { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextWithNamespace(const std::string & ns) { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::FirstNamed(const TQName & name) { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextNamed(const TQName & name) { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement(); + } + return NULL; +} + +const std::string & +XmlElement::TextNamed(const TQName & name) const { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement()->BodyText(); + } + return STR_EMPTY; +} + +void +XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) { + if (pPredecessor == NULL) { + pNext->pNextChild_ = pFirstChild_; + pFirstChild_ = pNext; + } + else { + pNext->pNextChild_ = pPredecessor->pNextChild_; + pPredecessor->pNextChild_ = pNext; + } +} + +void +XmlElement::RemoveChildAfter(XmlChild * pPredecessor) { + XmlChild * pNext; + + if (pPredecessor == NULL) { + pNext = pFirstChild_; + pFirstChild_ = pNext->pNextChild_; + } + else { + pNext = pPredecessor->pNextChild_; + pPredecessor->pNextChild_ = pNext->pNextChild_; + } + + if (pLastChild_ == pNext) + pLastChild_ = pPredecessor; + + delete pNext; +} + +void +XmlElement::AddAttr(const TQName & name, const std::string & value) { + ASSERT(!HasAttr(name)); + + XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_; + pLastAttr_ = (*pprev = new XmlAttr(name, value)); +} + +void +XmlElement::AddAttr(const TQName & name, const std::string & value, + int depth) { + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddAttr(name, value); +} + +void +XmlElement::AddParsedText(const char * cstr, int len) { + if (len == 0) + return; + + if (pLastChild_ && pLastChild_->IsText()) { + pLastChild_->AsText()->AddParsedText(cstr, len); + return; + } + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = new XmlText(cstr, len); +} + +void +XmlElement::AddText(const std::string & text) { + if (text == STR_EMPTY) + return; + + if (pLastChild_ && pLastChild_->IsText()) { + pLastChild_->AsText()->AddText(text); + return; + } + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = new XmlText(text); +} + +void +XmlElement::AddText(const std::string & text, int depth) { + // note: the first syntax is ambigious for msvc 6 + // XmlElement * pel(this); + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddText(text); +} + +void +XmlElement::AddElement(XmlElement *pelChild) { + if (pelChild == NULL) + return; + + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = pelChild; + pelChild->pNextChild_ = NULL; +} + +void +XmlElement::AddElement(XmlElement *pelChild, int depth) { + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddElement(pelChild); +} + +void +XmlElement::ClearNamedChildren(const TQName & name) { + XmlChild * prev_child = NULL; + XmlChild * next_child; + XmlChild * child; + for (child = FirstChild(); child; child = next_child) { + next_child = child->NextChild(); + if (!child->IsText() && child->AsElement()->Name() == name) + { + RemoveChildAfter(prev_child); + continue; + } + prev_child = child; + } +} + +void +XmlElement::ClearChildren() { + XmlChild * pchild; + for (pchild = pFirstChild_; pchild; ) { + XmlChild * pToDelete = pchild; + pchild = pchild->pNextChild_; + delete pToDelete; + } + pFirstChild_ = pLastChild_ = NULL; +} + +std::string +XmlElement::Str() const { + std::stringstream ss; + Print(&ss, NULL, 0); + return ss.str(); +} + +XmlElement * +XmlElement::ForStr(const std::string & str) { + XmlBuilder builder; + XmlParser::ParseXml(&builder, str); + return builder.CreateElement(); +} + +void +XmlElement::Print( + std::ostream * pout, std::string xmlns[], int xmlnsCount) const { + XmlPrinter::PrintXml(pout, this, xmlns, xmlnsCount); +} + +XmlElement::~XmlElement() { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; ) { + XmlAttr * pToDelete = pattr; + pattr = pattr->pNextAttr_; + delete pToDelete; + } + + XmlChild * pchild; + for (pchild = pFirstChild_; pchild; ) { + XmlChild * pToDelete = pchild; + pchild = pchild->pNextChild_; + delete pToDelete; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc deleted file mode 100644 index d6d4c7b4..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc +++ /dev/null @@ -1,205 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" -#include -#include -#include -#include -#include "talk/xmllite/xmlelement.h" -#include "talk/xmllite/xmlnsstack.h" -#include "talk/xmllite/xmlconstants.h" - -namespace buzz { - -XmlnsStack::XmlnsStack() : - pxmlnsStack_(new std::vector), - pxmlnsDepthStack_(new std::vector) { -} - -XmlnsStack::~XmlnsStack() {} - -void -XmlnsStack::PushFrame() { - pxmlnsDepthStack_->push_back(pxmlnsStack_->size()); -} - -void -XmlnsStack::PopFrame() { - size_t prev_size = pxmlnsDepthStack_->back(); - pxmlnsDepthStack_->pop_back(); - if (prev_size < pxmlnsStack_->size()) { - pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size, - pxmlnsStack_->end()); - } -} -const std::pair NS_NOT_FOUND(STR_EMPTY, false); -const std::pair EMPTY_NS_FOUND(STR_EMPTY, true); -const std::pair XMLNS_DEFINITION_FOUND(NS_XMLNS, true); - -const std::string * -XmlnsStack::NsForPrefix(const std::string & prefix) { - if (prefix.length() >= 3 && - (prefix[0] == 'x' || prefix[0] == 'X') && - (prefix[1] == 'm' || prefix[1] == 'M') && - (prefix[2] == 'l' || prefix[2] == 'L')) { - if (prefix == "xml") - return &(NS_XML); - if (prefix == "xmlns") - return &(NS_XMLNS); - return NULL; - } - - std::vector::iterator pos; - for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { - pos -= 2; - if (*pos == prefix) - return &(*(pos + 1)); - } - - if (prefix == STR_EMPTY) - return &(STR_EMPTY); // default namespace - - return NULL; // none found -} - -bool -XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) { - const std::string * match = NsForPrefix(prefix); - if (match == NULL) - return false; - return (*match == ns); -} - -std::pair -XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) { - if (ns == NS_XML) - return std::make_pair(std::string("xml"), true); - if (ns == NS_XMLNS) - return std::make_pair(std::string("xmlns"), true); - if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns)) - return std::make_pair(STR_EMPTY, true); - - std::vector::iterator pos; - for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { - pos -= 2; - if (*(pos + 1) == ns && - (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns)) - return std::make_pair(*pos, true); - } - - return std::make_pair(STR_EMPTY, false); // none found -} - -std::string -XmlnsStack::FormatTQName(const TQName & name, bool isAttr) { - std::string prefix(PrefixForNs(name.Namespace(), isAttr).first); - if (prefix == STR_EMPTY) - return name.LocalPart(); - else - return prefix + ':' + name.LocalPart(); -} - -void -XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) { - pxmlnsStack_->push_back(prefix); - pxmlnsStack_->push_back(ns); -} - -void -XmlnsStack::RemoveXmlns() { - pxmlnsStack_->pop_back(); - pxmlnsStack_->pop_back(); -} - -static bool IsAsciiLetter(char ch) { - return ((ch >= 'a' && ch <= 'z') || - (ch >= 'A' && ch <= 'Z')); -} - -static std::string AsciiLower(const std::string & s) { - std::string result(s); - size_t i; - for (i = 0; i < result.length(); i++) { - if (result[i] >= 'A' && result[i] <= 'Z') - result[i] += 'a' - 'A'; - } - return result; -} - -static std::string SuggestPrefix(const std::string & ns) { - size_t len = ns.length(); - size_t i = ns.find_last_of('.'); - if (i != std::string::npos && len - i <= 4 + 1) - len = i; // chop off ".html" or ".xsd" or ".?{0,4}" - size_t last = len; - while (last > 0) { - last -= 1; - if (IsAsciiLetter(ns[last])) { - size_t first = last; - last += 1; - while (first > 0) { - if (!IsAsciiLetter(ns[first - 1])) - break; - first -= 1; - } - if (last - first > 4) - last = first + 3; - std::string candidate(AsciiLower(ns.substr(first, last - first))); - if (candidate.find("xml") != 0) - return candidate; - break; - } - } - return "ns"; -} - - -std::pair -XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) { - if (PrefixForNs(ns, isAttr).second) - return std::make_pair(STR_EMPTY, false); - - std::string base(SuggestPrefix(ns)); - std::string result(base); - int i = 2; - while (NsForPrefix(result) != NULL) { - std::stringstream ss; - ss << base; - ss << (i++); - ss >> result; - } - AddXmlns(result, ns); - return std::make_pair(result, true); -} - -void XmlnsStack::Reset() { - pxmlnsStack_->clear(); - pxmlnsDepthStack_->clear(); -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cpp new file mode 100644 index 00000000..d6d4c7b4 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cpp @@ -0,0 +1,205 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include +#include +#include +#include +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +XmlnsStack::XmlnsStack() : + pxmlnsStack_(new std::vector), + pxmlnsDepthStack_(new std::vector) { +} + +XmlnsStack::~XmlnsStack() {} + +void +XmlnsStack::PushFrame() { + pxmlnsDepthStack_->push_back(pxmlnsStack_->size()); +} + +void +XmlnsStack::PopFrame() { + size_t prev_size = pxmlnsDepthStack_->back(); + pxmlnsDepthStack_->pop_back(); + if (prev_size < pxmlnsStack_->size()) { + pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size, + pxmlnsStack_->end()); + } +} +const std::pair NS_NOT_FOUND(STR_EMPTY, false); +const std::pair EMPTY_NS_FOUND(STR_EMPTY, true); +const std::pair XMLNS_DEFINITION_FOUND(NS_XMLNS, true); + +const std::string * +XmlnsStack::NsForPrefix(const std::string & prefix) { + if (prefix.length() >= 3 && + (prefix[0] == 'x' || prefix[0] == 'X') && + (prefix[1] == 'm' || prefix[1] == 'M') && + (prefix[2] == 'l' || prefix[2] == 'L')) { + if (prefix == "xml") + return &(NS_XML); + if (prefix == "xmlns") + return &(NS_XMLNS); + return NULL; + } + + std::vector::iterator pos; + for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { + pos -= 2; + if (*pos == prefix) + return &(*(pos + 1)); + } + + if (prefix == STR_EMPTY) + return &(STR_EMPTY); // default namespace + + return NULL; // none found +} + +bool +XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) { + const std::string * match = NsForPrefix(prefix); + if (match == NULL) + return false; + return (*match == ns); +} + +std::pair +XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) { + if (ns == NS_XML) + return std::make_pair(std::string("xml"), true); + if (ns == NS_XMLNS) + return std::make_pair(std::string("xmlns"), true); + if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns)) + return std::make_pair(STR_EMPTY, true); + + std::vector::iterator pos; + for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { + pos -= 2; + if (*(pos + 1) == ns && + (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns)) + return std::make_pair(*pos, true); + } + + return std::make_pair(STR_EMPTY, false); // none found +} + +std::string +XmlnsStack::FormatTQName(const TQName & name, bool isAttr) { + std::string prefix(PrefixForNs(name.Namespace(), isAttr).first); + if (prefix == STR_EMPTY) + return name.LocalPart(); + else + return prefix + ':' + name.LocalPart(); +} + +void +XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) { + pxmlnsStack_->push_back(prefix); + pxmlnsStack_->push_back(ns); +} + +void +XmlnsStack::RemoveXmlns() { + pxmlnsStack_->pop_back(); + pxmlnsStack_->pop_back(); +} + +static bool IsAsciiLetter(char ch) { + return ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z')); +} + +static std::string AsciiLower(const std::string & s) { + std::string result(s); + size_t i; + for (i = 0; i < result.length(); i++) { + if (result[i] >= 'A' && result[i] <= 'Z') + result[i] += 'a' - 'A'; + } + return result; +} + +static std::string SuggestPrefix(const std::string & ns) { + size_t len = ns.length(); + size_t i = ns.find_last_of('.'); + if (i != std::string::npos && len - i <= 4 + 1) + len = i; // chop off ".html" or ".xsd" or ".?{0,4}" + size_t last = len; + while (last > 0) { + last -= 1; + if (IsAsciiLetter(ns[last])) { + size_t first = last; + last += 1; + while (first > 0) { + if (!IsAsciiLetter(ns[first - 1])) + break; + first -= 1; + } + if (last - first > 4) + last = first + 3; + std::string candidate(AsciiLower(ns.substr(first, last - first))); + if (candidate.find("xml") != 0) + return candidate; + break; + } + } + return "ns"; +} + + +std::pair +XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) { + if (PrefixForNs(ns, isAttr).second) + return std::make_pair(STR_EMPTY, false); + + std::string base(SuggestPrefix(ns)); + std::string result(base); + int i = 2; + while (NsForPrefix(result) != NULL) { + std::stringstream ss; + ss << base; + ss << (i++); + ss >> result; + } + AddXmlns(result, ns); + return std::make_pair(result, true); +} + +void XmlnsStack::Reset() { + pxmlnsStack_->clear(); + pxmlnsDepthStack_->clear(); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc deleted file mode 100644 index 41cadb84..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc +++ /dev/null @@ -1,250 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" -#include -#include -#include -#include -#include "talk/xmllite/xmlelement.h" -#include "talk/base/common.h" -#include "talk/xmllite/xmlparser.h" -#include "talk/xmllite/xmlnsstack.h" -#include "talk/xmllite/xmlconstants.h" - -#include - -#define new TRACK_NEW - -namespace buzz { - - -static void -StartElementCallback(void * userData, const char *name, const char **atts) { - (static_cast(userData))->ExpatStartElement(name, atts); -} - -static void -EndElementCallback(void * userData, const char *name) { - (static_cast(userData))->ExpatEndElement(name); -} - -static void -CharacterDataCallback(void * userData, const char *text, int len) { - (static_cast(userData))->ExpatCharacterData(text, len); -} - -static void -XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) { - (static_cast(userData))->ExpatXmlDecl(ver, enc, st); -} - -XmlParser::XmlParser(XmlParseHandler *pxph) : - context_(this), pxph_(pxph), sentError_(false) { - expat_ = XML_ParserCreate(NULL); - XML_SetUserData(expat_, this); - XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); - XML_SetCharacterDataHandler(expat_, CharacterDataCallback); - XML_SetXmlDeclHandler(expat_, XmlDeclCallback); -} - -void -XmlParser::Reset() { - if (!XML_ParserReset(expat_, NULL)) { - XML_ParserFree(expat_); - expat_ = XML_ParserCreate(NULL); - } - XML_SetUserData(expat_, this); - XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); - XML_SetCharacterDataHandler(expat_, CharacterDataCallback); - XML_SetXmlDeclHandler(expat_, XmlDeclCallback); - context_.Reset(); - sentError_ = false; -} - -static bool -XmlParser_StartsWithXmlns(const char *name) { - return name[0] == 'x' && - name[1] == 'm' && - name[2] == 'l' && - name[3] == 'n' && - name[4] == 's'; -} - -void -XmlParser::ExpatStartElement(const char *name, const char **atts) { - if (context_.RaisedError() != XML_ERROR_NONE) - return; - const char **att; - context_.StartElement(); - for (att = atts; *att; att += 2) { - if (XmlParser_StartsWithXmlns(*att)) { - if ((*att)[5] == '\0') { - context_.StartNamespace("", *(att + 1)); - } - else if ((*att)[5] == ':') { - if (**(att + 1) == '\0') { - // In XML 1.0 empty namespace illegal with prefix (not in 1.1) - context_.RaiseError(XML_ERROR_SYNTAX); - return; - } - context_.StartNamespace((*att) + 6, *(att + 1)); - } - } - } - pxph_->StartElement(&context_, name, atts); -} - -void -XmlParser::ExpatEndElement(const char *name) { - if (context_.RaisedError() != XML_ERROR_NONE) - return; - context_.EndElement(); - pxph_->EndElement(&context_, name); -} - -void -XmlParser::ExpatCharacterData(const char *text, int len) { - if (context_.RaisedError() != XML_ERROR_NONE) - return; - pxph_->CharacterData(&context_, text, len); -} - -void -XmlParser::ExpatXmlDecl(const char * ver, const char * enc, int standalone) { - if (context_.RaisedError() != XML_ERROR_NONE) - return; - - if (ver && std::string("1.0") != ver) { - context_.RaiseError(XML_ERROR_SYNTAX); - return; - } - - if (standalone == 0) { - context_.RaiseError(XML_ERROR_SYNTAX); - return; - } - - if (enc && !((enc[0] == 'U' || enc[0] == 'u') && - (enc[1] == 'T' || enc[1] == 't') && - (enc[2] == 'F' || enc[2] == 'f') && - enc[3] == '-' && enc[4] =='8')) { - context_.RaiseError(XML_ERROR_INCORRECT_ENCODING); - return; - } - -} - -bool -XmlParser::Parse(const char *data, size_t len, bool isFinal) { - if (sentError_) - return false; - - if (XML_Parse(expat_, data, static_cast(len), isFinal) != XML_STATUS_OK) - context_.RaiseError(XML_GetErrorCode(expat_)); - - if (context_.RaisedError() != XML_ERROR_NONE) { - sentError_ = true; - pxph_->Error(&context_, context_.RaisedError()); - return false; - } - - return true; -} - -XmlParser::~XmlParser() { - XML_ParserFree(expat_); -} - -void -XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) { - XmlParser parser(pxph); - parser.Parse(text.c_str(), text.length(), true); -} - -XmlParser::ParseContext::ParseContext(XmlParser *parser) : - parser_(parser), - xmlnsstack_(), - raised_(XML_ERROR_NONE) { -} - -void -XmlParser::ParseContext::StartNamespace(const char *prefix, const char *ns) { - xmlnsstack_.AddXmlns( - *prefix ? std::string(prefix) : STR_EMPTY, -// ns == NS_CLIENT ? NS_CLIENT : -// ns == NS_ROSTER ? NS_ROSTER : -// ns == NS_GR ? NS_GR : - std::string(ns)); -} - -void -XmlParser::ParseContext::StartElement() { - xmlnsstack_.PushFrame(); -} - -void -XmlParser::ParseContext::EndElement() { - xmlnsstack_.PopFrame(); -} - -TQName -XmlParser::ParseContext::ResolveTQName(const char *qname, bool isAttr) { - const char *c; - for (c = qname; *c; ++c) { - if (*c == ':') { - const std::string * result; - result = xmlnsstack_.NsForPrefix(std::string(qname, c - qname)); - if (result == NULL) - return TQN_EMPTY; - const char * localname = c + 1; - return TQName(*result, localname); - } - } - if (isAttr) { - return TQName(STR_EMPTY, qname); - } - - const std::string * result; - result = xmlnsstack_.NsForPrefix(STR_EMPTY); - if (result == NULL) - return TQN_EMPTY; - - return TQName(*result, qname); -} - -void -XmlParser::ParseContext::Reset() { - xmlnsstack_.Reset(); - raised_ = XML_ERROR_NONE; -} - -XmlParser::ParseContext::~ParseContext() { -} - -} - diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cpp new file mode 100644 index 00000000..41cadb84 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cpp @@ -0,0 +1,250 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include +#include +#include +#include +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmllite/xmlparser.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +#include + +#define new TRACK_NEW + +namespace buzz { + + +static void +StartElementCallback(void * userData, const char *name, const char **atts) { + (static_cast(userData))->ExpatStartElement(name, atts); +} + +static void +EndElementCallback(void * userData, const char *name) { + (static_cast(userData))->ExpatEndElement(name); +} + +static void +CharacterDataCallback(void * userData, const char *text, int len) { + (static_cast(userData))->ExpatCharacterData(text, len); +} + +static void +XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) { + (static_cast(userData))->ExpatXmlDecl(ver, enc, st); +} + +XmlParser::XmlParser(XmlParseHandler *pxph) : + context_(this), pxph_(pxph), sentError_(false) { + expat_ = XML_ParserCreate(NULL); + XML_SetUserData(expat_, this); + XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); + XML_SetCharacterDataHandler(expat_, CharacterDataCallback); + XML_SetXmlDeclHandler(expat_, XmlDeclCallback); +} + +void +XmlParser::Reset() { + if (!XML_ParserReset(expat_, NULL)) { + XML_ParserFree(expat_); + expat_ = XML_ParserCreate(NULL); + } + XML_SetUserData(expat_, this); + XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); + XML_SetCharacterDataHandler(expat_, CharacterDataCallback); + XML_SetXmlDeclHandler(expat_, XmlDeclCallback); + context_.Reset(); + sentError_ = false; +} + +static bool +XmlParser_StartsWithXmlns(const char *name) { + return name[0] == 'x' && + name[1] == 'm' && + name[2] == 'l' && + name[3] == 'n' && + name[4] == 's'; +} + +void +XmlParser::ExpatStartElement(const char *name, const char **atts) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + const char **att; + context_.StartElement(); + for (att = atts; *att; att += 2) { + if (XmlParser_StartsWithXmlns(*att)) { + if ((*att)[5] == '\0') { + context_.StartNamespace("", *(att + 1)); + } + else if ((*att)[5] == ':') { + if (**(att + 1) == '\0') { + // In XML 1.0 empty namespace illegal with prefix (not in 1.1) + context_.RaiseError(XML_ERROR_SYNTAX); + return; + } + context_.StartNamespace((*att) + 6, *(att + 1)); + } + } + } + pxph_->StartElement(&context_, name, atts); +} + +void +XmlParser::ExpatEndElement(const char *name) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + context_.EndElement(); + pxph_->EndElement(&context_, name); +} + +void +XmlParser::ExpatCharacterData(const char *text, int len) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + pxph_->CharacterData(&context_, text, len); +} + +void +XmlParser::ExpatXmlDecl(const char * ver, const char * enc, int standalone) { + if (context_.RaisedError() != XML_ERROR_NONE) + return; + + if (ver && std::string("1.0") != ver) { + context_.RaiseError(XML_ERROR_SYNTAX); + return; + } + + if (standalone == 0) { + context_.RaiseError(XML_ERROR_SYNTAX); + return; + } + + if (enc && !((enc[0] == 'U' || enc[0] == 'u') && + (enc[1] == 'T' || enc[1] == 't') && + (enc[2] == 'F' || enc[2] == 'f') && + enc[3] == '-' && enc[4] =='8')) { + context_.RaiseError(XML_ERROR_INCORRECT_ENCODING); + return; + } + +} + +bool +XmlParser::Parse(const char *data, size_t len, bool isFinal) { + if (sentError_) + return false; + + if (XML_Parse(expat_, data, static_cast(len), isFinal) != XML_STATUS_OK) + context_.RaiseError(XML_GetErrorCode(expat_)); + + if (context_.RaisedError() != XML_ERROR_NONE) { + sentError_ = true; + pxph_->Error(&context_, context_.RaisedError()); + return false; + } + + return true; +} + +XmlParser::~XmlParser() { + XML_ParserFree(expat_); +} + +void +XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) { + XmlParser parser(pxph); + parser.Parse(text.c_str(), text.length(), true); +} + +XmlParser::ParseContext::ParseContext(XmlParser *parser) : + parser_(parser), + xmlnsstack_(), + raised_(XML_ERROR_NONE) { +} + +void +XmlParser::ParseContext::StartNamespace(const char *prefix, const char *ns) { + xmlnsstack_.AddXmlns( + *prefix ? std::string(prefix) : STR_EMPTY, +// ns == NS_CLIENT ? NS_CLIENT : +// ns == NS_ROSTER ? NS_ROSTER : +// ns == NS_GR ? NS_GR : + std::string(ns)); +} + +void +XmlParser::ParseContext::StartElement() { + xmlnsstack_.PushFrame(); +} + +void +XmlParser::ParseContext::EndElement() { + xmlnsstack_.PopFrame(); +} + +TQName +XmlParser::ParseContext::ResolveTQName(const char *qname, bool isAttr) { + const char *c; + for (c = qname; *c; ++c) { + if (*c == ':') { + const std::string * result; + result = xmlnsstack_.NsForPrefix(std::string(qname, c - qname)); + if (result == NULL) + return TQN_EMPTY; + const char * localname = c + 1; + return TQName(*result, localname); + } + } + if (isAttr) { + return TQName(STR_EMPTY, qname); + } + + const std::string * result; + result = xmlnsstack_.NsForPrefix(STR_EMPTY); + if (result == NULL) + return TQN_EMPTY; + + return TQName(*result, qname); +} + +void +XmlParser::ParseContext::Reset() { + xmlnsstack_.Reset(); + raised_ = XML_ERROR_NONE; +} + +XmlParser::ParseContext::~ParseContext() { +} + +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc deleted file mode 100644 index e0d2c006..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" -#include -#include -#include -#include -#include "talk/xmllite/xmlelement.h" -#include "talk/xmllite/xmlprinter.h" -#include "talk/xmllite/xmlnsstack.h" -#include "talk/xmllite/xmlconstants.h" - -namespace buzz { - -class XmlPrinterImpl { -public: - XmlPrinterImpl(std::ostream * pout, - const std::string * const xmlns, int xmlnsCount); - void PrintElement(const XmlElement * element); - void PrintQuotedValue(const std::string & text); - void PrintBodyText(const std::string & text); - -private: - std::ostream *pout_; - XmlnsStack xmlnsStack_; -}; - -void -XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) { - PrintXml(pout, element, NULL, 0); -} - -void -XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element, - const std::string * const xmlns, int xmlnsCount) { - XmlPrinterImpl printer(pout, xmlns, xmlnsCount); - printer.PrintElement(element); -} - -XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout, - const std::string * const xmlns, int xmlnsCount) : - pout_(pout), - xmlnsStack_() { - int i; - for (i = 0; i < xmlnsCount; i += 2) { - xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]); - } -} - -void -XmlPrinterImpl::PrintElement(const XmlElement * element) { - xmlnsStack_.PushFrame(); - - // first go through attrs of pel to add xmlns definitions - const XmlAttr * pattr; - for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { - if (pattr->Name() == TQN_XMLNS) - xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value()); - else if (pattr->Name().Namespace() == NS_XMLNS) - xmlnsStack_.AddXmlns(pattr->Name().LocalPart(), - pattr->Value()); - } - - // then go through qnames to make sure needed xmlns definitons are added - std::vector newXmlns; - std::pair prefix; - prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false); - if (prefix.second) { - newXmlns.push_back(prefix.first); - newXmlns.push_back(element->Name().Namespace()); - } - - for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { - prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true); - if (prefix.second) { - newXmlns.push_back(prefix.first); - newXmlns.push_back(element->Name().Namespace()); - } - } - - // print the element name - *pout_ << '<' << xmlnsStack_.FormatTQName(element->Name(), false); - - // and the attributes - for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { - *pout_ << ' ' << xmlnsStack_.FormatTQName(pattr->Name(), true) << "=\""; - PrintQuotedValue(pattr->Value()); - *pout_ << '"'; - } - - // and the extra xmlns declarations - std::vector::iterator i(newXmlns.begin()); - while (i < newXmlns.end()) { - if (*i == STR_EMPTY) - *pout_ << " xmlns=\"" << *(i + 1) << '"'; - else - *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"'; - i += 2; - } - - // now the children - const XmlChild * pchild = element->FirstChild(); - - if (pchild == NULL) - *pout_ << "/>"; - else { - *pout_ << '>'; - while (pchild) { - if (pchild->IsText()) - PrintBodyText(pchild->AsText()->Text()); - else - PrintElement(pchild->AsElement()); - pchild = pchild->NextChild(); - } - *pout_ << "Name(), false) << '>'; - } - - xmlnsStack_.PopFrame(); -} - -void -XmlPrinterImpl::PrintQuotedValue(const std::string & text) { - size_t safe = 0; - for (;;) { - size_t unsafe = text.find_first_of("<>&\"", safe); - if (unsafe == std::string::npos) - unsafe = text.length(); - *pout_ << text.substr(safe, unsafe - safe); - if (unsafe == text.length()) - return; - switch (text[unsafe]) { - case '<': *pout_ << "<"; break; - case '>': *pout_ << ">"; break; - case '&': *pout_ << "&"; break; - case '"': *pout_ << """; break; - } - safe = unsafe + 1; - if (safe == text.length()) - return; - } -} - -void -XmlPrinterImpl::PrintBodyText(const std::string & text) { - size_t safe = 0; - for (;;) { - size_t unsafe = text.find_first_of("<>&", safe); - if (unsafe == std::string::npos) - unsafe = text.length(); - *pout_ << text.substr(safe, unsafe - safe); - if (unsafe == text.length()) - return; - switch (text[unsafe]) { - case '<': *pout_ << "<"; break; - case '>': *pout_ << ">"; break; - case '&': *pout_ << "&"; break; - } - safe = unsafe + 1; - if (safe == text.length()) - return; - } -} - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cpp new file mode 100644 index 00000000..e0d2c006 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cpp @@ -0,0 +1,190 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/stl_decl.h" +#include +#include +#include +#include +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +class XmlPrinterImpl { +public: + XmlPrinterImpl(std::ostream * pout, + const std::string * const xmlns, int xmlnsCount); + void PrintElement(const XmlElement * element); + void PrintQuotedValue(const std::string & text); + void PrintBodyText(const std::string & text); + +private: + std::ostream *pout_; + XmlnsStack xmlnsStack_; +}; + +void +XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) { + PrintXml(pout, element, NULL, 0); +} + +void +XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element, + const std::string * const xmlns, int xmlnsCount) { + XmlPrinterImpl printer(pout, xmlns, xmlnsCount); + printer.PrintElement(element); +} + +XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout, + const std::string * const xmlns, int xmlnsCount) : + pout_(pout), + xmlnsStack_() { + int i; + for (i = 0; i < xmlnsCount; i += 2) { + xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]); + } +} + +void +XmlPrinterImpl::PrintElement(const XmlElement * element) { + xmlnsStack_.PushFrame(); + + // first go through attrs of pel to add xmlns definitions + const XmlAttr * pattr; + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + if (pattr->Name() == TQN_XMLNS) + xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value()); + else if (pattr->Name().Namespace() == NS_XMLNS) + xmlnsStack_.AddXmlns(pattr->Name().LocalPart(), + pattr->Value()); + } + + // then go through qnames to make sure needed xmlns definitons are added + std::vector newXmlns; + std::pair prefix; + prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false); + if (prefix.second) { + newXmlns.push_back(prefix.first); + newXmlns.push_back(element->Name().Namespace()); + } + + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true); + if (prefix.second) { + newXmlns.push_back(prefix.first); + newXmlns.push_back(element->Name().Namespace()); + } + } + + // print the element name + *pout_ << '<' << xmlnsStack_.FormatTQName(element->Name(), false); + + // and the attributes + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + *pout_ << ' ' << xmlnsStack_.FormatTQName(pattr->Name(), true) << "=\""; + PrintQuotedValue(pattr->Value()); + *pout_ << '"'; + } + + // and the extra xmlns declarations + std::vector::iterator i(newXmlns.begin()); + while (i < newXmlns.end()) { + if (*i == STR_EMPTY) + *pout_ << " xmlns=\"" << *(i + 1) << '"'; + else + *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"'; + i += 2; + } + + // now the children + const XmlChild * pchild = element->FirstChild(); + + if (pchild == NULL) + *pout_ << "/>"; + else { + *pout_ << '>'; + while (pchild) { + if (pchild->IsText()) + PrintBodyText(pchild->AsText()->Text()); + else + PrintElement(pchild->AsElement()); + pchild = pchild->NextChild(); + } + *pout_ << "Name(), false) << '>'; + } + + xmlnsStack_.PopFrame(); +} + +void +XmlPrinterImpl::PrintQuotedValue(const std::string & text) { + size_t safe = 0; + for (;;) { + size_t unsafe = text.find_first_of("<>&\"", safe); + if (unsafe == std::string::npos) + unsafe = text.length(); + *pout_ << text.substr(safe, unsafe - safe); + if (unsafe == text.length()) + return; + switch (text[unsafe]) { + case '<': *pout_ << "<"; break; + case '>': *pout_ << ">"; break; + case '&': *pout_ << "&"; break; + case '"': *pout_ << """; break; + } + safe = unsafe + 1; + if (safe == text.length()) + return; + } +} + +void +XmlPrinterImpl::PrintBodyText(const std::string & text) { + size_t safe = 0; + for (;;) { + size_t unsafe = text.find_first_of("<>&", safe); + if (unsafe == std::string::npos) + unsafe = text.length(); + *pout_ << text.substr(safe, unsafe - safe); + if (unsafe == text.length()) + return; + switch (text[unsafe]) { + case '<': *pout_ << "<"; break; + case '>': *pout_ << ">"; break; + case '&': *pout_ << "&"; break; + } + safe = unsafe + 1; + if (safe == text.length()) + return; + } +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt index c68cc840..09e060a4 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/CMakeLists.txt @@ -24,6 +24,6 @@ include_directories( tde_add_library( cricketxmpp STATIC_PIC SOURCES - constants.cc jid.cc saslmechanism.cc xmppclient.cc xmppengineimpl.cc - xmppengineimpl_iq.cc xmpplogintask.cc xmppstanzaparser.cc xmpptask.cc + constants.cpp jid.cpp saslmechanism.cpp xmppclient.cpp xmppengineimpl.cpp + xmppengineimpl_iq.cpp xmpplogintask.cpp xmppstanzaparser.cpp xmpptask.cpp ) diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am index 527f7053..6b773af7 100644 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am @@ -1,15 +1,15 @@ ## Does not compile with final KDE_OPTIONS = nofinal -libcricketxmpp_la_SOURCES = constants.cc \ - jid.cc \ - saslmechanism.cc \ - xmppclient.cc \ - xmppengineimpl.cc \ - xmppengineimpl_iq.cc \ - xmpplogintask.cc \ - xmppstanzaparser.cc \ - xmpptask.cc +libcricketxmpp_la_SOURCES = constants.cpp \ + jid.cpp \ + saslmechanism.cpp \ + xmppclient.cpp \ + xmppengineimpl.cpp \ + xmppengineimpl_iq.cpp \ + xmpplogintask.cpp \ + xmppstanzaparser.cpp \ + xmpptask.cpp noinst_HEADERS = asyncsocket.h \ prexmppauth.h \ diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc deleted file mode 100644 index f89df149..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc +++ /dev/null @@ -1,331 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include "talk/base/basicdefs.h" -#include "talk/xmllite/xmlconstants.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/xmllite/qname.h" -#include "talk/xmpp/jid.h" -#include "talk/xmpp/constants.h" -namespace buzz { - -const Jid JID_EMPTY(STR_EMPTY); - -const std::string & Constants::ns_client() { - static const std::string ns_client_("jabber:client"); - return ns_client_; -} - -const std::string & Constants::ns_server() { - static const std::string ns_server_("jabber:server"); - return ns_server_; -} - -const std::string & Constants::ns_stream() { - static const std::string ns_stream_("http://etherx.jabber.org/streams"); - return ns_stream_; -} - -const std::string & Constants::ns_xstream() { - static const std::string ns_xstream_("urn:ietf:params:xml:ns:xmpp-streams"); - return ns_xstream_; -} - -const std::string & Constants::ns_tls() { - static const std::string ns_tls_("urn:ietf:params:xml:ns:xmpp-tls"); - return ns_tls_; -} - -const std::string & Constants::ns_sasl() { - static const std::string ns_sasl_("urn:ietf:params:xml:ns:xmpp-sasl"); - return ns_sasl_; -} - -const std::string & Constants::ns_bind() { - static const std::string ns_bind_("urn:ietf:params:xml:ns:xmpp-bind"); - return ns_bind_; -} - -const std::string & Constants::ns_dialback() { - static const std::string ns_dialback_("jabber:server:dialback"); - return ns_dialback_; -} - -const std::string & Constants::ns_session() { - static const std::string ns_session_("urn:ietf:params:xml:ns:xmpp-session"); - return ns_session_; -} - -const std::string & Constants::ns_stanza() { - static const std::string ns_stanza_("urn:ietf:params:xml:ns:xmpp-stanzas"); - return ns_stanza_; -} - -const std::string & Constants::ns_privacy() { - static const std::string ns_privacy_("jabber:iq:privacy"); - return ns_privacy_; -} - -const std::string & Constants::ns_roster() { - static const std::string ns_roster_("jabber:iq:roster"); - return ns_roster_; -} - -const std::string & Constants::ns_vcard() { - static const std::string ns_vcard_("vcard-temp"); - return ns_vcard_; -} - -const std::string & Constants::str_client() { - static const std::string str_client_("client"); - return str_client_; -} - -const std::string & Constants::str_server() { - static const std::string str_server_("server"); - return str_server_; -} - -const std::string & Constants::str_stream() { - static const std::string str_stream_("stream"); - return str_stream_; -} - -const std::string STR_GET("get"); -const std::string STR_SET("set"); -const std::string STR_RESULT("result"); -const std::string STR_ERROR("error"); - -const std::string STR_FROM("from"); -const std::string STR_TO("to"); -const std::string STR_BOTH("both"); -const std::string STR_REMOVE("remove"); - -const std::string STR_UNAVAILABLE("unavailable"); -const std::string STR_INVISIBLE("invisible"); - -const std::string STR_GOOGLE_COM("google.com"); -const std::string STR_GMAIL_COM("gmail.com"); -const std::string STR_GOOGLEMAIL_COM("googlemail.com"); -const std::string STR_DEFAULT_DOMAIN("default.talk.google.com"); -const std::string STR_X("x"); - -const TQName TQN_STREAM_STREAM(true, NS_STREAM, STR_STREAM); -const TQName TQN_STREAM_FEATURES(true, NS_STREAM, "features"); -const TQName TQN_STREAM_ERROR(true, NS_STREAM, "error"); - -const TQName TQN_XSTREAM_BAD_FORMAT(true, NS_XSTREAM, "bad-format"); -const TQName TQN_XSTREAM_BAD_NAMESPACE_PREFIX(true, NS_XSTREAM, "bad-namespace-prefix"); -const TQName TQN_XSTREAM_CONFLICT(true, NS_XSTREAM, "conflict"); -const TQName TQN_XSTREAM_CONNECTION_TIMEOUT(true, NS_XSTREAM, "connection-timeout"); -const TQName TQN_XSTREAM_HOST_GONE(true, NS_XSTREAM, "host-gone"); -const TQName TQN_XSTREAM_HOST_UNKNOWN(true, NS_XSTREAM, "host-unknown"); -const TQName TQN_XSTREAM_IMPROPER_ADDRESSIING(true, NS_XSTREAM, "improper-addressing"); -const TQName TQN_XSTREAM_INTERNAL_SERVER_ERROR(true, NS_XSTREAM, "internal-server-error"); -const TQName TQN_XSTREAM_INVALID_FROM(true, NS_XSTREAM, "invalid-from"); -const TQName TQN_XSTREAM_INVALID_ID(true, NS_XSTREAM, "invalid-id"); -const TQName TQN_XSTREAM_INVALID_NAMESPACE(true, NS_XSTREAM, "invalid-namespace"); -const TQName TQN_XSTREAM_INVALID_XML(true, NS_XSTREAM, "invalid-xml"); -const TQName TQN_XSTREAM_NOT_AUTHORIZED(true, NS_XSTREAM, "not-authorized"); -const TQName TQN_XSTREAM_POLICY_VIOLATION(true, NS_XSTREAM, "policy-violation"); -const TQName TQN_XSTREAM_REMOTE_CONNECTION_FAILED(true, NS_XSTREAM, "remote-connection-failed"); -const TQName TQN_XSTREAM_RESOURCE_CONSTRAINT(true, NS_XSTREAM, "resource-constraint"); -const TQName TQN_XSTREAM_RESTRICTED_XML(true, NS_XSTREAM, "restricted-xml"); -const TQName TQN_XSTREAM_SEE_OTHER_HOST(true, NS_XSTREAM, "see-other-host"); -const TQName TQN_XSTREAM_SYSTEM_SHUTDOWN(true, NS_XSTREAM, "system-shutdown"); -const TQName TQN_XSTREAM_UNDEFINED_CONDITION(true, NS_XSTREAM, "undefined-condition"); -const TQName TQN_XSTREAM_UNSUPPORTED_ENCODING(true, NS_XSTREAM, "unsupported-encoding"); -const TQName TQN_XSTREAM_UNSUPPORTED_STANZA_TYPE(true, NS_XSTREAM, "unsupported-stanza-type"); -const TQName TQN_XSTREAM_UNSUPPORTED_VERSION(true, NS_XSTREAM, "unsupported-version"); -const TQName TQN_XSTREAM_XML_NOT_WELL_FORMED(true, NS_XSTREAM, "xml-not-well-formed"); -const TQName TQN_XSTREAM_TEXT(true, NS_XSTREAM, "text"); - -const TQName TQN_TLS_STARTTLS(true, NS_TLS, "starttls"); -const TQName TQN_TLS_REQUIRED(true, NS_TLS, "required"); -const TQName TQN_TLS_PROCEED(true, NS_TLS, "proceed"); -const TQName TQN_TLS_FAILURE(true, NS_TLS, "failure"); - -const TQName TQN_SASL_MECHANISMS(true, NS_SASL, "mechanisms"); -const TQName TQN_SASL_MECHANISM(true, NS_SASL, "mechanism"); -const TQName TQN_SASL_AUTH(true, NS_SASL, "auth"); -const TQName TQN_SASL_CHALLENGE(true, NS_SASL, "challenge"); -const TQName TQN_SASL_RESPONSE(true, NS_SASL, "response"); -const TQName TQN_SASL_ABORT(true, NS_SASL, "abort"); -const TQName TQN_SASL_SUCCESS(true, NS_SASL, "success"); -const TQName TQN_SASL_FAILURE(true, NS_SASL, "failure"); -const TQName TQN_SASL_ABORTED(true, NS_SASL, "aborted"); -const TQName TQN_SASL_INCORRECT_ENCODING(true, NS_SASL, "incorrect-encoding"); -const TQName TQN_SASL_INVALID_AUTHZID(true, NS_SASL, "invalid-authzid"); -const TQName TQN_SASL_INVALID_MECHANISM(true, NS_SASL, "invalid-mechanism"); -const TQName TQN_SASL_MECHANISM_TOO_WEAK(true, NS_SASL, "mechanism-too-weak"); -const TQName TQN_SASL_NOT_AUTHORIZED(true, NS_SASL, "not-authorized"); -const TQName TQN_SASL_TEMPORARY_AUTH_FAILURE(true, NS_SASL, "temporary-auth-failure"); - -const TQName TQN_DIALBACK_RESULT(true, NS_DIALBACK, "result"); -const TQName TQN_DIALBACK_VERIFY(true, NS_DIALBACK, "verify"); - -const TQName TQN_STANZA_BAD_REQUEST(true, NS_STANZA, "bad-request"); -const TQName TQN_STANZA_CONFLICT(true, NS_STANZA, "conflict"); -const TQName TQN_STANZA_FEATURE_NOT_IMPLEMENTED(true, NS_STANZA, "feature-not-implemented"); -const TQName TQN_STANZA_FORBIDDEN(true, NS_STANZA, "forbidden"); -const TQName TQN_STANZA_GONE(true, NS_STANZA, "gone"); -const TQName TQN_STANZA_INTERNAL_SERVER_ERROR(true, NS_STANZA, "internal-server-error"); -const TQName TQN_STANZA_ITEM_NOT_FOUND(true, NS_STANZA, "item-not-found"); -const TQName TQN_STANZA_JID_MALFORMED(true, NS_STANZA, "jid-malformed"); -const TQName TQN_STANZA_NOT_ACCEPTABLE(true, NS_STANZA, "not-acceptable"); -const TQName TQN_STANZA_NOT_ALLOWED(true, NS_STANZA, "not-allowed"); -const TQName TQN_STANZA_PAYMENT_REQUIRED(true, NS_STANZA, "payment-required"); -const TQName TQN_STANZA_RECIPIENT_UNAVAILABLE(true, NS_STANZA, "recipient-unavailable"); -const TQName TQN_STANZA_REDIRECT(true, NS_STANZA, "redirect"); -const TQName TQN_STANZA_REGISTRATION_REQUIRED(true, NS_STANZA, "registration-required"); -const TQName TQN_STANZA_REMOTE_SERVER_NOT_FOUND(true, NS_STANZA, "remote-server-not-found"); -const TQName TQN_STANZA_REMOTE_SERVER_TIMEOUT(true, NS_STANZA, "remote-server-timeout"); -const TQName TQN_STANZA_RESOURCE_CONSTRAINT(true, NS_STANZA, "resource-constraint"); -const TQName TQN_STANZA_SERVICE_UNAVAILABLE(true, NS_STANZA, "service-unavailable"); -const TQName TQN_STANZA_SUBSCRIPTION_REQUIRED(true, NS_STANZA, "subscription-required"); -const TQName TQN_STANZA_UNDEFINED_CONDITION(true, NS_STANZA, "undefined-condition"); -const TQName TQN_STANZA_UNEXPECTED_REQUEST(true, NS_STANZA, "unexpected-request"); -const TQName TQN_STANZA_TEXT(true, NS_STANZA, "text"); - -const TQName TQN_BIND_BIND(true, NS_BIND, "bind"); -const TQName TQN_BIND_RESOURCE(true, NS_BIND, "resource"); -const TQName TQN_BIND_JID(true, NS_BIND, "jid"); - -const TQName TQN_MESSAGE(true, NS_CLIENT, "message"); -const TQName TQN_BODY(true, NS_CLIENT, "body"); -const TQName TQN_SUBJECT(true, NS_CLIENT, "subject"); -const TQName TQN_THREAD(true, NS_CLIENT, "thread"); -const TQName TQN_PRESENCE(true, NS_CLIENT, "presence"); -const TQName TQN_SHOW(true, NS_CLIENT, "show"); -const TQName TQN_STATUS(true, NS_CLIENT, "status"); -const TQName TQN_LANG(true, NS_CLIENT, "lang"); -const TQName TQN_PRIORITY(true, NS_CLIENT, "priority"); -const TQName TQN_IQ(true, NS_CLIENT, "iq"); -const TQName TQN_ERROR(true, NS_CLIENT, "error"); - -const TQName TQN_SERVER_MESSAGE(true, NS_SERVER, "message"); -const TQName TQN_SERVER_BODY(true, NS_SERVER, "body"); -const TQName TQN_SERVER_SUBJECT(true, NS_SERVER, "subject"); -const TQName TQN_SERVER_THREAD(true, NS_SERVER, "thread"); -const TQName TQN_SERVER_PRESENCE(true, NS_SERVER, "presence"); -const TQName TQN_SERVER_SHOW(true, NS_SERVER, "show"); -const TQName TQN_SERVER_STATUS(true, NS_SERVER, "status"); -const TQName TQN_SERVER_LANG(true, NS_SERVER, "lang"); -const TQName TQN_SERVER_PRIORITY(true, NS_SERVER, "priority"); -const TQName TQN_SERVER_IQ(true, NS_SERVER, "iq"); -const TQName TQN_SERVER_ERROR(true, NS_SERVER, "error"); - -const TQName TQN_SESSION_SESSION(true, NS_SESSION, "session"); - -const TQName TQN_PRIVACY_QUERY(true, NS_PRIVACY, "query"); -const TQName TQN_PRIVACY_ACTIVE(true, NS_PRIVACY, "active"); -const TQName TQN_PRIVACY_DEFAULT(true, NS_PRIVACY, "default"); -const TQName TQN_PRIVACY_LIST(true, NS_PRIVACY, "list"); -const TQName TQN_PRIVACY_ITEM(true, NS_PRIVACY, "item"); -const TQName TQN_PRIVACY_IQ(true, NS_PRIVACY, "iq"); -const TQName TQN_PRIVACY_MESSAGE(true, NS_PRIVACY, "message"); -const TQName TQN_PRIVACY_PRESENCE_IN(true, NS_PRIVACY, "presence-in"); -const TQName TQN_PRIVACY_PRESENCE_OUT(true, NS_PRIVACY, "presence-out"); - -const TQName TQN_ROSTER_QUERY(true, NS_ROSTER, "query"); -const TQName TQN_ROSTER_ITEM(true, NS_ROSTER, "item"); -const TQName TQN_ROSTER_GROUP(true, NS_ROSTER, "group"); - -const TQName TQN_VCARD_QUERY(true, NS_VCARD, "vCard"); -const TQName TQN_VCARD_FN(true, NS_VCARD, "FN"); - -const TQName TQN_XML_LANG(true, NS_XML, "lang"); - -const std::string STR_TYPE("type"); -const std::string STR_ID("id"); -const std::string STR_NAME("name"); -const std::string STR_JID("jid"); -const std::string STR_SUBSCRIPTION("subscription"); -const std::string STR_ASK("ask"); - -const TQName TQN_ENCODING(true, STR_EMPTY, STR_ENCODING); -const TQName TQN_VERSION(true, STR_EMPTY, STR_VERSION); -const TQName TQN_TO(true, STR_EMPTY, "to"); -const TQName TQN_FROM(true, STR_EMPTY, "from"); -const TQName TQN_TYPE(true, STR_EMPTY, "type"); -const TQName TQN_ID(true, STR_EMPTY, "id"); -const TQName TQN_CODE(true, STR_EMPTY, "code"); -const TQName TQN_NAME(true, STR_EMPTY, "name"); -const TQName TQN_VALUE(true, STR_EMPTY, "value"); -const TQName TQN_ACTION(true, STR_EMPTY, "action"); -const TQName TQN_ORDER(true, STR_EMPTY, "order"); -const TQName TQN_MECHANISM(true, STR_EMPTY, "mechanism"); -const TQName TQN_ASK(true, STR_EMPTY, "ask"); -const TQName TQN_JID(true, STR_EMPTY, "jid"); -const TQName TQN_SUBSCRIPTION(true, STR_EMPTY, "subscription"); -const TQName TQN_SOURCE(true, STR_EMPTY, "source"); - -const TQName TQN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT); -const TQName TQN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER); -const TQName TQN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM); - -// Presence -const std::string STR_SHOW_AWAY("away"); -const std::string STR_SHOW_CHAT("chat"); -const std::string STR_SHOW_DND("dnd"); -const std::string STR_SHOW_XA("xa"); - -// Subscription -const std::string STR_SUBSCRIBE("subscribe"); -const std::string STR_SUBSCRIBED("subscribed"); -const std::string STR_UNSUBSCRIBE("unsubscribe"); -const std::string STR_UNSUBSCRIBED("unsubscribed"); - - -// JEP 0030 -const TQName TQN_NODE(true, STR_EMPTY, "node"); -const TQName TQN_CATEGORY(true, STR_EMPTY, "category"); -const TQName TQN_VAR(true, STR_EMPTY, "var"); -const std::string NS_DISCO_INFO("http://jabber.org/protocol/disco#info"); -const std::string NS_DISCO_ITEMS("http://jabber.org/protocol/disco#items"); -const TQName TQN_DISCO_INFO_QUERY(true, NS_DISCO_INFO, "query"); -const TQName TQN_DISCO_IDENTITY(true, NS_DISCO_INFO, "identity"); -const TQName TQN_DISCO_FEATURE(true, NS_DISCO_INFO, "feature"); - -const TQName TQN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query"); -const TQName TQN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item"); - - -// JEP 0115 -const std::string NS_CAPS("http://jabber.org/protocol/caps"); -const TQName TQN_CAPS_C(true, NS_CAPS, "c"); -const TQName TQN_VER(true, STR_EMPTY, "ver"); -const TQName TQN_EXT(true, STR_EMPTY, "ext"); - -// JEP 0091 Delayed Delivery -const std::string kNSDelay("jabber:x:delay"); -const TQName kQnDelayX(true, kNSDelay, "x"); -const TQName kQnStamp(true, STR_EMPTY, "stamp"); - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cpp new file mode 100644 index 00000000..f89df149 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cpp @@ -0,0 +1,331 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include "talk/base/basicdefs.h" +#include "talk/xmllite/xmlconstants.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" +namespace buzz { + +const Jid JID_EMPTY(STR_EMPTY); + +const std::string & Constants::ns_client() { + static const std::string ns_client_("jabber:client"); + return ns_client_; +} + +const std::string & Constants::ns_server() { + static const std::string ns_server_("jabber:server"); + return ns_server_; +} + +const std::string & Constants::ns_stream() { + static const std::string ns_stream_("http://etherx.jabber.org/streams"); + return ns_stream_; +} + +const std::string & Constants::ns_xstream() { + static const std::string ns_xstream_("urn:ietf:params:xml:ns:xmpp-streams"); + return ns_xstream_; +} + +const std::string & Constants::ns_tls() { + static const std::string ns_tls_("urn:ietf:params:xml:ns:xmpp-tls"); + return ns_tls_; +} + +const std::string & Constants::ns_sasl() { + static const std::string ns_sasl_("urn:ietf:params:xml:ns:xmpp-sasl"); + return ns_sasl_; +} + +const std::string & Constants::ns_bind() { + static const std::string ns_bind_("urn:ietf:params:xml:ns:xmpp-bind"); + return ns_bind_; +} + +const std::string & Constants::ns_dialback() { + static const std::string ns_dialback_("jabber:server:dialback"); + return ns_dialback_; +} + +const std::string & Constants::ns_session() { + static const std::string ns_session_("urn:ietf:params:xml:ns:xmpp-session"); + return ns_session_; +} + +const std::string & Constants::ns_stanza() { + static const std::string ns_stanza_("urn:ietf:params:xml:ns:xmpp-stanzas"); + return ns_stanza_; +} + +const std::string & Constants::ns_privacy() { + static const std::string ns_privacy_("jabber:iq:privacy"); + return ns_privacy_; +} + +const std::string & Constants::ns_roster() { + static const std::string ns_roster_("jabber:iq:roster"); + return ns_roster_; +} + +const std::string & Constants::ns_vcard() { + static const std::string ns_vcard_("vcard-temp"); + return ns_vcard_; +} + +const std::string & Constants::str_client() { + static const std::string str_client_("client"); + return str_client_; +} + +const std::string & Constants::str_server() { + static const std::string str_server_("server"); + return str_server_; +} + +const std::string & Constants::str_stream() { + static const std::string str_stream_("stream"); + return str_stream_; +} + +const std::string STR_GET("get"); +const std::string STR_SET("set"); +const std::string STR_RESULT("result"); +const std::string STR_ERROR("error"); + +const std::string STR_FROM("from"); +const std::string STR_TO("to"); +const std::string STR_BOTH("both"); +const std::string STR_REMOVE("remove"); + +const std::string STR_UNAVAILABLE("unavailable"); +const std::string STR_INVISIBLE("invisible"); + +const std::string STR_GOOGLE_COM("google.com"); +const std::string STR_GMAIL_COM("gmail.com"); +const std::string STR_GOOGLEMAIL_COM("googlemail.com"); +const std::string STR_DEFAULT_DOMAIN("default.talk.google.com"); +const std::string STR_X("x"); + +const TQName TQN_STREAM_STREAM(true, NS_STREAM, STR_STREAM); +const TQName TQN_STREAM_FEATURES(true, NS_STREAM, "features"); +const TQName TQN_STREAM_ERROR(true, NS_STREAM, "error"); + +const TQName TQN_XSTREAM_BAD_FORMAT(true, NS_XSTREAM, "bad-format"); +const TQName TQN_XSTREAM_BAD_NAMESPACE_PREFIX(true, NS_XSTREAM, "bad-namespace-prefix"); +const TQName TQN_XSTREAM_CONFLICT(true, NS_XSTREAM, "conflict"); +const TQName TQN_XSTREAM_CONNECTION_TIMEOUT(true, NS_XSTREAM, "connection-timeout"); +const TQName TQN_XSTREAM_HOST_GONE(true, NS_XSTREAM, "host-gone"); +const TQName TQN_XSTREAM_HOST_UNKNOWN(true, NS_XSTREAM, "host-unknown"); +const TQName TQN_XSTREAM_IMPROPER_ADDRESSIING(true, NS_XSTREAM, "improper-addressing"); +const TQName TQN_XSTREAM_INTERNAL_SERVER_ERROR(true, NS_XSTREAM, "internal-server-error"); +const TQName TQN_XSTREAM_INVALID_FROM(true, NS_XSTREAM, "invalid-from"); +const TQName TQN_XSTREAM_INVALID_ID(true, NS_XSTREAM, "invalid-id"); +const TQName TQN_XSTREAM_INVALID_NAMESPACE(true, NS_XSTREAM, "invalid-namespace"); +const TQName TQN_XSTREAM_INVALID_XML(true, NS_XSTREAM, "invalid-xml"); +const TQName TQN_XSTREAM_NOT_AUTHORIZED(true, NS_XSTREAM, "not-authorized"); +const TQName TQN_XSTREAM_POLICY_VIOLATION(true, NS_XSTREAM, "policy-violation"); +const TQName TQN_XSTREAM_REMOTE_CONNECTION_FAILED(true, NS_XSTREAM, "remote-connection-failed"); +const TQName TQN_XSTREAM_RESOURCE_CONSTRAINT(true, NS_XSTREAM, "resource-constraint"); +const TQName TQN_XSTREAM_RESTRICTED_XML(true, NS_XSTREAM, "restricted-xml"); +const TQName TQN_XSTREAM_SEE_OTHER_HOST(true, NS_XSTREAM, "see-other-host"); +const TQName TQN_XSTREAM_SYSTEM_SHUTDOWN(true, NS_XSTREAM, "system-shutdown"); +const TQName TQN_XSTREAM_UNDEFINED_CONDITION(true, NS_XSTREAM, "undefined-condition"); +const TQName TQN_XSTREAM_UNSUPPORTED_ENCODING(true, NS_XSTREAM, "unsupported-encoding"); +const TQName TQN_XSTREAM_UNSUPPORTED_STANZA_TYPE(true, NS_XSTREAM, "unsupported-stanza-type"); +const TQName TQN_XSTREAM_UNSUPPORTED_VERSION(true, NS_XSTREAM, "unsupported-version"); +const TQName TQN_XSTREAM_XML_NOT_WELL_FORMED(true, NS_XSTREAM, "xml-not-well-formed"); +const TQName TQN_XSTREAM_TEXT(true, NS_XSTREAM, "text"); + +const TQName TQN_TLS_STARTTLS(true, NS_TLS, "starttls"); +const TQName TQN_TLS_REQUIRED(true, NS_TLS, "required"); +const TQName TQN_TLS_PROCEED(true, NS_TLS, "proceed"); +const TQName TQN_TLS_FAILURE(true, NS_TLS, "failure"); + +const TQName TQN_SASL_MECHANISMS(true, NS_SASL, "mechanisms"); +const TQName TQN_SASL_MECHANISM(true, NS_SASL, "mechanism"); +const TQName TQN_SASL_AUTH(true, NS_SASL, "auth"); +const TQName TQN_SASL_CHALLENGE(true, NS_SASL, "challenge"); +const TQName TQN_SASL_RESPONSE(true, NS_SASL, "response"); +const TQName TQN_SASL_ABORT(true, NS_SASL, "abort"); +const TQName TQN_SASL_SUCCESS(true, NS_SASL, "success"); +const TQName TQN_SASL_FAILURE(true, NS_SASL, "failure"); +const TQName TQN_SASL_ABORTED(true, NS_SASL, "aborted"); +const TQName TQN_SASL_INCORRECT_ENCODING(true, NS_SASL, "incorrect-encoding"); +const TQName TQN_SASL_INVALID_AUTHZID(true, NS_SASL, "invalid-authzid"); +const TQName TQN_SASL_INVALID_MECHANISM(true, NS_SASL, "invalid-mechanism"); +const TQName TQN_SASL_MECHANISM_TOO_WEAK(true, NS_SASL, "mechanism-too-weak"); +const TQName TQN_SASL_NOT_AUTHORIZED(true, NS_SASL, "not-authorized"); +const TQName TQN_SASL_TEMPORARY_AUTH_FAILURE(true, NS_SASL, "temporary-auth-failure"); + +const TQName TQN_DIALBACK_RESULT(true, NS_DIALBACK, "result"); +const TQName TQN_DIALBACK_VERIFY(true, NS_DIALBACK, "verify"); + +const TQName TQN_STANZA_BAD_REQUEST(true, NS_STANZA, "bad-request"); +const TQName TQN_STANZA_CONFLICT(true, NS_STANZA, "conflict"); +const TQName TQN_STANZA_FEATURE_NOT_IMPLEMENTED(true, NS_STANZA, "feature-not-implemented"); +const TQName TQN_STANZA_FORBIDDEN(true, NS_STANZA, "forbidden"); +const TQName TQN_STANZA_GONE(true, NS_STANZA, "gone"); +const TQName TQN_STANZA_INTERNAL_SERVER_ERROR(true, NS_STANZA, "internal-server-error"); +const TQName TQN_STANZA_ITEM_NOT_FOUND(true, NS_STANZA, "item-not-found"); +const TQName TQN_STANZA_JID_MALFORMED(true, NS_STANZA, "jid-malformed"); +const TQName TQN_STANZA_NOT_ACCEPTABLE(true, NS_STANZA, "not-acceptable"); +const TQName TQN_STANZA_NOT_ALLOWED(true, NS_STANZA, "not-allowed"); +const TQName TQN_STANZA_PAYMENT_REQUIRED(true, NS_STANZA, "payment-required"); +const TQName TQN_STANZA_RECIPIENT_UNAVAILABLE(true, NS_STANZA, "recipient-unavailable"); +const TQName TQN_STANZA_REDIRECT(true, NS_STANZA, "redirect"); +const TQName TQN_STANZA_REGISTRATION_REQUIRED(true, NS_STANZA, "registration-required"); +const TQName TQN_STANZA_REMOTE_SERVER_NOT_FOUND(true, NS_STANZA, "remote-server-not-found"); +const TQName TQN_STANZA_REMOTE_SERVER_TIMEOUT(true, NS_STANZA, "remote-server-timeout"); +const TQName TQN_STANZA_RESOURCE_CONSTRAINT(true, NS_STANZA, "resource-constraint"); +const TQName TQN_STANZA_SERVICE_UNAVAILABLE(true, NS_STANZA, "service-unavailable"); +const TQName TQN_STANZA_SUBSCRIPTION_REQUIRED(true, NS_STANZA, "subscription-required"); +const TQName TQN_STANZA_UNDEFINED_CONDITION(true, NS_STANZA, "undefined-condition"); +const TQName TQN_STANZA_UNEXPECTED_REQUEST(true, NS_STANZA, "unexpected-request"); +const TQName TQN_STANZA_TEXT(true, NS_STANZA, "text"); + +const TQName TQN_BIND_BIND(true, NS_BIND, "bind"); +const TQName TQN_BIND_RESOURCE(true, NS_BIND, "resource"); +const TQName TQN_BIND_JID(true, NS_BIND, "jid"); + +const TQName TQN_MESSAGE(true, NS_CLIENT, "message"); +const TQName TQN_BODY(true, NS_CLIENT, "body"); +const TQName TQN_SUBJECT(true, NS_CLIENT, "subject"); +const TQName TQN_THREAD(true, NS_CLIENT, "thread"); +const TQName TQN_PRESENCE(true, NS_CLIENT, "presence"); +const TQName TQN_SHOW(true, NS_CLIENT, "show"); +const TQName TQN_STATUS(true, NS_CLIENT, "status"); +const TQName TQN_LANG(true, NS_CLIENT, "lang"); +const TQName TQN_PRIORITY(true, NS_CLIENT, "priority"); +const TQName TQN_IQ(true, NS_CLIENT, "iq"); +const TQName TQN_ERROR(true, NS_CLIENT, "error"); + +const TQName TQN_SERVER_MESSAGE(true, NS_SERVER, "message"); +const TQName TQN_SERVER_BODY(true, NS_SERVER, "body"); +const TQName TQN_SERVER_SUBJECT(true, NS_SERVER, "subject"); +const TQName TQN_SERVER_THREAD(true, NS_SERVER, "thread"); +const TQName TQN_SERVER_PRESENCE(true, NS_SERVER, "presence"); +const TQName TQN_SERVER_SHOW(true, NS_SERVER, "show"); +const TQName TQN_SERVER_STATUS(true, NS_SERVER, "status"); +const TQName TQN_SERVER_LANG(true, NS_SERVER, "lang"); +const TQName TQN_SERVER_PRIORITY(true, NS_SERVER, "priority"); +const TQName TQN_SERVER_IQ(true, NS_SERVER, "iq"); +const TQName TQN_SERVER_ERROR(true, NS_SERVER, "error"); + +const TQName TQN_SESSION_SESSION(true, NS_SESSION, "session"); + +const TQName TQN_PRIVACY_QUERY(true, NS_PRIVACY, "query"); +const TQName TQN_PRIVACY_ACTIVE(true, NS_PRIVACY, "active"); +const TQName TQN_PRIVACY_DEFAULT(true, NS_PRIVACY, "default"); +const TQName TQN_PRIVACY_LIST(true, NS_PRIVACY, "list"); +const TQName TQN_PRIVACY_ITEM(true, NS_PRIVACY, "item"); +const TQName TQN_PRIVACY_IQ(true, NS_PRIVACY, "iq"); +const TQName TQN_PRIVACY_MESSAGE(true, NS_PRIVACY, "message"); +const TQName TQN_PRIVACY_PRESENCE_IN(true, NS_PRIVACY, "presence-in"); +const TQName TQN_PRIVACY_PRESENCE_OUT(true, NS_PRIVACY, "presence-out"); + +const TQName TQN_ROSTER_QUERY(true, NS_ROSTER, "query"); +const TQName TQN_ROSTER_ITEM(true, NS_ROSTER, "item"); +const TQName TQN_ROSTER_GROUP(true, NS_ROSTER, "group"); + +const TQName TQN_VCARD_QUERY(true, NS_VCARD, "vCard"); +const TQName TQN_VCARD_FN(true, NS_VCARD, "FN"); + +const TQName TQN_XML_LANG(true, NS_XML, "lang"); + +const std::string STR_TYPE("type"); +const std::string STR_ID("id"); +const std::string STR_NAME("name"); +const std::string STR_JID("jid"); +const std::string STR_SUBSCRIPTION("subscription"); +const std::string STR_ASK("ask"); + +const TQName TQN_ENCODING(true, STR_EMPTY, STR_ENCODING); +const TQName TQN_VERSION(true, STR_EMPTY, STR_VERSION); +const TQName TQN_TO(true, STR_EMPTY, "to"); +const TQName TQN_FROM(true, STR_EMPTY, "from"); +const TQName TQN_TYPE(true, STR_EMPTY, "type"); +const TQName TQN_ID(true, STR_EMPTY, "id"); +const TQName TQN_CODE(true, STR_EMPTY, "code"); +const TQName TQN_NAME(true, STR_EMPTY, "name"); +const TQName TQN_VALUE(true, STR_EMPTY, "value"); +const TQName TQN_ACTION(true, STR_EMPTY, "action"); +const TQName TQN_ORDER(true, STR_EMPTY, "order"); +const TQName TQN_MECHANISM(true, STR_EMPTY, "mechanism"); +const TQName TQN_ASK(true, STR_EMPTY, "ask"); +const TQName TQN_JID(true, STR_EMPTY, "jid"); +const TQName TQN_SUBSCRIPTION(true, STR_EMPTY, "subscription"); +const TQName TQN_SOURCE(true, STR_EMPTY, "source"); + +const TQName TQN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT); +const TQName TQN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER); +const TQName TQN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM); + +// Presence +const std::string STR_SHOW_AWAY("away"); +const std::string STR_SHOW_CHAT("chat"); +const std::string STR_SHOW_DND("dnd"); +const std::string STR_SHOW_XA("xa"); + +// Subscription +const std::string STR_SUBSCRIBE("subscribe"); +const std::string STR_SUBSCRIBED("subscribed"); +const std::string STR_UNSUBSCRIBE("unsubscribe"); +const std::string STR_UNSUBSCRIBED("unsubscribed"); + + +// JEP 0030 +const TQName TQN_NODE(true, STR_EMPTY, "node"); +const TQName TQN_CATEGORY(true, STR_EMPTY, "category"); +const TQName TQN_VAR(true, STR_EMPTY, "var"); +const std::string NS_DISCO_INFO("http://jabber.org/protocol/disco#info"); +const std::string NS_DISCO_ITEMS("http://jabber.org/protocol/disco#items"); +const TQName TQN_DISCO_INFO_QUERY(true, NS_DISCO_INFO, "query"); +const TQName TQN_DISCO_IDENTITY(true, NS_DISCO_INFO, "identity"); +const TQName TQN_DISCO_FEATURE(true, NS_DISCO_INFO, "feature"); + +const TQName TQN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query"); +const TQName TQN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item"); + + +// JEP 0115 +const std::string NS_CAPS("http://jabber.org/protocol/caps"); +const TQName TQN_CAPS_C(true, NS_CAPS, "c"); +const TQName TQN_VER(true, STR_EMPTY, "ver"); +const TQName TQN_EXT(true, STR_EMPTY, "ext"); + +// JEP 0091 Delayed Delivery +const std::string kNSDelay("jabber:x:delay"); +const TQName kQnDelayX(true, kNSDelay, "x"); +const TQName kQnStamp(true, STR_EMPTY, "stamp"); + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc deleted file mode 100644 index b742e03a..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc +++ /dev/null @@ -1,477 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -extern "C" { -#include -} -#include -#include "talk/xmpp/jid.h" -#include "talk/xmpp/constants.h" -#include "talk/base/common.h" -#include - -#define new TRACK_NEW - -namespace buzz { - -static int AsciiToLower(int x) { - return (x <= 'Z' && x >= 'A') ? (x + ('a' - 'A')) : x; -} - -Jid::Jid() : data_(NULL) { -} - -Jid::Jid(bool is_special, const std::string & special) { - data_ = is_special ? new Data(special, STR_EMPTY, STR_EMPTY) : NULL; -} - -Jid::Jid(const std::string & jid_string) { - if (jid_string == STR_EMPTY) { - data_ = NULL; - return; - } - - // First find the slash and slice of that part - size_t slash = jid_string.find('/'); - std::string resource_name = (slash == std::string::npos ? STR_EMPTY : - jid_string.substr(slash + 1)); - - // Now look for the node - std::string node_name; - size_t at = jid_string.find('@'); - size_t domain_begin; - if (at < slash && at != std::string::npos) { - node_name = jid_string.substr(0, at); - domain_begin = at + 1; - } else { - domain_begin = 0; - } - - // Now take what is left as the domain - size_t domain_length = - ( slash == std::string::npos - ? jid_string.length() - domain_begin - : slash - domain_begin); - - // avoid allocating these constants repeatedly - std::string domain_name; - - if (domain_length == 9 && jid_string.find("gmail.com", domain_begin) == domain_begin) { - domain_name = STR_GMAIL_COM; - } - else if (domain_length == 14 && jid_string.find("googlemail.com", domain_begin) == domain_begin) { - domain_name = STR_GOOGLEMAIL_COM; - } - else if (domain_length == 10 && jid_string.find("google.com", domain_begin) == domain_begin) { - domain_name = STR_GOOGLE_COM; - } - else { - domain_name = jid_string.substr(domain_begin, domain_length); - } - - // If the domain is empty we have a non-valid jid and we should empty - // everything else out - if (domain_name.empty()) { - data_ = NULL; - return; - } - - bool valid_node; - std::string validated_node = prepNode(node_name, - node_name.begin(), node_name.end(), &valid_node); - bool valid_domain; - std::string validated_domain = prepDomain(domain_name, - domain_name.begin(), domain_name.end(), &valid_domain); - bool valid_resource; - std::string validated_resource = prepResource(resource_name, - resource_name.begin(), resource_name.end(), &valid_resource); - - if (!valid_node || !valid_domain || !valid_resource) { - data_ = NULL; - return; - } - - data_ = new Data(validated_node, validated_domain, validated_resource); -} - -Jid::Jid(const std::string & node_name, - const std::string & domain_name, - const std::string & resource_name) { - if (domain_name.empty()) { - data_ = NULL; - return; - } - - bool valid_node; - std::string validated_node = prepNode(node_name, - node_name.begin(), node_name.end(), &valid_node); - bool valid_domain; - std::string validated_domain = prepDomain(domain_name, - domain_name.begin(), domain_name.end(), &valid_domain); - bool valid_resource; - std::string validated_resource = prepResource(resource_name, - resource_name.begin(), resource_name.end(), &valid_resource); - - if (!valid_node || !valid_domain || !valid_resource) { - data_ = NULL; - return; - } - - data_ = new Data(validated_node, validated_domain, validated_resource); -} - -std::string Jid::Str() const { - if (!IsValid()) - return STR_EMPTY; - - std::string ret; - - if (!data_->node_name_.empty()) - ret = data_->node_name_ + "@"; - - ASSERT(data_->domain_name_ != STR_EMPTY); - ret += data_->domain_name_; - - if (!data_->resource_name_.empty()) - ret += "/" + data_->resource_name_; - - return ret; -} - -bool -Jid::IsValid() const { - return data_ != NULL && !data_->domain_name_.empty(); -} - -bool -Jid::IsBare() const { - return IsValid() && - data_->resource_name_.empty(); -} - -bool -Jid::IsFull() const { - return IsValid() && - !data_->resource_name_.empty(); -} - -Jid -Jid::BareJid() const { - if (!IsValid()) - return Jid(); - if (!IsFull()) - return *this; - return Jid(data_->node_name_, data_->domain_name_, STR_EMPTY); -} - -#if 0 -void -Jid::set_node(const std::string & node_name) { - data_->node_name_ = node_name; -} -void -Jid::set_domain(const std::string & domain_name) { - data_->domain_name_ = domain_name; -} -void -Jid::set_resource(const std::string & res_name) { - data_->resource_name_ = res_name; -} -#endif - -bool -Jid::BareEquals(const Jid & other) const { - return (other.data_ == data_ || - data_ != NULL && - other.data_ != NULL && - other.data_->node_name_ == data_->node_name_ && - other.data_->domain_name_ == data_->domain_name_); -} - -bool -Jid::operator==(const Jid & other) const { - return (other.data_ == data_ || - data_ != NULL && - other.data_ != NULL && - other.data_->node_name_ == data_->node_name_ && - other.data_->domain_name_ == data_->domain_name_ && - other.data_->resource_name_ == data_->resource_name_); -} - -int -Jid::Compare(const Jid & other) const { - if (other.data_ == data_) - return 0; - if (data_ == NULL) - return -1; - if (other.data_ == NULL) - return 1; - - int compare_result; - compare_result = data_->node_name_.compare(other.data_->node_name_); - if (0 != compare_result) - return compare_result; - compare_result = data_->domain_name_.compare(other.data_->domain_name_); - if (0 != compare_result) - return compare_result; - compare_result = data_->resource_name_.compare(other.data_->resource_name_); - return compare_result; -} - - -// --- JID parsing code: --- - -// Checks and normalizes the node part of a JID. -std::string -Jid::prepNode(const std::string str, std::string::const_iterator start, - std::string::const_iterator end, bool *valid) { - *valid = false; - std::string result; - - for (std::string::const_iterator i = start; i < end; i++) { - bool char_valid = true; - char ch = *i; - if (ch <= 0x7F) { - result += prepNodeAscii(ch, &char_valid); - } - else { - // TODO: implement the correct stringprep protocol for these - result += tolower(ch); - } - if (!char_valid) { - return STR_EMPTY; - } - } - - if (result.length() > 1023) { - return STR_EMPTY; - } - *valid = true; - return result; -} - - -// Returns the appropriate mapping for an ASCII character in a node. -char -Jid::prepNodeAscii(char ch, bool *valid) { - *valid = true; - switch (ch) { - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': - case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': - case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': - case 'V': case 'W': case 'X': case 'Y': case 'Z': - return (char)(ch + ('a' - 'A')); - - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: - case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: - case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: - case ' ': case '&': case '/': case ':': case '<': case '>': case '@': - case '\"': case '\'': - case 0x7F: - *valid = false; - return 0; - - default: - return ch; - } -} - - -// Checks and normalizes the resource part of a JID. -std::string -Jid::prepResource(const std::string str, std::string::const_iterator start, - std::string::const_iterator end, bool *valid) { - *valid = false; - std::string result; - - for (std::string::const_iterator i = start; i < end; i++) { - bool char_valid = true; - char ch = *i; - if (ch <= 0x7F) { - result += prepResourceAscii(ch, &char_valid); - } - else { - // TODO: implement the correct stringprep protocol for these - result += ch; - } - } - - if (result.length() > 1023) { - return STR_EMPTY; - } - *valid = true; - return result; -} - -// Returns the appropriate mapping for an ASCII character in a resource. -char -Jid::prepResourceAscii(char ch, bool *valid) { - *valid = true; - switch (ch) { - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: - case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: - case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: - case 0x7F: - *valid = false; - return 0; - - default: - return ch; - } -} - -// Checks and normalizes the domain part of a JID. -std::string -Jid::prepDomain(const std::string str, std::string::const_iterator start, - std::string::const_iterator end, bool *valid) { - *valid = false; - std::string result; - - // TODO: if the domain contains a ':', then we should parse it - // as an IPv6 address rather than giving an error about illegal domain. - prepDomain(str, start, end, &result, valid); - if (!*valid) { - return STR_EMPTY; - } - - if (result.length() > 1023) { - return STR_EMPTY; - } - *valid = true; - return result; -} - - -// Checks and normalizes an IDNA domain. -void -Jid::prepDomain(const std::string str, std::string::const_iterator start, - std::string::const_iterator end, std::string *buf, bool *valid) { - *valid = false; - std::string::const_iterator last = start; - for (std::string::const_iterator i = start; i < end; i++) { - bool label_valid = true; - char ch = *i; - switch (ch) { - case 0x002E: -#if 0 // FIX: This isn't UTF-8-aware. - case 0x3002: - case 0xFF0E: - case 0xFF61: -#endif - prepDomainLabel(str, last, i, buf, &label_valid); - *buf += '.'; - last = i + 1; - break; - } - if (!label_valid) { - return; - } - } - prepDomainLabel(str, last, end, buf, valid); -} - -// Checks and normalizes a domain label. -void -Jid::prepDomainLabel(const std::string str, std::string::const_iterator start, - std::string::const_iterator end, std::string *buf, bool *valid) { - *valid = false; - - int startLen = buf->length(); - for (std::string::const_iterator i = start; i < end; i++) { - bool char_valid = true; - char ch = *i; - if (ch <= 0x7F) { - *buf += prepDomainLabelAscii(ch, &char_valid); - } - else { - // TODO: implement ToASCII for these - *buf += ch; - } - if (!char_valid) { - return; - } - } - - int count = buf->length() - startLen; - if (count == 0) { - return; - } - else if (count > 63) { - return; - } - - // Is this check needed? See comment in prepDomainLabelAscii. - if ((*buf)[startLen] == '-') { - return; - } - if ((*buf)[buf->length() - 1] == '-') { - return; - } - *valid = true; -} - - -// Returns the appropriate mapping for an ASCII character in a domain label. -char -Jid::prepDomainLabelAscii(char ch, bool *valid) { - *valid = true; - // TODO: A literal reading of the spec seems to say that we do - // not need to check for these illegal characters (an "internationalized - // domain label" runs ToASCII with UseSTD3... set to false). But that - // can't be right. We should at least be checking that there are no '/' - // or '@' characters in the domain. Perhaps we should see what others - // do in this case. - - switch (ch) { - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': - case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': - case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': - case 'V': case 'W': case 'X': case 'Y': case 'Z': - return (char)(ch + ('a' - 'A')); - - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: - case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: - case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: - case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: - case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: - case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A: - case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: - case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: - case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: - *valid = false; - return 0; - - default: - return ch; - } -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cpp new file mode 100644 index 00000000..b742e03a --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cpp @@ -0,0 +1,477 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +extern "C" { +#include +} +#include +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" +#include "talk/base/common.h" +#include + +#define new TRACK_NEW + +namespace buzz { + +static int AsciiToLower(int x) { + return (x <= 'Z' && x >= 'A') ? (x + ('a' - 'A')) : x; +} + +Jid::Jid() : data_(NULL) { +} + +Jid::Jid(bool is_special, const std::string & special) { + data_ = is_special ? new Data(special, STR_EMPTY, STR_EMPTY) : NULL; +} + +Jid::Jid(const std::string & jid_string) { + if (jid_string == STR_EMPTY) { + data_ = NULL; + return; + } + + // First find the slash and slice of that part + size_t slash = jid_string.find('/'); + std::string resource_name = (slash == std::string::npos ? STR_EMPTY : + jid_string.substr(slash + 1)); + + // Now look for the node + std::string node_name; + size_t at = jid_string.find('@'); + size_t domain_begin; + if (at < slash && at != std::string::npos) { + node_name = jid_string.substr(0, at); + domain_begin = at + 1; + } else { + domain_begin = 0; + } + + // Now take what is left as the domain + size_t domain_length = + ( slash == std::string::npos + ? jid_string.length() - domain_begin + : slash - domain_begin); + + // avoid allocating these constants repeatedly + std::string domain_name; + + if (domain_length == 9 && jid_string.find("gmail.com", domain_begin) == domain_begin) { + domain_name = STR_GMAIL_COM; + } + else if (domain_length == 14 && jid_string.find("googlemail.com", domain_begin) == domain_begin) { + domain_name = STR_GOOGLEMAIL_COM; + } + else if (domain_length == 10 && jid_string.find("google.com", domain_begin) == domain_begin) { + domain_name = STR_GOOGLE_COM; + } + else { + domain_name = jid_string.substr(domain_begin, domain_length); + } + + // If the domain is empty we have a non-valid jid and we should empty + // everything else out + if (domain_name.empty()) { + data_ = NULL; + return; + } + + bool valid_node; + std::string validated_node = prepNode(node_name, + node_name.begin(), node_name.end(), &valid_node); + bool valid_domain; + std::string validated_domain = prepDomain(domain_name, + domain_name.begin(), domain_name.end(), &valid_domain); + bool valid_resource; + std::string validated_resource = prepResource(resource_name, + resource_name.begin(), resource_name.end(), &valid_resource); + + if (!valid_node || !valid_domain || !valid_resource) { + data_ = NULL; + return; + } + + data_ = new Data(validated_node, validated_domain, validated_resource); +} + +Jid::Jid(const std::string & node_name, + const std::string & domain_name, + const std::string & resource_name) { + if (domain_name.empty()) { + data_ = NULL; + return; + } + + bool valid_node; + std::string validated_node = prepNode(node_name, + node_name.begin(), node_name.end(), &valid_node); + bool valid_domain; + std::string validated_domain = prepDomain(domain_name, + domain_name.begin(), domain_name.end(), &valid_domain); + bool valid_resource; + std::string validated_resource = prepResource(resource_name, + resource_name.begin(), resource_name.end(), &valid_resource); + + if (!valid_node || !valid_domain || !valid_resource) { + data_ = NULL; + return; + } + + data_ = new Data(validated_node, validated_domain, validated_resource); +} + +std::string Jid::Str() const { + if (!IsValid()) + return STR_EMPTY; + + std::string ret; + + if (!data_->node_name_.empty()) + ret = data_->node_name_ + "@"; + + ASSERT(data_->domain_name_ != STR_EMPTY); + ret += data_->domain_name_; + + if (!data_->resource_name_.empty()) + ret += "/" + data_->resource_name_; + + return ret; +} + +bool +Jid::IsValid() const { + return data_ != NULL && !data_->domain_name_.empty(); +} + +bool +Jid::IsBare() const { + return IsValid() && + data_->resource_name_.empty(); +} + +bool +Jid::IsFull() const { + return IsValid() && + !data_->resource_name_.empty(); +} + +Jid +Jid::BareJid() const { + if (!IsValid()) + return Jid(); + if (!IsFull()) + return *this; + return Jid(data_->node_name_, data_->domain_name_, STR_EMPTY); +} + +#if 0 +void +Jid::set_node(const std::string & node_name) { + data_->node_name_ = node_name; +} +void +Jid::set_domain(const std::string & domain_name) { + data_->domain_name_ = domain_name; +} +void +Jid::set_resource(const std::string & res_name) { + data_->resource_name_ = res_name; +} +#endif + +bool +Jid::BareEquals(const Jid & other) const { + return (other.data_ == data_ || + data_ != NULL && + other.data_ != NULL && + other.data_->node_name_ == data_->node_name_ && + other.data_->domain_name_ == data_->domain_name_); +} + +bool +Jid::operator==(const Jid & other) const { + return (other.data_ == data_ || + data_ != NULL && + other.data_ != NULL && + other.data_->node_name_ == data_->node_name_ && + other.data_->domain_name_ == data_->domain_name_ && + other.data_->resource_name_ == data_->resource_name_); +} + +int +Jid::Compare(const Jid & other) const { + if (other.data_ == data_) + return 0; + if (data_ == NULL) + return -1; + if (other.data_ == NULL) + return 1; + + int compare_result; + compare_result = data_->node_name_.compare(other.data_->node_name_); + if (0 != compare_result) + return compare_result; + compare_result = data_->domain_name_.compare(other.data_->domain_name_); + if (0 != compare_result) + return compare_result; + compare_result = data_->resource_name_.compare(other.data_->resource_name_); + return compare_result; +} + + +// --- JID parsing code: --- + +// Checks and normalizes the node part of a JID. +std::string +Jid::prepNode(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + char ch = *i; + if (ch <= 0x7F) { + result += prepNodeAscii(ch, &char_valid); + } + else { + // TODO: implement the correct stringprep protocol for these + result += tolower(ch); + } + if (!char_valid) { + return STR_EMPTY; + } + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + + +// Returns the appropriate mapping for an ASCII character in a node. +char +Jid::prepNodeAscii(char ch, bool *valid) { + *valid = true; + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + return (char)(ch + ('a' - 'A')); + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case ' ': case '&': case '/': case ':': case '<': case '>': case '@': + case '\"': case '\'': + case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + + +// Checks and normalizes the resource part of a JID. +std::string +Jid::prepResource(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + char ch = *i; + if (ch <= 0x7F) { + result += prepResourceAscii(ch, &char_valid); + } + else { + // TODO: implement the correct stringprep protocol for these + result += ch; + } + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + +// Returns the appropriate mapping for an ASCII character in a resource. +char +Jid::prepResourceAscii(char ch, bool *valid) { + *valid = true; + switch (ch) { + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + +// Checks and normalizes the domain part of a JID. +std::string +Jid::prepDomain(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + // TODO: if the domain contains a ':', then we should parse it + // as an IPv6 address rather than giving an error about illegal domain. + prepDomain(str, start, end, &result, valid); + if (!*valid) { + return STR_EMPTY; + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + + +// Checks and normalizes an IDNA domain. +void +Jid::prepDomain(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, std::string *buf, bool *valid) { + *valid = false; + std::string::const_iterator last = start; + for (std::string::const_iterator i = start; i < end; i++) { + bool label_valid = true; + char ch = *i; + switch (ch) { + case 0x002E: +#if 0 // FIX: This isn't UTF-8-aware. + case 0x3002: + case 0xFF0E: + case 0xFF61: +#endif + prepDomainLabel(str, last, i, buf, &label_valid); + *buf += '.'; + last = i + 1; + break; + } + if (!label_valid) { + return; + } + } + prepDomainLabel(str, last, end, buf, valid); +} + +// Checks and normalizes a domain label. +void +Jid::prepDomainLabel(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, std::string *buf, bool *valid) { + *valid = false; + + int startLen = buf->length(); + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + char ch = *i; + if (ch <= 0x7F) { + *buf += prepDomainLabelAscii(ch, &char_valid); + } + else { + // TODO: implement ToASCII for these + *buf += ch; + } + if (!char_valid) { + return; + } + } + + int count = buf->length() - startLen; + if (count == 0) { + return; + } + else if (count > 63) { + return; + } + + // Is this check needed? See comment in prepDomainLabelAscii. + if ((*buf)[startLen] == '-') { + return; + } + if ((*buf)[buf->length() - 1] == '-') { + return; + } + *valid = true; +} + + +// Returns the appropriate mapping for an ASCII character in a domain label. +char +Jid::prepDomainLabelAscii(char ch, bool *valid) { + *valid = true; + // TODO: A literal reading of the spec seems to say that we do + // not need to check for these illegal characters (an "internationalized + // domain label" runs ToASCII with UseSTD3... set to false). But that + // can't be right. We should at least be checking that there are no '/' + // or '@' characters in the domain. Perhaps we should see what others + // do in this case. + + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + return (char)(ch + ('a' - 'A')); + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: + case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: + case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A: + case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: + case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: + case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc deleted file mode 100644 index 4a6d7c24..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/base64.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/xmpp/constants.h" -#include "talk/xmpp/saslmechanism.h" - -namespace buzz { - -XmlElement * -SaslMechanism::StartSaslAuth() { - return new XmlElement(TQN_SASL_AUTH, true); -} - -XmlElement * -SaslMechanism::HandleSaslChallenge(const XmlElement * challenge) { - return new XmlElement(TQN_SASL_ABORT, true); -} - -void -SaslMechanism::HandleSaslSuccess(const XmlElement * success) { -} - -void -SaslMechanism::HandleSaslFailure(const XmlElement * failure) { -} - -std::string -SaslMechanism::Base64Encode(const std::string & plain) { - return Base64::encode(plain); -} - -std::string -SaslMechanism::Base64Decode(const std::string & encoded) { - return Base64::decode(encoded); -} - -std::string -SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) { - return Base64::encodeFromArray(plain, length); -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cpp new file mode 100644 index 00000000..4a6d7c24 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cpp @@ -0,0 +1,68 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/base/base64.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/saslmechanism.h" + +namespace buzz { + +XmlElement * +SaslMechanism::StartSaslAuth() { + return new XmlElement(TQN_SASL_AUTH, true); +} + +XmlElement * +SaslMechanism::HandleSaslChallenge(const XmlElement * challenge) { + return new XmlElement(TQN_SASL_ABORT, true); +} + +void +SaslMechanism::HandleSaslSuccess(const XmlElement * success) { +} + +void +SaslMechanism::HandleSaslFailure(const XmlElement * failure) { +} + +std::string +SaslMechanism::Base64Encode(const std::string & plain) { + return Base64::encode(plain); +} + +std::string +SaslMechanism::Base64Decode(const std::string & encoded) { + return Base64::decode(encoded); +} + +std::string +SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) { + return Base64::encodeFromArray(plain, length); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc deleted file mode 100644 index 959b6f88..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc +++ /dev/null @@ -1,372 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "xmppclient.h" -#include "xmpptask.h" -#include "talk/xmpp/constants.h" -#include "talk/base/sigslot.h" -#include "talk/xmpp/saslplainmechanism.h" -#include "talk/xmpp/prexmppauth.h" -#include "talk/base/scoped_ptr.h" -#include "talk/xmpp/plainsaslhandler.h" - -namespace buzz { - -Task * -XmppClient::GetParent(int code) { - if (code == XMPP_CLIENT_TASK_CODE) - return this; - else - return Task::GetParent(code); -} - -class XmppClient::Private : - public sigslot::has_slots<>, - public XmppSessionHandler, - public XmppOutputHandler { -public: - - Private(XmppClient * client) : - client_(client), - socket_(NULL), - engine_(NULL), - proxy_port_(0), - pre_engine_error_(XmppEngine::ERROR_NONE), - signal_closed_(false) {} - - // the owner - XmppClient * const client_; - - // the two main objects - scoped_ptr socket_; - scoped_ptr engine_; - scoped_ptr pre_auth_; - XmppPassword pass_; - std::string auth_cookie_; - cricket::SocketAddress server_; - std::string proxy_host_; - int proxy_port_; - XmppEngine::Error pre_engine_error_; - CaptchaChallenge captcha_challenge_; - bool signal_closed_; - - // implementations of interfaces - void OnStateChange(int state); - void WriteOutput(const char * bytes, size_t len); - void StartTls(const std::string & domainname); - void CloseConnection(); - - // slots for socket signals - void OnSocketConnected(); - void OnSocketRead(); - void OnSocketClosed(); -}; - -XmppReturnStatus -XmppClient::Connect(const XmppClientSettings & settings, AsyncSocket * socket, PreXmppAuth * pre_auth) { - if (socket == NULL) - return XMPP_RETURN_BADARGUMENT; - if (d_->socket_.get() != NULL) - return XMPP_RETURN_BADSTATE; - - d_->socket_.reset(socket); - - d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected); - d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead); - d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed); - - d_->engine_.reset(XmppEngine::Create()); - d_->engine_->SetSessionHandler(d_.get()); - d_->engine_->SetOutputHandler(d_.get()); - d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY)); - if (!settings.resource().empty()) { - d_->engine_->SetRequestedResource(settings.resource()); - } - d_->engine_->SetUseTls(settings.use_tls()); - - - d_->pass_ = settings.pass(); - d_->auth_cookie_ = settings.auth_cookie(); - d_->server_ = settings.server(); - d_->proxy_host_ = settings.proxy_host(); - d_->proxy_port_ = settings.proxy_port(); - d_->pre_auth_.reset(pre_auth); - - return XMPP_RETURN_OK; -} - -XmppEngine::State -XmppClient::GetState() { - if (d_->engine_.get() == NULL) - return XmppEngine::STATE_NONE; - return d_->engine_->GetState(); -} - -XmppEngine::Error -XmppClient::GetError() { - if (d_->engine_.get() == NULL) - return XmppEngine::ERROR_NONE; - if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) - return d_->pre_engine_error_; - return d_->engine_->GetError(); -} - -CaptchaChallenge XmppClient::GetCaptchaChallenge() { - if (d_->engine_.get() == NULL) - return CaptchaChallenge(); - return d_->captcha_challenge_; -} - -std::string -XmppClient::GetAuthCookie() { - if (d_->engine_.get() == NULL) - return ""; - return d_->auth_cookie_; -} - -static void -ForgetPassword(std::string & to_erase) { - size_t len = to_erase.size(); - for (size_t i = 0; i < len; i++) { - // get rid of characters - to_erase[i] = 'x'; - } - // get rid of length - to_erase.erase(); -} - -int -XmppClient::ProcessStart() { - if (d_->pre_auth_.get()) { - d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone); - d_->pre_auth_->StartPreXmppAuth( - d_->engine_->GetUser(), d_->server_, d_->pass_, d_->auth_cookie_); - d_->pass_.Clear(); // done with this; - return STATE_PRE_XMPP_LOGIN; - } - else { - d_->engine_->SetSaslHandler(new PlainSaslHandler( - d_->engine_->GetUser(), d_->pass_)); - d_->pass_.Clear(); // done with this; - return STATE_START_XMPP_LOGIN; - } -} - -void -XmppClient::OnAuthDone() { - Wake(); -} - -int -XmppClient::ProcessCookieLogin() { - // Don't know how this could happen, but crash reports show it as NULL - if (!d_->pre_auth_.get()) { - d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; - EnsureClosed(); - return STATE_ERROR; - } - - // Wait until pre authentication is done is done - if (!d_->pre_auth_->IsAuthDone()) - return STATE_BLOCKED; - - if (!d_->pre_auth_->IsAuthorized()) { - // maybe split out a case when gaia is down? - if (d_->pre_auth_->HadError()) { - d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; - } - else { - d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED; - d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge(); - } - d_->pre_auth_.reset(NULL); // done with this - EnsureClosed(); - return STATE_ERROR; - } - - // Save auth cookie as a result - d_->auth_cookie_ = d_->pre_auth_->GetAuthCookie(); - - // transfer ownership of pre_auth_ to engine - d_->engine_->SetSaslHandler(d_->pre_auth_.release()); - - return STATE_START_XMPP_LOGIN; -} - -int -XmppClient::ProcessStartXmppLogin() { - // Done with pre-connect tasks - connect! - if (!d_->socket_->Connect(d_->server_)) { - EnsureClosed(); - return STATE_ERROR; - } - - return STATE_RESPONSE; -} - -int -XmppClient::ProcessResponse() { - // Hang around while we are connected. - if (!delivering_signal_ && (d_->engine_.get() == NULL || - d_->engine_->GetState() == XmppEngine::STATE_CLOSED)) - return STATE_DONE; - return STATE_BLOCKED; -} - -XmppReturnStatus -XmppClient::Disconnect() { - if (d_->socket_.get() == NULL) - return XMPP_RETURN_BADSTATE; - d_->engine_->Disconnect(); - return XMPP_RETURN_OK; -} - -XmppClient::XmppClient(Task * parent) : Task(parent), - delivering_signal_(false) { - d_.reset(new Private(this)); -} - -XmppClient::~XmppClient() {} - -const Jid & -XmppClient::jid() { - return d_->engine_->FullJid(); -} - - -std::string -XmppClient::NextId() { - return d_->engine_->NextId(); -} - -XmppReturnStatus -XmppClient::SendStanza(const XmlElement * stanza) { - return d_->engine_->SendStanza(stanza); -} - -XmppReturnStatus -XmppClient::SendStanzaError(const XmlElement * old_stanza, XmppStanzaError xse, const std::string & message) { - return d_->engine_->SendStanzaError(old_stanza, xse, message); -} - -XmppReturnStatus -XmppClient::SendRaw(const std::string & text) { - return d_->engine_->SendRaw(text); -} - -XmppEngine* -XmppClient::engine() { - return d_->engine_.get(); -} - -void -XmppClient::Private::OnSocketConnected() { - engine_->Connect(); -} - -void -XmppClient::Private::OnSocketRead() { - char bytes[4096]; - size_t bytes_read; - for (;;) { - if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) { - // TODO: deal with error information - return; - } - - if (bytes_read == 0) - return; - -//#ifdef _DEBUG - client_->SignalLogInput(bytes, bytes_read); -//#endif - - engine_->HandleInput(bytes, bytes_read); - } -} - -void -XmppClient::Private::OnSocketClosed() { - engine_->ConnectionClosed(); -} - -void -XmppClient::Private::OnStateChange(int state) { - if (state == XmppEngine::STATE_CLOSED) { - client_->EnsureClosed(); - } - else { - client_->SignalStateChange((XmppEngine::State)state); - } - client_->Wake(); -} - -void -XmppClient::Private::WriteOutput(const char * bytes, size_t len) { - -//#ifdef _DEBUG - client_->SignalLogOutput(bytes, len); -//#endif - - socket_->Write(bytes, len); - // TODO: deal with error information -} - -void -XmppClient::Private::StartTls(const std::string & domain) { -#if defined(FEATURE_ENABLE_SSL) - socket_->StartTls(domain); -#endif -} - -void -XmppClient::Private::CloseConnection() { - socket_->Close(); -} - -void -XmppClient::AddXmppTask(XmppTask * task, XmppEngine::HandlerLevel level) { - d_->engine_->AddStanzaHandler(task, level); -} - -void -XmppClient::RemoveXmppTask(XmppTask * task) { - d_->engine_->RemoveStanzaHandler(task); -} - -void -XmppClient::EnsureClosed() { - if (!d_->signal_closed_) { - d_->signal_closed_ = true; - delivering_signal_ = true; - SignalStateChange(XmppEngine::STATE_CLOSED); - delivering_signal_ = false; - } -} - - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cpp new file mode 100644 index 00000000..959b6f88 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cpp @@ -0,0 +1,372 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "xmppclient.h" +#include "xmpptask.h" +#include "talk/xmpp/constants.h" +#include "talk/base/sigslot.h" +#include "talk/xmpp/saslplainmechanism.h" +#include "talk/xmpp/prexmppauth.h" +#include "talk/base/scoped_ptr.h" +#include "talk/xmpp/plainsaslhandler.h" + +namespace buzz { + +Task * +XmppClient::GetParent(int code) { + if (code == XMPP_CLIENT_TASK_CODE) + return this; + else + return Task::GetParent(code); +} + +class XmppClient::Private : + public sigslot::has_slots<>, + public XmppSessionHandler, + public XmppOutputHandler { +public: + + Private(XmppClient * client) : + client_(client), + socket_(NULL), + engine_(NULL), + proxy_port_(0), + pre_engine_error_(XmppEngine::ERROR_NONE), + signal_closed_(false) {} + + // the owner + XmppClient * const client_; + + // the two main objects + scoped_ptr socket_; + scoped_ptr engine_; + scoped_ptr pre_auth_; + XmppPassword pass_; + std::string auth_cookie_; + cricket::SocketAddress server_; + std::string proxy_host_; + int proxy_port_; + XmppEngine::Error pre_engine_error_; + CaptchaChallenge captcha_challenge_; + bool signal_closed_; + + // implementations of interfaces + void OnStateChange(int state); + void WriteOutput(const char * bytes, size_t len); + void StartTls(const std::string & domainname); + void CloseConnection(); + + // slots for socket signals + void OnSocketConnected(); + void OnSocketRead(); + void OnSocketClosed(); +}; + +XmppReturnStatus +XmppClient::Connect(const XmppClientSettings & settings, AsyncSocket * socket, PreXmppAuth * pre_auth) { + if (socket == NULL) + return XMPP_RETURN_BADARGUMENT; + if (d_->socket_.get() != NULL) + return XMPP_RETURN_BADSTATE; + + d_->socket_.reset(socket); + + d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected); + d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead); + d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed); + + d_->engine_.reset(XmppEngine::Create()); + d_->engine_->SetSessionHandler(d_.get()); + d_->engine_->SetOutputHandler(d_.get()); + d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY)); + if (!settings.resource().empty()) { + d_->engine_->SetRequestedResource(settings.resource()); + } + d_->engine_->SetUseTls(settings.use_tls()); + + + d_->pass_ = settings.pass(); + d_->auth_cookie_ = settings.auth_cookie(); + d_->server_ = settings.server(); + d_->proxy_host_ = settings.proxy_host(); + d_->proxy_port_ = settings.proxy_port(); + d_->pre_auth_.reset(pre_auth); + + return XMPP_RETURN_OK; +} + +XmppEngine::State +XmppClient::GetState() { + if (d_->engine_.get() == NULL) + return XmppEngine::STATE_NONE; + return d_->engine_->GetState(); +} + +XmppEngine::Error +XmppClient::GetError() { + if (d_->engine_.get() == NULL) + return XmppEngine::ERROR_NONE; + if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) + return d_->pre_engine_error_; + return d_->engine_->GetError(); +} + +CaptchaChallenge XmppClient::GetCaptchaChallenge() { + if (d_->engine_.get() == NULL) + return CaptchaChallenge(); + return d_->captcha_challenge_; +} + +std::string +XmppClient::GetAuthCookie() { + if (d_->engine_.get() == NULL) + return ""; + return d_->auth_cookie_; +} + +static void +ForgetPassword(std::string & to_erase) { + size_t len = to_erase.size(); + for (size_t i = 0; i < len; i++) { + // get rid of characters + to_erase[i] = 'x'; + } + // get rid of length + to_erase.erase(); +} + +int +XmppClient::ProcessStart() { + if (d_->pre_auth_.get()) { + d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone); + d_->pre_auth_->StartPreXmppAuth( + d_->engine_->GetUser(), d_->server_, d_->pass_, d_->auth_cookie_); + d_->pass_.Clear(); // done with this; + return STATE_PRE_XMPP_LOGIN; + } + else { + d_->engine_->SetSaslHandler(new PlainSaslHandler( + d_->engine_->GetUser(), d_->pass_)); + d_->pass_.Clear(); // done with this; + return STATE_START_XMPP_LOGIN; + } +} + +void +XmppClient::OnAuthDone() { + Wake(); +} + +int +XmppClient::ProcessCookieLogin() { + // Don't know how this could happen, but crash reports show it as NULL + if (!d_->pre_auth_.get()) { + d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; + EnsureClosed(); + return STATE_ERROR; + } + + // Wait until pre authentication is done is done + if (!d_->pre_auth_->IsAuthDone()) + return STATE_BLOCKED; + + if (!d_->pre_auth_->IsAuthorized()) { + // maybe split out a case when gaia is down? + if (d_->pre_auth_->HadError()) { + d_->pre_engine_error_ = XmppEngine::ERROR_AUTH; + } + else { + d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED; + d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge(); + } + d_->pre_auth_.reset(NULL); // done with this + EnsureClosed(); + return STATE_ERROR; + } + + // Save auth cookie as a result + d_->auth_cookie_ = d_->pre_auth_->GetAuthCookie(); + + // transfer ownership of pre_auth_ to engine + d_->engine_->SetSaslHandler(d_->pre_auth_.release()); + + return STATE_START_XMPP_LOGIN; +} + +int +XmppClient::ProcessStartXmppLogin() { + // Done with pre-connect tasks - connect! + if (!d_->socket_->Connect(d_->server_)) { + EnsureClosed(); + return STATE_ERROR; + } + + return STATE_RESPONSE; +} + +int +XmppClient::ProcessResponse() { + // Hang around while we are connected. + if (!delivering_signal_ && (d_->engine_.get() == NULL || + d_->engine_->GetState() == XmppEngine::STATE_CLOSED)) + return STATE_DONE; + return STATE_BLOCKED; +} + +XmppReturnStatus +XmppClient::Disconnect() { + if (d_->socket_.get() == NULL) + return XMPP_RETURN_BADSTATE; + d_->engine_->Disconnect(); + return XMPP_RETURN_OK; +} + +XmppClient::XmppClient(Task * parent) : Task(parent), + delivering_signal_(false) { + d_.reset(new Private(this)); +} + +XmppClient::~XmppClient() {} + +const Jid & +XmppClient::jid() { + return d_->engine_->FullJid(); +} + + +std::string +XmppClient::NextId() { + return d_->engine_->NextId(); +} + +XmppReturnStatus +XmppClient::SendStanza(const XmlElement * stanza) { + return d_->engine_->SendStanza(stanza); +} + +XmppReturnStatus +XmppClient::SendStanzaError(const XmlElement * old_stanza, XmppStanzaError xse, const std::string & message) { + return d_->engine_->SendStanzaError(old_stanza, xse, message); +} + +XmppReturnStatus +XmppClient::SendRaw(const std::string & text) { + return d_->engine_->SendRaw(text); +} + +XmppEngine* +XmppClient::engine() { + return d_->engine_.get(); +} + +void +XmppClient::Private::OnSocketConnected() { + engine_->Connect(); +} + +void +XmppClient::Private::OnSocketRead() { + char bytes[4096]; + size_t bytes_read; + for (;;) { + if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) { + // TODO: deal with error information + return; + } + + if (bytes_read == 0) + return; + +//#ifdef _DEBUG + client_->SignalLogInput(bytes, bytes_read); +//#endif + + engine_->HandleInput(bytes, bytes_read); + } +} + +void +XmppClient::Private::OnSocketClosed() { + engine_->ConnectionClosed(); +} + +void +XmppClient::Private::OnStateChange(int state) { + if (state == XmppEngine::STATE_CLOSED) { + client_->EnsureClosed(); + } + else { + client_->SignalStateChange((XmppEngine::State)state); + } + client_->Wake(); +} + +void +XmppClient::Private::WriteOutput(const char * bytes, size_t len) { + +//#ifdef _DEBUG + client_->SignalLogOutput(bytes, len); +//#endif + + socket_->Write(bytes, len); + // TODO: deal with error information +} + +void +XmppClient::Private::StartTls(const std::string & domain) { +#if defined(FEATURE_ENABLE_SSL) + socket_->StartTls(domain); +#endif +} + +void +XmppClient::Private::CloseConnection() { + socket_->Close(); +} + +void +XmppClient::AddXmppTask(XmppTask * task, XmppEngine::HandlerLevel level) { + d_->engine_->AddStanzaHandler(task, level); +} + +void +XmppClient::RemoveXmppTask(XmppTask * task) { + d_->engine_->RemoveStanzaHandler(task); +} + +void +XmppClient::EnsureClosed() { + if (!d_->signal_closed_) { + d_->signal_closed_ = true; + delivering_signal_ = true; + SignalStateChange(XmppEngine::STATE_CLOSED); + delivering_signal_ = false; + } +} + + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc deleted file mode 100644 index 3d363a78..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc +++ /dev/null @@ -1,480 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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. - */ - -#define TRACK_ARRAY_ALLOC_PROBLEM - -#include -#include -#include -#include "talk/xmllite/xmlelement.h" -#include "talk/base/common.h" -#include "talk/xmpp/xmppengineimpl.h" -#include "talk/xmpp/xmpplogintask.h" -#include "talk/xmpp/constants.h" -#include "talk/xmllite/xmlprinter.h" -#include "talk/xmpp/saslhandler.h" -// #include "buzz/saslmechanism.h" - -#define new TRACK_NEW - -namespace buzz { - -static const std::string XMPP_CLIENT_NAMESPACES[] = { - "stream", "http://etherx.jabber.org/streams", - "", "jabber:client", -}; - -static const size_t XMPP_CLIENT_NAMESPACES_LEN = 4; - -XmppEngine * XmppEngine::Create() { - return new XmppEngineImpl(); -} - - -XmppEngineImpl::XmppEngineImpl() : - stanzaParseHandler_(this), - stanzaParser_(&stanzaParseHandler_), - engine_entered_(0), - user_jid_(JID_EMPTY), - password_(), - requested_resource_(STR_EMPTY), - tls_needed_(true), - login_task_(new XmppLoginTask(this)), - next_id_(0), - bound_jid_(JID_EMPTY), - state_(STATE_START), - encrypted_(false), - error_code_(ERROR_NONE), - stream_error_(NULL), - raised_reset_(false), - output_handler_(NULL), - session_handler_(NULL), - iq_entries_(new IqEntryVector()), - output_(new std::stringstream()), - sasl_handler_(NULL) { - for (int i = 0; i < HL_COUNT; i+= 1) { - stanza_handlers_[i].reset(new StanzaHandlerVector()); - } -} - -XmppEngineImpl::~XmppEngineImpl() { - DeleteIqCookies(); -} - -XmppReturnStatus -XmppEngineImpl::SetOutputHandler(XmppOutputHandler* output_handler) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - output_handler_ = output_handler; - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::SetSessionHandler(XmppSessionHandler* session_handler) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - session_handler_ = session_handler; - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::HandleInput(const char * bytes, size_t len) { - if (state_ < STATE_OPENING || state_ > STATE_OPEN) - return XMPP_RETURN_BADSTATE; - - EnterExit ee(this); - - stanzaParser_.Parse(bytes, len, false); - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::ConnectionClosed() { - if (state_ != STATE_CLOSED) { - EnterExit ee(this); - // If told that connection closed and not already closed, - // then connection was unpexectedly dropped. - SignalError(ERROR_CONNECTION_CLOSED); - } - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::SetUseTls(bool useTls) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - tls_needed_ = useTls; - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::SetTlsServerDomain(const std::string & tls_server_domain) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - tls_server_domain_= tls_server_domain; - - return XMPP_RETURN_OK; -} - -bool -XmppEngineImpl::GetUseTls() { - return tls_needed_; -} - -XmppReturnStatus -XmppEngineImpl::SetUser(const Jid & jid) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - user_jid_ = jid; - - return XMPP_RETURN_OK; -} - -const Jid & -XmppEngineImpl::GetUser() { - return user_jid_; -} - -XmppReturnStatus -XmppEngineImpl::SetSaslHandler(SaslHandler * sasl_handler) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - sasl_handler_.reset(sasl_handler); - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::SetRequestedResource(const std::string & resource) { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - requested_resource_ = resource; - - return XMPP_RETURN_OK; -} - -const std::string & -XmppEngineImpl::GetRequestedResource() { - return requested_resource_; -} - -XmppReturnStatus -XmppEngineImpl::AddStanzaHandler(XmppStanzaHandler * stanza_handler, - XmppEngine::HandlerLevel level) { - if (state_ == STATE_CLOSED) - return XMPP_RETURN_BADSTATE; - - stanza_handlers_[level]->push_back(stanza_handler); - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::RemoveStanzaHandler(XmppStanzaHandler * stanza_handler) { - - bool found = false; - - for (int level = 0; level < HL_COUNT; level += 1) { - StanzaHandlerVector::iterator new_end = - std::remove(stanza_handlers_[level]->begin(), - stanza_handlers_[level]->end(), - stanza_handler); - - if (new_end != stanza_handlers_[level]->end()) { - stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end()); - found = true; - } - } - - if (!found) { - return XMPP_RETURN_BADARGUMENT; - } - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::Connect() { - if (state_ != STATE_START) - return XMPP_RETURN_BADSTATE; - - EnterExit ee(this); - - // get the login task started - state_ = STATE_OPENING; - if (login_task_.get()) { - login_task_->IncomingStanza(NULL, false); - if (login_task_->IsDone()) - login_task_.reset(); - } - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::SendStanza(const XmlElement * element) { - if (state_ == STATE_CLOSED) - return XMPP_RETURN_BADSTATE; - - EnterExit ee(this); - - if (login_task_.get()) { - // still handshaking - then outbound stanzas are queued - login_task_->OutgoingStanza(element); - } else { - // handshake done - send straight through - InternalSendStanza(element); - } - - return XMPP_RETURN_OK; -} - -XmppReturnStatus -XmppEngineImpl::SendRaw(const std::string & text) { - if (state_ == STATE_CLOSED || login_task_.get()) - return XMPP_RETURN_BADSTATE; - - EnterExit ee(this); - - (*output_) << text; - - return XMPP_RETURN_OK; -} - -std::string -XmppEngineImpl::NextId() { - std::stringstream ss; - ss << next_id_++; - return ss.str(); -} - -XmppReturnStatus -XmppEngineImpl::Disconnect() { - - if (state_ != STATE_CLOSED) { - EnterExit ee(this); - if (state_ == STATE_OPEN) - *output_ << ""; - state_ = STATE_CLOSED; - } - - return XMPP_RETURN_OK; -} - -void -XmppEngineImpl::IncomingStart(const XmlElement * pelStart) { - if (HasError() || raised_reset_) - return; - - if (login_task_.get()) { - // start-stream should go to login task - login_task_->IncomingStanza(pelStart, true); - if (login_task_->IsDone()) - login_task_.reset(); - } - else { - // if not logging in, it's an error to see a start - SignalError(ERROR_XML); - } -} - -void -XmppEngineImpl::IncomingStanza(const XmlElement * stanza) { - if (HasError() || raised_reset_) - return; - - if (stanza->Name() == TQN_STREAM_ERROR) { - // Explicit XMPP stream error - SignalStreamError(stanza); - } else if (login_task_.get()) { - // Handle login handshake - login_task_->IncomingStanza(stanza, false); - if (login_task_->IsDone()) - login_task_.reset(); - } else if (HandleIqResponse(stanza)) { - // iq is handled by above call - } else { - // give every "peek" handler a shot at all stanzas - for (size_t i = 0; i < stanza_handlers_[HL_PEEK]->size(); i += 1) { - (*stanza_handlers_[HL_PEEK])[i]->HandleStanza(stanza); - } - - // give other handlers a shot in precedence order, stopping after handled - for (int level = HL_SINGLE; level <= HL_ALL; level += 1) { - for (size_t i = 0; i < stanza_handlers_[level]->size(); i += 1) { - if ((*stanza_handlers_[level])[i]->HandleStanza(stanza)) - goto Handled; - } - } - - // If nobody wants to handle a stanza then send back an error. - // Only do this for IQ stanzas as messages should probably just be dropped - // and presence stanzas should certainly be dropped. - std::string type = stanza->Attr(TQN_TYPE); - if (stanza->Name() == TQN_IQ && - !(type == "error" || type == "result")) { - SendStanzaError(stanza, XSE_FEATURE_NOT_IMPLEMENTED, STR_EMPTY); - } - } - Handled: - ; // handled - we're done -} - -void -XmppEngineImpl::IncomingEnd(bool isError) { - if (HasError() || raised_reset_) - return; - - SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED); -} - -void -XmppEngineImpl::InternalSendStart(const std::string & to) { - // send stream-beginning - // note, we put a \r\n at tne end fo the first line to cause non-XMPP - // line-oriented servers (e.g., Apache) to reveal themselves more quickly. - *output_ << "\r\n"; -} - -void -XmppEngineImpl::InternalSendStanza(const XmlElement * element) { - // It should really never be necessary to set a FROM attribute on a stanza. - // It is implied by the bind on the stream and if you get it wrong - // (by flipping from/to on a message?) the server will close the stream. - ASSERT(!element->HasAttr(TQN_FROM)); - - // TODO: consider caching the XmlPrinter - XmlPrinter::PrintXml(output_.get(), element, - XMPP_CLIENT_NAMESPACES, XMPP_CLIENT_NAMESPACES_LEN); -} - -std::string -XmppEngineImpl::ChooseBestSaslMechanism(const std::vector & mechanisms, bool encrypted) { - return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted); -} - -SaslMechanism * -XmppEngineImpl::GetSaslMechanism(const std::string & name) { - return sasl_handler_->CreateSaslMechanism(name); -} - -void -XmppEngineImpl::SignalBound(const Jid & fullJid) { - if (state_ == STATE_OPENING) { - bound_jid_ = fullJid; - state_ = STATE_OPEN; - } -} - -void -XmppEngineImpl::SignalStreamError(const XmlElement * pelStreamError) { - if (state_ != STATE_CLOSED) { - stream_error_.reset(new XmlElement(*pelStreamError)); - SignalError(ERROR_STREAM); - } -} - -void -XmppEngineImpl::SignalError(Error errorCode) { - if (state_ != STATE_CLOSED) { - error_code_ = errorCode; - state_ = STATE_CLOSED; - } -} - -bool -XmppEngineImpl::HasError() { - return error_code_ != ERROR_NONE; -} - -void -XmppEngineImpl::StartTls(const std::string & domain) { - if (output_handler_) { - output_handler_->StartTls( - tls_server_domain_.empty() ? domain : tls_server_domain_); - encrypted_ = true; - } -} - -XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) - : engine_(engine), - state_(engine->state_), - error_(engine->error_code_) { - engine->engine_entered_ += 1; -} - -XmppEngineImpl::EnterExit::~EnterExit() { - XmppEngineImpl* engine = engine_; - - engine->engine_entered_ -= 1; - - bool closing = (engine->state_ != state_ && - engine->state_ == STATE_CLOSED); - bool flushing = closing || (engine->engine_entered_ == 0); - - if (engine->output_handler_ && flushing) { - std::string output = engine->output_->str(); - if (output.length() > 0) - engine->output_handler_->WriteOutput(output.c_str(), output.length()); - engine->output_->str(""); - - if (closing) { - engine->output_handler_->CloseConnection(); - engine->output_handler_ = 0; - } - } - - if (engine->engine_entered_) - return; - - if (engine->raised_reset_) { - engine->stanzaParser_.Reset(); - engine->raised_reset_ = false; - } - - if (engine->session_handler_) { - if (engine->state_ != state_) - engine->session_handler_->OnStateChange(engine->state_); - // Note: Handling of OnStateChange(CLOSED) should allow for the - // deletion of the engine, so no members should be accessed - // after this line. - } -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cpp new file mode 100644 index 00000000..3d363a78 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cpp @@ -0,0 +1,480 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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. + */ + +#define TRACK_ARRAY_ALLOC_PROBLEM + +#include +#include +#include +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmpp/xmppengineimpl.h" +#include "talk/xmpp/xmpplogintask.h" +#include "talk/xmpp/constants.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmpp/saslhandler.h" +// #include "buzz/saslmechanism.h" + +#define new TRACK_NEW + +namespace buzz { + +static const std::string XMPP_CLIENT_NAMESPACES[] = { + "stream", "http://etherx.jabber.org/streams", + "", "jabber:client", +}; + +static const size_t XMPP_CLIENT_NAMESPACES_LEN = 4; + +XmppEngine * XmppEngine::Create() { + return new XmppEngineImpl(); +} + + +XmppEngineImpl::XmppEngineImpl() : + stanzaParseHandler_(this), + stanzaParser_(&stanzaParseHandler_), + engine_entered_(0), + user_jid_(JID_EMPTY), + password_(), + requested_resource_(STR_EMPTY), + tls_needed_(true), + login_task_(new XmppLoginTask(this)), + next_id_(0), + bound_jid_(JID_EMPTY), + state_(STATE_START), + encrypted_(false), + error_code_(ERROR_NONE), + stream_error_(NULL), + raised_reset_(false), + output_handler_(NULL), + session_handler_(NULL), + iq_entries_(new IqEntryVector()), + output_(new std::stringstream()), + sasl_handler_(NULL) { + for (int i = 0; i < HL_COUNT; i+= 1) { + stanza_handlers_[i].reset(new StanzaHandlerVector()); + } +} + +XmppEngineImpl::~XmppEngineImpl() { + DeleteIqCookies(); +} + +XmppReturnStatus +XmppEngineImpl::SetOutputHandler(XmppOutputHandler* output_handler) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + output_handler_ = output_handler; + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetSessionHandler(XmppSessionHandler* session_handler) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + session_handler_ = session_handler; + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::HandleInput(const char * bytes, size_t len) { + if (state_ < STATE_OPENING || state_ > STATE_OPEN) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + stanzaParser_.Parse(bytes, len, false); + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::ConnectionClosed() { + if (state_ != STATE_CLOSED) { + EnterExit ee(this); + // If told that connection closed and not already closed, + // then connection was unpexectedly dropped. + SignalError(ERROR_CONNECTION_CLOSED); + } + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetUseTls(bool useTls) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + tls_needed_ = useTls; + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetTlsServerDomain(const std::string & tls_server_domain) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + tls_server_domain_= tls_server_domain; + + return XMPP_RETURN_OK; +} + +bool +XmppEngineImpl::GetUseTls() { + return tls_needed_; +} + +XmppReturnStatus +XmppEngineImpl::SetUser(const Jid & jid) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + user_jid_ = jid; + + return XMPP_RETURN_OK; +} + +const Jid & +XmppEngineImpl::GetUser() { + return user_jid_; +} + +XmppReturnStatus +XmppEngineImpl::SetSaslHandler(SaslHandler * sasl_handler) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + sasl_handler_.reset(sasl_handler); + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SetRequestedResource(const std::string & resource) { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + requested_resource_ = resource; + + return XMPP_RETURN_OK; +} + +const std::string & +XmppEngineImpl::GetRequestedResource() { + return requested_resource_; +} + +XmppReturnStatus +XmppEngineImpl::AddStanzaHandler(XmppStanzaHandler * stanza_handler, + XmppEngine::HandlerLevel level) { + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + + stanza_handlers_[level]->push_back(stanza_handler); + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::RemoveStanzaHandler(XmppStanzaHandler * stanza_handler) { + + bool found = false; + + for (int level = 0; level < HL_COUNT; level += 1) { + StanzaHandlerVector::iterator new_end = + std::remove(stanza_handlers_[level]->begin(), + stanza_handlers_[level]->end(), + stanza_handler); + + if (new_end != stanza_handlers_[level]->end()) { + stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end()); + found = true; + } + } + + if (!found) { + return XMPP_RETURN_BADARGUMENT; + } + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::Connect() { + if (state_ != STATE_START) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + // get the login task started + state_ = STATE_OPENING; + if (login_task_.get()) { + login_task_->IncomingStanza(NULL, false); + if (login_task_->IsDone()) + login_task_.reset(); + } + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SendStanza(const XmlElement * element) { + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + if (login_task_.get()) { + // still handshaking - then outbound stanzas are queued + login_task_->OutgoingStanza(element); + } else { + // handshake done - send straight through + InternalSendStanza(element); + } + + return XMPP_RETURN_OK; +} + +XmppReturnStatus +XmppEngineImpl::SendRaw(const std::string & text) { + if (state_ == STATE_CLOSED || login_task_.get()) + return XMPP_RETURN_BADSTATE; + + EnterExit ee(this); + + (*output_) << text; + + return XMPP_RETURN_OK; +} + +std::string +XmppEngineImpl::NextId() { + std::stringstream ss; + ss << next_id_++; + return ss.str(); +} + +XmppReturnStatus +XmppEngineImpl::Disconnect() { + + if (state_ != STATE_CLOSED) { + EnterExit ee(this); + if (state_ == STATE_OPEN) + *output_ << ""; + state_ = STATE_CLOSED; + } + + return XMPP_RETURN_OK; +} + +void +XmppEngineImpl::IncomingStart(const XmlElement * pelStart) { + if (HasError() || raised_reset_) + return; + + if (login_task_.get()) { + // start-stream should go to login task + login_task_->IncomingStanza(pelStart, true); + if (login_task_->IsDone()) + login_task_.reset(); + } + else { + // if not logging in, it's an error to see a start + SignalError(ERROR_XML); + } +} + +void +XmppEngineImpl::IncomingStanza(const XmlElement * stanza) { + if (HasError() || raised_reset_) + return; + + if (stanza->Name() == TQN_STREAM_ERROR) { + // Explicit XMPP stream error + SignalStreamError(stanza); + } else if (login_task_.get()) { + // Handle login handshake + login_task_->IncomingStanza(stanza, false); + if (login_task_->IsDone()) + login_task_.reset(); + } else if (HandleIqResponse(stanza)) { + // iq is handled by above call + } else { + // give every "peek" handler a shot at all stanzas + for (size_t i = 0; i < stanza_handlers_[HL_PEEK]->size(); i += 1) { + (*stanza_handlers_[HL_PEEK])[i]->HandleStanza(stanza); + } + + // give other handlers a shot in precedence order, stopping after handled + for (int level = HL_SINGLE; level <= HL_ALL; level += 1) { + for (size_t i = 0; i < stanza_handlers_[level]->size(); i += 1) { + if ((*stanza_handlers_[level])[i]->HandleStanza(stanza)) + goto Handled; + } + } + + // If nobody wants to handle a stanza then send back an error. + // Only do this for IQ stanzas as messages should probably just be dropped + // and presence stanzas should certainly be dropped. + std::string type = stanza->Attr(TQN_TYPE); + if (stanza->Name() == TQN_IQ && + !(type == "error" || type == "result")) { + SendStanzaError(stanza, XSE_FEATURE_NOT_IMPLEMENTED, STR_EMPTY); + } + } + Handled: + ; // handled - we're done +} + +void +XmppEngineImpl::IncomingEnd(bool isError) { + if (HasError() || raised_reset_) + return; + + SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED); +} + +void +XmppEngineImpl::InternalSendStart(const std::string & to) { + // send stream-beginning + // note, we put a \r\n at tne end fo the first line to cause non-XMPP + // line-oriented servers (e.g., Apache) to reveal themselves more quickly. + *output_ << "\r\n"; +} + +void +XmppEngineImpl::InternalSendStanza(const XmlElement * element) { + // It should really never be necessary to set a FROM attribute on a stanza. + // It is implied by the bind on the stream and if you get it wrong + // (by flipping from/to on a message?) the server will close the stream. + ASSERT(!element->HasAttr(TQN_FROM)); + + // TODO: consider caching the XmlPrinter + XmlPrinter::PrintXml(output_.get(), element, + XMPP_CLIENT_NAMESPACES, XMPP_CLIENT_NAMESPACES_LEN); +} + +std::string +XmppEngineImpl::ChooseBestSaslMechanism(const std::vector & mechanisms, bool encrypted) { + return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted); +} + +SaslMechanism * +XmppEngineImpl::GetSaslMechanism(const std::string & name) { + return sasl_handler_->CreateSaslMechanism(name); +} + +void +XmppEngineImpl::SignalBound(const Jid & fullJid) { + if (state_ == STATE_OPENING) { + bound_jid_ = fullJid; + state_ = STATE_OPEN; + } +} + +void +XmppEngineImpl::SignalStreamError(const XmlElement * pelStreamError) { + if (state_ != STATE_CLOSED) { + stream_error_.reset(new XmlElement(*pelStreamError)); + SignalError(ERROR_STREAM); + } +} + +void +XmppEngineImpl::SignalError(Error errorCode) { + if (state_ != STATE_CLOSED) { + error_code_ = errorCode; + state_ = STATE_CLOSED; + } +} + +bool +XmppEngineImpl::HasError() { + return error_code_ != ERROR_NONE; +} + +void +XmppEngineImpl::StartTls(const std::string & domain) { + if (output_handler_) { + output_handler_->StartTls( + tls_server_domain_.empty() ? domain : tls_server_domain_); + encrypted_ = true; + } +} + +XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) + : engine_(engine), + state_(engine->state_), + error_(engine->error_code_) { + engine->engine_entered_ += 1; +} + +XmppEngineImpl::EnterExit::~EnterExit() { + XmppEngineImpl* engine = engine_; + + engine->engine_entered_ -= 1; + + bool closing = (engine->state_ != state_ && + engine->state_ == STATE_CLOSED); + bool flushing = closing || (engine->engine_entered_ == 0); + + if (engine->output_handler_ && flushing) { + std::string output = engine->output_->str(); + if (output.length() > 0) + engine->output_handler_->WriteOutput(output.c_str(), output.length()); + engine->output_->str(""); + + if (closing) { + engine->output_handler_->CloseConnection(); + engine->output_handler_ = 0; + } + } + + if (engine->engine_entered_) + return; + + if (engine->raised_reset_) { + engine->stanzaParser_.Reset(); + engine->raised_reset_ = false; + } + + if (engine->session_handler_) { + if (engine->state_ != state_) + engine->session_handler_->OnStateChange(engine->state_); + // Note: Handling of OnStateChange(CLOSED) should allow for the + // deletion of the engine, so no members should be accessed + // after this line. + } +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc deleted file mode 100644 index 38304048..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc +++ /dev/null @@ -1,279 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include -#include "talk/base/common.h" -#include "talk/xmpp/xmppengineimpl.h" -#include "talk/xmpp/constants.h" - -#define new TRACK_NEW - -namespace buzz { - -class XmppIqEntry { - XmppIqEntry(const std::string & id, const std::string & to, - XmppEngine * pxce, XmppIqHandler * iq_handler) : - id_(id), - to_(to), - engine_(pxce), - iq_handler_(iq_handler) { - } - -private: - friend class XmppEngineImpl; - - const std::string id_; - const std::string to_; - XmppEngine * const engine_; - XmppIqHandler * const iq_handler_; -}; - - -XmppReturnStatus -XmppEngineImpl::SendIq(const XmlElement * element, XmppIqHandler * iq_handler, - XmppIqCookie* cookie) { - if (state_ == STATE_CLOSED) - return XMPP_RETURN_BADSTATE; - if (NULL == iq_handler) - return XMPP_RETURN_BADARGUMENT; - if (!element || element->Name() != TQN_IQ) - return XMPP_RETURN_BADARGUMENT; - - const std::string& type = element->Attr(TQN_TYPE); - if (type != "get" && type != "set") - return XMPP_RETURN_BADARGUMENT; - - if (!element->HasAttr(TQN_ID)) - return XMPP_RETURN_BADARGUMENT; - const std::string& id = element->Attr(TQN_ID); - - XmppIqEntry * iq_entry = new XmppIqEntry(id, - element->Attr(TQN_TO), - this, iq_handler); - iq_entries_->push_back(iq_entry); - SendStanza(element); - - if (cookie) - *cookie = iq_entry; - - return XMPP_RETURN_OK; -} - - -XmppReturnStatus -XmppEngineImpl::RemoveIqHandler(XmppIqCookie cookie, - XmppIqHandler ** iq_handler) { - - std::vector >::iterator pos; - - pos = std::find(iq_entries_->begin(), - iq_entries_->end(), - reinterpret_cast(cookie)); - - if (pos == iq_entries_->end()) - return XMPP_RETURN_BADARGUMENT; - - XmppIqEntry* entry = *pos; - iq_entries_->erase(pos); - if (iq_handler) - *iq_handler = entry->iq_handler_; - delete entry; - - return XMPP_RETURN_OK; -} - -void -XmppEngineImpl::DeleteIqCookies() { - for (size_t i = 0; i < iq_entries_->size(); i += 1) { - XmppIqEntry * iq_entry_ = (*iq_entries_)[i]; - (*iq_entries_)[i] = NULL; - delete iq_entry_; - } - iq_entries_->clear(); -} - -static void -AecImpl(XmlElement * error_element, const TQName & name, - const char * type, const char * code) { - error_element->AddElement(new XmlElement(TQN_ERROR)); - error_element->AddAttr(TQN_CODE, code, 1); - error_element->AddAttr(TQN_TYPE, type, 1); - error_element->AddElement(new XmlElement(name, true), 1); -} - - -static void -AddErrorCode(XmlElement * error_element, XmppStanzaError code) { - switch (code) { - case XSE_BAD_REQUEST: - AecImpl(error_element, TQN_STANZA_BAD_REQUEST, "modify", "400"); - break; - case XSE_CONFLICT: - AecImpl(error_element, TQN_STANZA_CONFLICT, "cancel", "409"); - break; - case XSE_FEATURE_NOT_IMPLEMENTED: - AecImpl(error_element, TQN_STANZA_FEATURE_NOT_IMPLEMENTED, - "cancel", "501"); - break; - case XSE_FORBIDDEN: - AecImpl(error_element, TQN_STANZA_FORBIDDEN, "auth", "403"); - break; - case XSE_GONE: - AecImpl(error_element, TQN_STANZA_GONE, "modify", "302"); - break; - case XSE_INTERNAL_SERVER_ERROR: - AecImpl(error_element, TQN_STANZA_INTERNAL_SERVER_ERROR, "wait", "500"); - break; - case XSE_ITEM_NOT_FOUND: - AecImpl(error_element, TQN_STANZA_ITEM_NOT_FOUND, "cancel", "404"); - break; - case XSE_JID_MALFORMED: - AecImpl(error_element, TQN_STANZA_JID_MALFORMED, "modify", "400"); - break; - case XSE_NOT_ACCEPTABLE: - AecImpl(error_element, TQN_STANZA_NOT_ACCEPTABLE, "cancel", "406"); - break; - case XSE_NOT_ALLOWED: - AecImpl(error_element, TQN_STANZA_NOT_ALLOWED, "cancel", "405"); - break; - case XSE_PAYMENT_REQUIRED: - AecImpl(error_element, TQN_STANZA_PAYMENT_REQUIRED, "auth", "402"); - break; - case XSE_RECIPIENT_UNAVAILABLE: - AecImpl(error_element, TQN_STANZA_RECIPIENT_UNAVAILABLE, "wait", "404"); - break; - case XSE_REDIRECT: - AecImpl(error_element, TQN_STANZA_REDIRECT, "modify", "302"); - break; - case XSE_REGISTRATION_REQUIRED: - AecImpl(error_element, TQN_STANZA_REGISTRATION_REQUIRED, "auth", "407"); - break; - case XSE_SERVER_NOT_FOUND: - AecImpl(error_element, TQN_STANZA_REMOTE_SERVER_NOT_FOUND, - "cancel", "404"); - break; - case XSE_SERVER_TIMEOUT: - AecImpl(error_element, TQN_STANZA_REMOTE_SERVER_TIMEOUT, "wait", "502"); - break; - case XSE_RESOURCE_CONSTRAINT: - AecImpl(error_element, TQN_STANZA_RESOURCE_CONSTRAINT, "wait", "500"); - break; - case XSE_SERVICE_UNAVAILABLE: - AecImpl(error_element, TQN_STANZA_SERVICE_UNAVAILABLE, "cancel", "503"); - break; - case XSE_SUBSCRIPTION_REQUIRED: - AecImpl(error_element, TQN_STANZA_SUBSCRIPTION_REQUIRED, "auth", "407"); - break; - case XSE_UNDEFINED_CONDITION: - AecImpl(error_element, TQN_STANZA_UNDEFINED_CONDITION, "wait", "500"); - break; - case XSE_UNEXPECTED_REQUEST: - AecImpl(error_element, TQN_STANZA_UNEXPECTED_REQUEST, "wait", "400"); - break; - } -} - - -XmppReturnStatus -XmppEngineImpl::SendStanzaError(const XmlElement * element_original, - XmppStanzaError code, - const std::string & text) { - - if (state_ == STATE_CLOSED) - return XMPP_RETURN_BADSTATE; - - XmlElement error_element(element_original->Name()); - error_element.AddAttr(TQN_TYPE, "error"); - - // copy attrs, copy 'from' to 'to' and strip 'from' - for (const XmlAttr * attribute = element_original->FirstAttr(); - attribute; attribute = attribute->NextAttr()) { - TQName name = attribute->Name(); - if (name == TQN_TO) - continue; // no need to put a from attr. Server will stamp stanza - else if (name == TQN_FROM) - name = TQN_TO; - else if (name == TQN_TYPE) - continue; - error_element.AddAttr(name, attribute->Value()); - } - - // copy children - for (const XmlChild * child = element_original->FirstChild(); - child; - child = child->NextChild()) { - if (child->IsText()) { - error_element.AddText(child->AsText()->Text()); - } else { - error_element.AddElement(new XmlElement(*(child->AsElement()))); - } - } - - // add error information - AddErrorCode(&error_element, code); - if (text != STR_EMPTY) { - XmlElement * text_element = new XmlElement(TQN_STANZA_TEXT, true); - text_element->AddText(text); - error_element.AddElement(text_element); - } - - SendStanza(&error_element); - - return XMPP_RETURN_OK; -} - - -bool -XmppEngineImpl::HandleIqResponse(const XmlElement * element) { - if (iq_entries_->empty()) - return false; - if (element->Name() != TQN_IQ) - return false; - std::string type = element->Attr(TQN_TYPE); - if (type != "result" && type != "error") - return false; - if (!element->HasAttr(TQN_ID)) - return false; - std::string id = element->Attr(TQN_ID); - std::string from = element->Attr(TQN_FROM); - - for (std::vector::iterator it = iq_entries_->begin(); - it != iq_entries_->end(); it += 1) { - XmppIqEntry * iq_entry = *it; - if (iq_entry->id_ == id && iq_entry->to_ == from) { - iq_entries_->erase(it); - iq_entry->iq_handler_->IqResponse(iq_entry, element); - delete iq_entry; - return true; - } - } - - return false; -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cpp new file mode 100644 index 00000000..38304048 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cpp @@ -0,0 +1,279 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include +#include "talk/base/common.h" +#include "talk/xmpp/xmppengineimpl.h" +#include "talk/xmpp/constants.h" + +#define new TRACK_NEW + +namespace buzz { + +class XmppIqEntry { + XmppIqEntry(const std::string & id, const std::string & to, + XmppEngine * pxce, XmppIqHandler * iq_handler) : + id_(id), + to_(to), + engine_(pxce), + iq_handler_(iq_handler) { + } + +private: + friend class XmppEngineImpl; + + const std::string id_; + const std::string to_; + XmppEngine * const engine_; + XmppIqHandler * const iq_handler_; +}; + + +XmppReturnStatus +XmppEngineImpl::SendIq(const XmlElement * element, XmppIqHandler * iq_handler, + XmppIqCookie* cookie) { + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + if (NULL == iq_handler) + return XMPP_RETURN_BADARGUMENT; + if (!element || element->Name() != TQN_IQ) + return XMPP_RETURN_BADARGUMENT; + + const std::string& type = element->Attr(TQN_TYPE); + if (type != "get" && type != "set") + return XMPP_RETURN_BADARGUMENT; + + if (!element->HasAttr(TQN_ID)) + return XMPP_RETURN_BADARGUMENT; + const std::string& id = element->Attr(TQN_ID); + + XmppIqEntry * iq_entry = new XmppIqEntry(id, + element->Attr(TQN_TO), + this, iq_handler); + iq_entries_->push_back(iq_entry); + SendStanza(element); + + if (cookie) + *cookie = iq_entry; + + return XMPP_RETURN_OK; +} + + +XmppReturnStatus +XmppEngineImpl::RemoveIqHandler(XmppIqCookie cookie, + XmppIqHandler ** iq_handler) { + + std::vector >::iterator pos; + + pos = std::find(iq_entries_->begin(), + iq_entries_->end(), + reinterpret_cast(cookie)); + + if (pos == iq_entries_->end()) + return XMPP_RETURN_BADARGUMENT; + + XmppIqEntry* entry = *pos; + iq_entries_->erase(pos); + if (iq_handler) + *iq_handler = entry->iq_handler_; + delete entry; + + return XMPP_RETURN_OK; +} + +void +XmppEngineImpl::DeleteIqCookies() { + for (size_t i = 0; i < iq_entries_->size(); i += 1) { + XmppIqEntry * iq_entry_ = (*iq_entries_)[i]; + (*iq_entries_)[i] = NULL; + delete iq_entry_; + } + iq_entries_->clear(); +} + +static void +AecImpl(XmlElement * error_element, const TQName & name, + const char * type, const char * code) { + error_element->AddElement(new XmlElement(TQN_ERROR)); + error_element->AddAttr(TQN_CODE, code, 1); + error_element->AddAttr(TQN_TYPE, type, 1); + error_element->AddElement(new XmlElement(name, true), 1); +} + + +static void +AddErrorCode(XmlElement * error_element, XmppStanzaError code) { + switch (code) { + case XSE_BAD_REQUEST: + AecImpl(error_element, TQN_STANZA_BAD_REQUEST, "modify", "400"); + break; + case XSE_CONFLICT: + AecImpl(error_element, TQN_STANZA_CONFLICT, "cancel", "409"); + break; + case XSE_FEATURE_NOT_IMPLEMENTED: + AecImpl(error_element, TQN_STANZA_FEATURE_NOT_IMPLEMENTED, + "cancel", "501"); + break; + case XSE_FORBIDDEN: + AecImpl(error_element, TQN_STANZA_FORBIDDEN, "auth", "403"); + break; + case XSE_GONE: + AecImpl(error_element, TQN_STANZA_GONE, "modify", "302"); + break; + case XSE_INTERNAL_SERVER_ERROR: + AecImpl(error_element, TQN_STANZA_INTERNAL_SERVER_ERROR, "wait", "500"); + break; + case XSE_ITEM_NOT_FOUND: + AecImpl(error_element, TQN_STANZA_ITEM_NOT_FOUND, "cancel", "404"); + break; + case XSE_JID_MALFORMED: + AecImpl(error_element, TQN_STANZA_JID_MALFORMED, "modify", "400"); + break; + case XSE_NOT_ACCEPTABLE: + AecImpl(error_element, TQN_STANZA_NOT_ACCEPTABLE, "cancel", "406"); + break; + case XSE_NOT_ALLOWED: + AecImpl(error_element, TQN_STANZA_NOT_ALLOWED, "cancel", "405"); + break; + case XSE_PAYMENT_REQUIRED: + AecImpl(error_element, TQN_STANZA_PAYMENT_REQUIRED, "auth", "402"); + break; + case XSE_RECIPIENT_UNAVAILABLE: + AecImpl(error_element, TQN_STANZA_RECIPIENT_UNAVAILABLE, "wait", "404"); + break; + case XSE_REDIRECT: + AecImpl(error_element, TQN_STANZA_REDIRECT, "modify", "302"); + break; + case XSE_REGISTRATION_REQUIRED: + AecImpl(error_element, TQN_STANZA_REGISTRATION_REQUIRED, "auth", "407"); + break; + case XSE_SERVER_NOT_FOUND: + AecImpl(error_element, TQN_STANZA_REMOTE_SERVER_NOT_FOUND, + "cancel", "404"); + break; + case XSE_SERVER_TIMEOUT: + AecImpl(error_element, TQN_STANZA_REMOTE_SERVER_TIMEOUT, "wait", "502"); + break; + case XSE_RESOURCE_CONSTRAINT: + AecImpl(error_element, TQN_STANZA_RESOURCE_CONSTRAINT, "wait", "500"); + break; + case XSE_SERVICE_UNAVAILABLE: + AecImpl(error_element, TQN_STANZA_SERVICE_UNAVAILABLE, "cancel", "503"); + break; + case XSE_SUBSCRIPTION_REQUIRED: + AecImpl(error_element, TQN_STANZA_SUBSCRIPTION_REQUIRED, "auth", "407"); + break; + case XSE_UNDEFINED_CONDITION: + AecImpl(error_element, TQN_STANZA_UNDEFINED_CONDITION, "wait", "500"); + break; + case XSE_UNEXPECTED_REQUEST: + AecImpl(error_element, TQN_STANZA_UNEXPECTED_REQUEST, "wait", "400"); + break; + } +} + + +XmppReturnStatus +XmppEngineImpl::SendStanzaError(const XmlElement * element_original, + XmppStanzaError code, + const std::string & text) { + + if (state_ == STATE_CLOSED) + return XMPP_RETURN_BADSTATE; + + XmlElement error_element(element_original->Name()); + error_element.AddAttr(TQN_TYPE, "error"); + + // copy attrs, copy 'from' to 'to' and strip 'from' + for (const XmlAttr * attribute = element_original->FirstAttr(); + attribute; attribute = attribute->NextAttr()) { + TQName name = attribute->Name(); + if (name == TQN_TO) + continue; // no need to put a from attr. Server will stamp stanza + else if (name == TQN_FROM) + name = TQN_TO; + else if (name == TQN_TYPE) + continue; + error_element.AddAttr(name, attribute->Value()); + } + + // copy children + for (const XmlChild * child = element_original->FirstChild(); + child; + child = child->NextChild()) { + if (child->IsText()) { + error_element.AddText(child->AsText()->Text()); + } else { + error_element.AddElement(new XmlElement(*(child->AsElement()))); + } + } + + // add error information + AddErrorCode(&error_element, code); + if (text != STR_EMPTY) { + XmlElement * text_element = new XmlElement(TQN_STANZA_TEXT, true); + text_element->AddText(text); + error_element.AddElement(text_element); + } + + SendStanza(&error_element); + + return XMPP_RETURN_OK; +} + + +bool +XmppEngineImpl::HandleIqResponse(const XmlElement * element) { + if (iq_entries_->empty()) + return false; + if (element->Name() != TQN_IQ) + return false; + std::string type = element->Attr(TQN_TYPE); + if (type != "result" && type != "error") + return false; + if (!element->HasAttr(TQN_ID)) + return false; + std::string id = element->Attr(TQN_ID); + std::string from = element->Attr(TQN_FROM); + + for (std::vector::iterator it = iq_entries_->begin(); + it != iq_entries_->end(); it += 1) { + XmppIqEntry * iq_entry = *it; + if (iq_entry->id_ == id && iq_entry->to_ == from) { + iq_entries_->erase(it); + iq_entry->iq_handler_->IqResponse(iq_entry, element); + delete iq_entry; + return true; + } + } + + return false; +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc deleted file mode 100644 index 9b3fe9bc..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc +++ /dev/null @@ -1,357 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include -#include -#include "talk/xmpp/jid.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/base/common.h" -#include "talk/xmpp/xmppengineimpl.h" -#include "talk/xmpp/constants.h" -#include "talk/base/base64.h" -#include "talk/xmpp/xmpplogintask.h" -#include "talk/xmpp/saslmechanism.h" - -#define new TRACK_NEW - -namespace buzz { - -XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) : - pctx_(pctx), - authNeeded_(true), - state_(LOGINSTATE_INIT), - pelStanza_(NULL), - isStart_(false), - iqId_(STR_EMPTY), - pelFeatures_(NULL), - fullJid_(STR_EMPTY), - streamId_(STR_EMPTY), - pvecQueuedStanzas_(new std::vector()), - sasl_mech_(NULL) { -} - -XmppLoginTask::~XmppLoginTask() { - for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) - delete (*pvecQueuedStanzas_)[i]; -} - -void -XmppLoginTask::IncomingStanza(const XmlElement *element, bool isStart) { - pelStanza_ = element; - isStart_ = isStart; - Advance(); - pelStanza_ = NULL; - isStart_ = false; -} - -const XmlElement * -XmppLoginTask::NextStanza() { - const XmlElement * result = pelStanza_; - pelStanza_ = NULL; - return result; -} - -bool -XmppLoginTask::Advance() { - - for (;;) { - - const XmlElement * element = NULL; - - switch (state_) { - - case LOGINSTATE_INIT: { - pctx_->RaiseReset(); - pelFeatures_.reset(NULL); - - pctx_->InternalSendStart(pctx_->user_jid_.domain()); - state_ = LOGINSTATE_STREAMSTART_SENT; - break; - } - - case LOGINSTATE_STREAMSTART_SENT: { - if (NULL == (element = NextStanza())) - return true; - - if (!isStart_ || !HandleStartStream(element)) - return Failure(XmppEngine::ERROR_VERSION); - - state_ = LOGINSTATE_STARTED_XMPP; - return true; - } - - case LOGINSTATE_STARTED_XMPP: { - if (NULL == (element = NextStanza())) - return true; - - if (!HandleFeatures(element)) - return Failure(XmppEngine::ERROR_VERSION); - - if (pctx_->tls_needed_) { - state_ = LOGINSTATE_TLS_INIT; - continue; - } - - if (authNeeded_) { - state_ = LOGINSTATE_AUTH_INIT; - continue; - } - - state_ = LOGINSTATE_BIND_INIT; - continue; - } - - case LOGINSTATE_TLS_INIT: { - const XmlElement * pelTls = GetFeature(TQN_TLS_STARTTLS); - if (!pelTls) - return Failure(XmppEngine::ERROR_TLS); - - XmlElement el(TQN_TLS_STARTTLS, true); - pctx_->InternalSendStanza(&el); - state_ = LOGINSTATE_TLS_REQUESTED; - continue; - } - - case LOGINSTATE_TLS_REQUESTED: { - if (NULL == (element = NextStanza())) - return true; - if (element->Name() != TQN_TLS_PROCEED) - return Failure(XmppEngine::ERROR_TLS); - - // The proper domain to verify against is the real underlying - // domain - i.e., the domain that owns the JID. Our XmppEngineImpl - // also allows matching against a proxy domain instead, if it is told - // to do so - see the implementation of XmppEngineImpl::StartTls and - // XmppEngine::SetTlsServerDomain to see how you can use that feature - pctx_->StartTls(pctx_->user_jid_.domain()); - pctx_->tls_needed_ = false; - state_ = LOGINSTATE_INIT; - continue; - } - - case LOGINSTATE_AUTH_INIT: { - const XmlElement * pelSaslAuth = GetFeature(TQN_SASL_MECHANISMS); - if (!pelSaslAuth) { - return Failure(XmppEngine::ERROR_AUTH); - } - - // Collect together the SASL auth mechanisms presented by the server - std::vector mechanisms; - for (const XmlElement * pelMech = - pelSaslAuth->FirstNamed(TQN_SASL_MECHANISM); - pelMech; - pelMech = pelMech->NextNamed(TQN_SASL_MECHANISM)) { - - mechanisms.push_back(pelMech->BodyText()); - } - - // Given all the mechanisms, choose the best - std::string choice(pctx_->ChooseBestSaslMechanism(mechanisms, pctx_->IsEncrypted())); - if (choice.empty()) { - return Failure(XmppEngine::ERROR_AUTH); - } - - // No recognized auth mechanism - that's an error - sasl_mech_.reset(pctx_->GetSaslMechanism(choice)); - if (sasl_mech_.get() == NULL) { - return Failure(XmppEngine::ERROR_AUTH); - } - - // OK, let's start it. - XmlElement * auth = sasl_mech_->StartSaslAuth(); - if (auth == NULL) { - return Failure(XmppEngine::ERROR_AUTH); - } - - pctx_->InternalSendStanza(auth); - delete auth; - state_ = LOGINSTATE_SASL_RUNNING; - continue; - } - - case LOGINSTATE_SASL_RUNNING: { - if (NULL == (element = NextStanza())) - return true; - if (element->Name().Namespace() != NS_SASL) - return Failure(XmppEngine::ERROR_AUTH); - if (element->Name() == TQN_SASL_CHALLENGE) { - XmlElement * response = sasl_mech_->HandleSaslChallenge(element); - if (response == NULL) { - return Failure(XmppEngine::ERROR_AUTH); - } - pctx_->InternalSendStanza(response); - delete response; - state_ = LOGINSTATE_SASL_RUNNING; - continue; - } - if (element->Name() != TQN_SASL_SUCCESS) { - return Failure(XmppEngine::ERROR_UNAUTHORIZED); - } - - // Authenticated! - authNeeded_ = false; - state_ = LOGINSTATE_INIT; - continue; - } - - case LOGINSTATE_BIND_INIT: { - const XmlElement * pelBindFeature = GetFeature(TQN_BIND_BIND); - const XmlElement * pelSessionFeature = GetFeature(TQN_SESSION_SESSION); - if (!pelBindFeature || !pelSessionFeature) - return Failure(XmppEngine::ERROR_BIND); - - XmlElement iq(TQN_IQ); - iq.AddAttr(TQN_TYPE, "set"); - - iqId_ = pctx_->NextId(); - iq.AddAttr(TQN_ID, iqId_); - iq.AddElement(new XmlElement(TQN_BIND_BIND, true)); - - if (pctx_->requested_resource_ != STR_EMPTY) { - iq.AddElement(new XmlElement(TQN_BIND_RESOURCE), 1); - iq.AddText(pctx_->requested_resource_, 2); - } - pctx_->InternalSendStanza(&iq); - state_ = LOGINSTATE_BIND_REQUESTED; - continue; - } - - case LOGINSTATE_BIND_REQUESTED: { - if (NULL == (element = NextStanza())) - return true; - - if (element->Name() != TQN_IQ || element->Attr(TQN_ID) != iqId_ || - element->Attr(TQN_TYPE) == "get" || element->Attr(TQN_TYPE) == "set") - return true; - - if (element->Attr(TQN_TYPE) != "result" || element->FirstElement() == NULL || - element->FirstElement()->Name() != TQN_BIND_BIND) - return Failure(XmppEngine::ERROR_BIND); - - fullJid_ = Jid(element->FirstElement()->TextNamed(TQN_BIND_JID)); - if (!fullJid_.IsFull()) { - return Failure(XmppEngine::ERROR_BIND); - } - - if (pctx_->user_jid_.domain() != STR_DEFAULT_DOMAIN && - fullJid_.BareJid() != pctx_->user_jid_) { - return Failure(XmppEngine::ERROR_BIND); - } - - // now request session - XmlElement iq(TQN_IQ); - iq.AddAttr(TQN_TYPE, "set"); - - iqId_ = pctx_->NextId(); - iq.AddAttr(TQN_ID, iqId_); - iq.AddElement(new XmlElement(TQN_SESSION_SESSION, true)); - pctx_->InternalSendStanza(&iq); - - state_ = LOGINSTATE_SESSION_REQUESTED; - continue; - } - - case LOGINSTATE_SESSION_REQUESTED: { - if (NULL == (element = NextStanza())) - return true; - if (element->Name() != TQN_IQ || element->Attr(TQN_ID) != iqId_ || - element->Attr(TQN_TYPE) == "get" || element->Attr(TQN_TYPE) == "set") - return false; - - if (element->Attr(TQN_TYPE) != "result") - return Failure(XmppEngine::ERROR_BIND); - - pctx_->SignalBound(fullJid_); - FlushQueuedStanzas(); - state_ = LOGINSTATE_DONE; - return true; - } - - case LOGINSTATE_DONE: - return false; - } - } -} - -bool -XmppLoginTask::HandleStartStream(const XmlElement *element) { - - if (element->Name() != TQN_STREAM_STREAM) - return false; - - if (element->Attr(TQN_XMLNS) != "jabber:client") - return false; - - if (element->Attr(TQN_VERSION) != "1.0") - return false; - - if (!element->HasAttr(TQN_ID)) - return false; - - streamId_ = element->Attr(TQN_ID); - - return true; -} - -bool -XmppLoginTask::HandleFeatures(const XmlElement *element) { - if (element->Name() != TQN_STREAM_FEATURES) - return false; - - pelFeatures_.reset(new XmlElement(*element)); - return true; -} - -const XmlElement * -XmppLoginTask::GetFeature(const TQName & name) { - return pelFeatures_->FirstNamed(name); -} - -bool -XmppLoginTask::Failure(XmppEngine::Error reason) { - state_ = LOGINSTATE_DONE; - pctx_->SignalError(reason); - return false; -} - -void -XmppLoginTask::OutgoingStanza(const XmlElement * element) { - XmlElement * pelCopy = new XmlElement(*element); - pvecQueuedStanzas_->push_back(pelCopy); -} - -void -XmppLoginTask::FlushQueuedStanzas() { - for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) { - pctx_->InternalSendStanza((*pvecQueuedStanzas_)[i]); - delete (*pvecQueuedStanzas_)[i]; - } - pvecQueuedStanzas_->clear(); -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cpp new file mode 100644 index 00000000..9b3fe9bc --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cpp @@ -0,0 +1,357 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include +#include +#include "talk/xmpp/jid.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmpp/xmppengineimpl.h" +#include "talk/xmpp/constants.h" +#include "talk/base/base64.h" +#include "talk/xmpp/xmpplogintask.h" +#include "talk/xmpp/saslmechanism.h" + +#define new TRACK_NEW + +namespace buzz { + +XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) : + pctx_(pctx), + authNeeded_(true), + state_(LOGINSTATE_INIT), + pelStanza_(NULL), + isStart_(false), + iqId_(STR_EMPTY), + pelFeatures_(NULL), + fullJid_(STR_EMPTY), + streamId_(STR_EMPTY), + pvecQueuedStanzas_(new std::vector()), + sasl_mech_(NULL) { +} + +XmppLoginTask::~XmppLoginTask() { + for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) + delete (*pvecQueuedStanzas_)[i]; +} + +void +XmppLoginTask::IncomingStanza(const XmlElement *element, bool isStart) { + pelStanza_ = element; + isStart_ = isStart; + Advance(); + pelStanza_ = NULL; + isStart_ = false; +} + +const XmlElement * +XmppLoginTask::NextStanza() { + const XmlElement * result = pelStanza_; + pelStanza_ = NULL; + return result; +} + +bool +XmppLoginTask::Advance() { + + for (;;) { + + const XmlElement * element = NULL; + + switch (state_) { + + case LOGINSTATE_INIT: { + pctx_->RaiseReset(); + pelFeatures_.reset(NULL); + + pctx_->InternalSendStart(pctx_->user_jid_.domain()); + state_ = LOGINSTATE_STREAMSTART_SENT; + break; + } + + case LOGINSTATE_STREAMSTART_SENT: { + if (NULL == (element = NextStanza())) + return true; + + if (!isStart_ || !HandleStartStream(element)) + return Failure(XmppEngine::ERROR_VERSION); + + state_ = LOGINSTATE_STARTED_XMPP; + return true; + } + + case LOGINSTATE_STARTED_XMPP: { + if (NULL == (element = NextStanza())) + return true; + + if (!HandleFeatures(element)) + return Failure(XmppEngine::ERROR_VERSION); + + if (pctx_->tls_needed_) { + state_ = LOGINSTATE_TLS_INIT; + continue; + } + + if (authNeeded_) { + state_ = LOGINSTATE_AUTH_INIT; + continue; + } + + state_ = LOGINSTATE_BIND_INIT; + continue; + } + + case LOGINSTATE_TLS_INIT: { + const XmlElement * pelTls = GetFeature(TQN_TLS_STARTTLS); + if (!pelTls) + return Failure(XmppEngine::ERROR_TLS); + + XmlElement el(TQN_TLS_STARTTLS, true); + pctx_->InternalSendStanza(&el); + state_ = LOGINSTATE_TLS_REQUESTED; + continue; + } + + case LOGINSTATE_TLS_REQUESTED: { + if (NULL == (element = NextStanza())) + return true; + if (element->Name() != TQN_TLS_PROCEED) + return Failure(XmppEngine::ERROR_TLS); + + // The proper domain to verify against is the real underlying + // domain - i.e., the domain that owns the JID. Our XmppEngineImpl + // also allows matching against a proxy domain instead, if it is told + // to do so - see the implementation of XmppEngineImpl::StartTls and + // XmppEngine::SetTlsServerDomain to see how you can use that feature + pctx_->StartTls(pctx_->user_jid_.domain()); + pctx_->tls_needed_ = false; + state_ = LOGINSTATE_INIT; + continue; + } + + case LOGINSTATE_AUTH_INIT: { + const XmlElement * pelSaslAuth = GetFeature(TQN_SASL_MECHANISMS); + if (!pelSaslAuth) { + return Failure(XmppEngine::ERROR_AUTH); + } + + // Collect together the SASL auth mechanisms presented by the server + std::vector mechanisms; + for (const XmlElement * pelMech = + pelSaslAuth->FirstNamed(TQN_SASL_MECHANISM); + pelMech; + pelMech = pelMech->NextNamed(TQN_SASL_MECHANISM)) { + + mechanisms.push_back(pelMech->BodyText()); + } + + // Given all the mechanisms, choose the best + std::string choice(pctx_->ChooseBestSaslMechanism(mechanisms, pctx_->IsEncrypted())); + if (choice.empty()) { + return Failure(XmppEngine::ERROR_AUTH); + } + + // No recognized auth mechanism - that's an error + sasl_mech_.reset(pctx_->GetSaslMechanism(choice)); + if (sasl_mech_.get() == NULL) { + return Failure(XmppEngine::ERROR_AUTH); + } + + // OK, let's start it. + XmlElement * auth = sasl_mech_->StartSaslAuth(); + if (auth == NULL) { + return Failure(XmppEngine::ERROR_AUTH); + } + + pctx_->InternalSendStanza(auth); + delete auth; + state_ = LOGINSTATE_SASL_RUNNING; + continue; + } + + case LOGINSTATE_SASL_RUNNING: { + if (NULL == (element = NextStanza())) + return true; + if (element->Name().Namespace() != NS_SASL) + return Failure(XmppEngine::ERROR_AUTH); + if (element->Name() == TQN_SASL_CHALLENGE) { + XmlElement * response = sasl_mech_->HandleSaslChallenge(element); + if (response == NULL) { + return Failure(XmppEngine::ERROR_AUTH); + } + pctx_->InternalSendStanza(response); + delete response; + state_ = LOGINSTATE_SASL_RUNNING; + continue; + } + if (element->Name() != TQN_SASL_SUCCESS) { + return Failure(XmppEngine::ERROR_UNAUTHORIZED); + } + + // Authenticated! + authNeeded_ = false; + state_ = LOGINSTATE_INIT; + continue; + } + + case LOGINSTATE_BIND_INIT: { + const XmlElement * pelBindFeature = GetFeature(TQN_BIND_BIND); + const XmlElement * pelSessionFeature = GetFeature(TQN_SESSION_SESSION); + if (!pelBindFeature || !pelSessionFeature) + return Failure(XmppEngine::ERROR_BIND); + + XmlElement iq(TQN_IQ); + iq.AddAttr(TQN_TYPE, "set"); + + iqId_ = pctx_->NextId(); + iq.AddAttr(TQN_ID, iqId_); + iq.AddElement(new XmlElement(TQN_BIND_BIND, true)); + + if (pctx_->requested_resource_ != STR_EMPTY) { + iq.AddElement(new XmlElement(TQN_BIND_RESOURCE), 1); + iq.AddText(pctx_->requested_resource_, 2); + } + pctx_->InternalSendStanza(&iq); + state_ = LOGINSTATE_BIND_REQUESTED; + continue; + } + + case LOGINSTATE_BIND_REQUESTED: { + if (NULL == (element = NextStanza())) + return true; + + if (element->Name() != TQN_IQ || element->Attr(TQN_ID) != iqId_ || + element->Attr(TQN_TYPE) == "get" || element->Attr(TQN_TYPE) == "set") + return true; + + if (element->Attr(TQN_TYPE) != "result" || element->FirstElement() == NULL || + element->FirstElement()->Name() != TQN_BIND_BIND) + return Failure(XmppEngine::ERROR_BIND); + + fullJid_ = Jid(element->FirstElement()->TextNamed(TQN_BIND_JID)); + if (!fullJid_.IsFull()) { + return Failure(XmppEngine::ERROR_BIND); + } + + if (pctx_->user_jid_.domain() != STR_DEFAULT_DOMAIN && + fullJid_.BareJid() != pctx_->user_jid_) { + return Failure(XmppEngine::ERROR_BIND); + } + + // now request session + XmlElement iq(TQN_IQ); + iq.AddAttr(TQN_TYPE, "set"); + + iqId_ = pctx_->NextId(); + iq.AddAttr(TQN_ID, iqId_); + iq.AddElement(new XmlElement(TQN_SESSION_SESSION, true)); + pctx_->InternalSendStanza(&iq); + + state_ = LOGINSTATE_SESSION_REQUESTED; + continue; + } + + case LOGINSTATE_SESSION_REQUESTED: { + if (NULL == (element = NextStanza())) + return true; + if (element->Name() != TQN_IQ || element->Attr(TQN_ID) != iqId_ || + element->Attr(TQN_TYPE) == "get" || element->Attr(TQN_TYPE) == "set") + return false; + + if (element->Attr(TQN_TYPE) != "result") + return Failure(XmppEngine::ERROR_BIND); + + pctx_->SignalBound(fullJid_); + FlushQueuedStanzas(); + state_ = LOGINSTATE_DONE; + return true; + } + + case LOGINSTATE_DONE: + return false; + } + } +} + +bool +XmppLoginTask::HandleStartStream(const XmlElement *element) { + + if (element->Name() != TQN_STREAM_STREAM) + return false; + + if (element->Attr(TQN_XMLNS) != "jabber:client") + return false; + + if (element->Attr(TQN_VERSION) != "1.0") + return false; + + if (!element->HasAttr(TQN_ID)) + return false; + + streamId_ = element->Attr(TQN_ID); + + return true; +} + +bool +XmppLoginTask::HandleFeatures(const XmlElement *element) { + if (element->Name() != TQN_STREAM_FEATURES) + return false; + + pelFeatures_.reset(new XmlElement(*element)); + return true; +} + +const XmlElement * +XmppLoginTask::GetFeature(const TQName & name) { + return pelFeatures_->FirstNamed(name); +} + +bool +XmppLoginTask::Failure(XmppEngine::Error reason) { + state_ = LOGINSTATE_DONE; + pctx_->SignalError(reason); + return false; +} + +void +XmppLoginTask::OutgoingStanza(const XmlElement * element) { + XmlElement * pelCopy = new XmlElement(*element); + pvecQueuedStanzas_->push_back(pelCopy); +} + +void +XmppLoginTask::FlushQueuedStanzas() { + for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) { + pctx_->InternalSendStanza((*pvecQueuedStanzas_)[i]); + delete (*pvecQueuedStanzas_)[i]; + } + pvecQueuedStanzas_->clear(); +} + +} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc deleted file mode 100644 index 66ed44fb..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 -#include "talk/xmllite/xmlelement.h" -#include "talk/base/common.h" -#include "talk/xmpp/xmppstanzaparser.h" -#include "talk/xmpp/constants.h" - -#define new TRACK_NEW - -namespace buzz { - -XmppStanzaParser::XmppStanzaParser(XmppStanzaParseHandler *psph) : - psph_(psph), - innerHandler_(this), - parser_(&innerHandler_), - depth_(0), - builder_() { -} - -void -XmppStanzaParser::Reset() { - parser_.Reset(); - depth_ = 0; - builder_.Reset(); -} - -void -XmppStanzaParser::IncomingStartElement( - XmlParseContext * pctx, const char * name, const char ** atts) { - if (depth_++ == 0) { - XmlElement * pelStream = XmlBuilder::BuildElement(pctx, name, atts); - if (pelStream == NULL) { - pctx->RaiseError(XML_ERROR_SYNTAX); - return; - } - psph_->StartStream(pelStream); - delete pelStream; - return; - } - - builder_.StartElement(pctx, name, atts); -} - -void -XmppStanzaParser::IncomingCharacterData( - XmlParseContext * pctx, const char * text, int len) { - if (depth_ > 1) { - builder_.CharacterData(pctx, text, len); - } -} - -void -XmppStanzaParser::IncomingEndElement( - XmlParseContext * pctx, const char * name) { - if (--depth_ == 0) { - psph_->EndStream(); - return; - } - - builder_.EndElement(pctx, name); - - if (depth_ == 1) { - XmlElement *element = builder_.CreateElement(); - psph_->Stanza(element); - delete element; - } -} - -void -XmppStanzaParser::IncomingError( - XmlParseContext * pctx, XML_Error errCode) { - UNUSED(pctx); - UNUSED(errCode); - psph_->XmlError(); -} - -} - diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cpp new file mode 100644 index 00000000..66ed44fb --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cpp @@ -0,0 +1,104 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 +#include "talk/xmllite/xmlelement.h" +#include "talk/base/common.h" +#include "talk/xmpp/xmppstanzaparser.h" +#include "talk/xmpp/constants.h" + +#define new TRACK_NEW + +namespace buzz { + +XmppStanzaParser::XmppStanzaParser(XmppStanzaParseHandler *psph) : + psph_(psph), + innerHandler_(this), + parser_(&innerHandler_), + depth_(0), + builder_() { +} + +void +XmppStanzaParser::Reset() { + parser_.Reset(); + depth_ = 0; + builder_.Reset(); +} + +void +XmppStanzaParser::IncomingStartElement( + XmlParseContext * pctx, const char * name, const char ** atts) { + if (depth_++ == 0) { + XmlElement * pelStream = XmlBuilder::BuildElement(pctx, name, atts); + if (pelStream == NULL) { + pctx->RaiseError(XML_ERROR_SYNTAX); + return; + } + psph_->StartStream(pelStream); + delete pelStream; + return; + } + + builder_.StartElement(pctx, name, atts); +} + +void +XmppStanzaParser::IncomingCharacterData( + XmlParseContext * pctx, const char * text, int len) { + if (depth_ > 1) { + builder_.CharacterData(pctx, text, len); + } +} + +void +XmppStanzaParser::IncomingEndElement( + XmlParseContext * pctx, const char * name) { + if (--depth_ == 0) { + psph_->EndStream(); + return; + } + + builder_.EndElement(pctx, name); + + if (depth_ == 1) { + XmlElement *element = builder_.CreateElement(); + psph_->Stanza(element); + delete element; + } +} + +void +XmppStanzaParser::IncomingError( + XmlParseContext * pctx, XML_Error errCode) { + UNUSED(pctx); + UNUSED(errCode); + psph_->XmlError(); +} + +} + diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc deleted file mode 100644 index c9d86ba6..00000000 --- a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc +++ /dev/null @@ -1,168 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/xmpp/xmpptask.h" -#include "talk/xmpp/xmppclient.h" -#include "talk/xmpp/xmppengine.h" -#include "talk/xmpp/constants.h" - -namespace buzz { - -XmppTask::XmppTask(Task * parent, XmppEngine::HandlerLevel level) - : Task(parent), client_(NULL) { - XmppClient * client = (XmppClient*)parent->GetParent(XMPP_CLIENT_TASK_CODE); - client_ = client; - id_ = client->NextId(); - client->AddXmppTask(this, level); - client->SignalDisconnected.connect(this, &XmppTask::OnDisconnect); -} - -XmppTask::~XmppTask() { - StopImpl(); -} - -void -XmppTask::StopImpl() { - while (NextStanza() != NULL) {} - if (client_) { - client_->RemoveXmppTask(this); - client_->SignalDisconnected.disconnect(this); - client_ = NULL; - } -} - -XmppReturnStatus -XmppTask::SendStanza(const XmlElement * stanza) { - if (client_ == NULL) - return XMPP_RETURN_BADSTATE; - return client_->SendStanza(stanza); -} - -XmppReturnStatus -XmppTask::SendStanzaError(const XmlElement * element_original, - XmppStanzaError code, - const std::string & text) { - if (client_ == NULL) - return XMPP_RETURN_BADSTATE; - return client_->SendStanzaError(element_original, code, text); -} - -void -XmppTask::Stop() { - StopImpl(); - Task::Stop(); -} - -void -XmppTask::OnDisconnect() { - Error(); -} - -void -XmppTask::QueueStanza(const XmlElement * stanza) { - stanza_queue_.push_back(new XmlElement(*stanza)); - Wake(); -} - -const XmlElement * -XmppTask::NextStanza() { - XmlElement * result = NULL; - if (!stanza_queue_.empty()) { - result = stanza_queue_.front(); - stanza_queue_.pop_front(); - } - next_stanza_.reset(result); - return result; -} - -XmlElement * -XmppTask::MakeIq(const std::string & type, - const buzz::Jid & to, const std::string id) { - XmlElement * result = new XmlElement(TQN_IQ); - if (!type.empty()) - result->AddAttr(TQN_TYPE, type); - if (to != JID_EMPTY) - result->AddAttr(TQN_TO, to.Str()); - if (!id.empty()) - result->AddAttr(TQN_ID, id); - return result; -} - -XmlElement * -XmppTask::MakeIqResult(const XmlElement * query) { - XmlElement * result = new XmlElement(TQN_IQ); - result->AddAttr(TQN_TYPE, STR_RESULT); - if (query->HasAttr(TQN_FROM)) { - result->AddAttr(TQN_TO, query->Attr(TQN_FROM)); - } - result->AddAttr(TQN_ID, query->Attr(TQN_ID)); - return result; -} - -bool -XmppTask::MatchResponseIq(const XmlElement * stanza, - const Jid & to, const std::string & id) { - if (stanza->Name() != TQN_IQ) - return false; - - if (stanza->Attr(TQN_ID) != id) - return false; - - Jid from(stanza->Attr(TQN_FROM)); - if (from != to) { - Jid me = client_->jid(); - // we address the server as "", but it is legal for the server - // to identify itself with "domain" or "myself@domain" - if (to != JID_EMPTY) { - return false; - } - - if (from != Jid(me.domain()) && from != me.BareJid()) { - return false; - } - } - - - return true; -} - -bool -XmppTask::MatchRequestIq(const XmlElement * stanza, - const std::string & type, const TQName & qn) { - if (stanza->Name() != TQN_IQ) - return false; - - if (stanza->Attr(TQN_TYPE) != type) - return false; - - if (stanza->FirstNamed(qn) == NULL) - return false; - - return true; -} - -} diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cpp new file mode 100644 index 00000000..c9d86ba6 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cpp @@ -0,0 +1,168 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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. 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 AUTHOR ``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 AUTHOR 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 "talk/xmpp/xmpptask.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/constants.h" + +namespace buzz { + +XmppTask::XmppTask(Task * parent, XmppEngine::HandlerLevel level) + : Task(parent), client_(NULL) { + XmppClient * client = (XmppClient*)parent->GetParent(XMPP_CLIENT_TASK_CODE); + client_ = client; + id_ = client->NextId(); + client->AddXmppTask(this, level); + client->SignalDisconnected.connect(this, &XmppTask::OnDisconnect); +} + +XmppTask::~XmppTask() { + StopImpl(); +} + +void +XmppTask::StopImpl() { + while (NextStanza() != NULL) {} + if (client_) { + client_->RemoveXmppTask(this); + client_->SignalDisconnected.disconnect(this); + client_ = NULL; + } +} + +XmppReturnStatus +XmppTask::SendStanza(const XmlElement * stanza) { + if (client_ == NULL) + return XMPP_RETURN_BADSTATE; + return client_->SendStanza(stanza); +} + +XmppReturnStatus +XmppTask::SendStanzaError(const XmlElement * element_original, + XmppStanzaError code, + const std::string & text) { + if (client_ == NULL) + return XMPP_RETURN_BADSTATE; + return client_->SendStanzaError(element_original, code, text); +} + +void +XmppTask::Stop() { + StopImpl(); + Task::Stop(); +} + +void +XmppTask::OnDisconnect() { + Error(); +} + +void +XmppTask::QueueStanza(const XmlElement * stanza) { + stanza_queue_.push_back(new XmlElement(*stanza)); + Wake(); +} + +const XmlElement * +XmppTask::NextStanza() { + XmlElement * result = NULL; + if (!stanza_queue_.empty()) { + result = stanza_queue_.front(); + stanza_queue_.pop_front(); + } + next_stanza_.reset(result); + return result; +} + +XmlElement * +XmppTask::MakeIq(const std::string & type, + const buzz::Jid & to, const std::string id) { + XmlElement * result = new XmlElement(TQN_IQ); + if (!type.empty()) + result->AddAttr(TQN_TYPE, type); + if (to != JID_EMPTY) + result->AddAttr(TQN_TO, to.Str()); + if (!id.empty()) + result->AddAttr(TQN_ID, id); + return result; +} + +XmlElement * +XmppTask::MakeIqResult(const XmlElement * query) { + XmlElement * result = new XmlElement(TQN_IQ); + result->AddAttr(TQN_TYPE, STR_RESULT); + if (query->HasAttr(TQN_FROM)) { + result->AddAttr(TQN_TO, query->Attr(TQN_FROM)); + } + result->AddAttr(TQN_ID, query->Attr(TQN_ID)); + return result; +} + +bool +XmppTask::MatchResponseIq(const XmlElement * stanza, + const Jid & to, const std::string & id) { + if (stanza->Name() != TQN_IQ) + return false; + + if (stanza->Attr(TQN_ID) != id) + return false; + + Jid from(stanza->Attr(TQN_FROM)); + if (from != to) { + Jid me = client_->jid(); + // we address the server as "", but it is legal for the server + // to identify itself with "domain" or "myself@domain" + if (to != JID_EMPTY) { + return false; + } + + if (from != Jid(me.domain()) && from != me.BareJid()) { + return false; + } + } + + + return true; +} + +bool +XmppTask::MatchRequestIq(const XmlElement * stanza, + const std::string & type, const TQName & qn) { + if (stanza->Name() != TQN_IQ) + return false; + + if (stanza->Attr(TQN_TYPE) != type) + return false; + + if (stanza->FirstNamed(qn) == NULL) + return false; + + return true; +} + +} -- cgit v1.2.1