From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- .../jingle/libjingle/talk/p2p/base/session.cc | 421 +++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc (limited to 'kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc') diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc new file mode 100644 index 00000000..73873338 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc @@ -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 -- cgit v1.2.1