diff options
Diffstat (limited to 'Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc')
-rw-r--r-- | Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc new file mode 100644 index 0000000..5d030bb --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc @@ -0,0 +1,336 @@ +/* + * 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/sessionmanager.h" +#include "talk/base/common.h" +#include "talk/base/helpers.h" +#include "talk/p2p/base/constants.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/jid.h" + +namespace cricket { + +SessionManager::SessionManager(PortAllocator *allocator, + talk_base::Thread *worker, + talk_base::Thread *signaling_thread) { + allocator_ = allocator; + if (signaling_thread == NULL) { + signaling_thread_ = talk_base::Thread::Current(); + } else { + signaling_thread_ = signaling_thread; + } + if (worker == NULL) { + worker_thread_ = talk_base::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(); +} + +void SessionManager::AddClient(const std::string& session_type, + SessionClient* client) { + ASSERT(client_map_.find(session_type) == client_map_.end()); + client_map_[session_type] = client; +} + +void SessionManager::RemoveClient(const std::string& session_type) { + ClientMap::iterator iter = client_map_.find(session_type); + ASSERT(iter != client_map_.end()); + client_map_.erase(iter); +} + +SessionClient* SessionManager::GetClient(const std::string& session_type) { + ClientMap::iterator iter = client_map_.find(session_type); + return (iter != client_map_.end()) ? iter->second : NULL; +} + +Session *SessionManager::CreateSession(const std::string& name, + const std::string& session_type) { + return CreateSession(name, SessionID(name, CreateRandomId()), session_type, + false); +} + +Session *SessionManager::CreateSession( + const std::string &name, const SessionID& id, + const std::string& session_type, bool received_initiate) { + SessionClient* client = GetClient(session_type); + ASSERT(client != NULL); + + Session *session = new Session(this, name, id, session_type, client); + session_map_[session->id()] = session; + session->SignalRequestSignaling.connect( + this, &SessionManager::OnRequestSignaling); + session->SignalOutgoingMessage.connect( + this, &SessionManager::OnOutgoingMessage); + session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage); + SignalSessionCreate(session, received_initiate); + session->client()->OnSessionCreate(session, received_initiate); + return session; +} + +void SessionManager::DestroySession(Session *session) { + if (session != NULL) { + SessionMap::iterator it = session_map_.find(session->id()); + if (it != session_map_.end()) { + SignalSessionDestroy(session); + session->client()->OnSessionDestroy(session); + session_map_.erase(it); + delete session; + } + } +} + +Session *SessionManager::GetSession(const SessionID& id) { + SessionMap::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(); + } +} + +bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { + if (stanza->Name() != buzz::QN_IQ) + return false; + if (!stanza->HasAttr(buzz::QN_TYPE)) + return false; + if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET) + return false; + + const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION); + if (!session) + return false; + if (!session->HasAttr(buzz::QN_TYPE)) + return false; + if (!session->HasAttr(buzz::QN_ID) || !session->HasAttr(QN_INITIATOR)) + return false; + + return true; +} + +Session* SessionManager::FindSessionForStanza(const buzz::XmlElement* stanza, + bool incoming) { + const buzz::XmlElement* session_xml = stanza->FirstNamed(QN_SESSION); + ASSERT(session_xml != NULL); + + SessionID id; + id.set_id_str(session_xml->Attr(buzz::QN_ID)); + id.set_initiator(session_xml->Attr(QN_INITIATOR)); + + // Pass this message to the session in question. + SessionMap::iterator iter = session_map_.find(id); + if (iter == session_map_.end()) + return NULL; + + Session* session = iter->second; + + // match on "from"? or "to"? + buzz::QName attr = buzz::QN_TO; + if (incoming) { + attr = buzz::QN_FROM; + } + buzz::Jid remote(session->remote_name()); + buzz::Jid match(stanza->Attr(attr)); + if (remote == match) { + return session; + } + return NULL; +} + +void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) { + ASSERT(stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET); + + Session* session = FindSessionForStanza(stanza, true); + if (session) { + session->OnIncomingMessage(stanza); + return; + } + + const buzz::XmlElement* session_xml = stanza->FirstNamed(QN_SESSION); + ASSERT(session_xml != NULL); + if (session_xml->Attr(buzz::QN_TYPE) == "initiate") { + std::string session_type = FindClient(session_xml); + if (session_type.size() == 0) { + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown session description type", NULL); + } else { + SessionID id; + id.set_id_str(session_xml->Attr(buzz::QN_ID)); + id.set_initiator(session_xml->Attr(QN_INITIATOR)); + + session = CreateSession(stanza->Attr(buzz::QN_TO), + id, + session_type, true); + session->OnIncomingMessage(stanza); + + // If we haven't rejected, and we haven't selected a transport yet, + // let's do it now. + if ((session->state() != Session::STATE_SENTREJECT) && + (session->transport() == NULL)) { + session->ChooseTransport(stanza); + } + } + return; + } + + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown session", NULL); +} + +void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza) { + // We don't do anything with the response now. If we need to we can forward + // it to the session. + return; +} + +void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza) { + Session* session = FindSessionForStanza(orig_stanza, false); + if (session) { + scoped_ptr<buzz::XmlElement> synthetic_error; + if (!error_stanza) { + // A failed send is semantically equivalent to an error response, so we + // can just turn the former into the latter. + synthetic_error.reset( + CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, + "cancel", "Recipient did not respond", NULL)); + error_stanza = synthetic_error.get(); + } + + session->OnFailedSend(orig_stanza, error_stanza); + } +} + +std::string SessionManager::FindClient(const buzz::XmlElement* session) { + for (const buzz::XmlElement* elem = session->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + if (elem->Name().LocalPart() == "description") { + ClientMap::iterator iter = client_map_.find(elem->Name().Namespace()); + if (iter != client_map_.end()) + return iter->first; + } + } + return ""; +} + +void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + scoped_ptr<buzz::XmlElement> msg( + CreateErrorMessage(stanza, name, type, text, extra_info)); + SignalOutgoingMessage(msg.get()); +} + +buzz::XmlElement* SessionManager::CreateErrorMessage( + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); + iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); + iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); + iq->SetAttr(buzz::QN_TYPE, "error"); + + for (const buzz::XmlElement* elem = stanza->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + iq->AddElement(new buzz::XmlElement(*elem)); + } + + buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); + error->SetAttr(buzz::QN_TYPE, type); + iq->AddElement(error); + + // If the error name is not in the standard namespace, we have to first add + // some error from that namespace. + if (name.Namespace() != buzz::NS_STANZA) { + error->AddElement( + new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); + } + error->AddElement(new buzz::XmlElement(name)); + + if (extra_info) + error->AddElement(new buzz::XmlElement(*extra_info)); + + if (text.size() > 0) { + // It's okay to always use English here. This text is for debugging + // purposes only. + buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); + text_elem->SetAttr(buzz::QN_XML_LANG, "en"); + text_elem->SetBodyText(text); + error->AddElement(text_elem); + } + + // TODO: Should we include error codes as well for SIP compatibility? + + return iq; +} + +void SessionManager::OnOutgoingMessage(Session* session, + const buzz::XmlElement* stanza) { + SignalOutgoingMessage(stanza); +} + +void SessionManager::OnErrorMessage(Session* session, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + SendErrorMessage(stanza, name, type, text, extra_info); +} + +void SessionManager::OnSignalingReady() { + for (SessionMap::iterator it = session_map_.begin(); + it != session_map_.end(); + ++it) { + it->second->OnSignalingReady(); + } +} + +void SessionManager::OnRequestSignaling(Session* session) { + SignalRequestSignaling(); +} + +} // namespace cricket |