3 * Copyright 2004--2005, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "talk/p2p/base/sessionmanager.h"
30 #include "talk/p2p/base/constants.h"
31 #include "talk/p2p/base/session.h"
32 #include "talk/p2p/base/sessionmessages.h"
33 #include "talk/xmpp/constants.h"
34 #include "talk/xmpp/jid.h"
35 #include "webrtc/base/common.h"
36 #include "webrtc/base/helpers.h"
37 #include "webrtc/base/logging.h"
38 #include "webrtc/base/scoped_ptr.h"
39 #include "webrtc/base/stringencode.h"
43 SessionManager::SessionManager(PortAllocator *allocator,
44 rtc::Thread *worker) {
45 allocator_ = allocator;
46 signaling_thread_ = rtc::Thread::Current();
48 worker_thread_ = rtc::Thread::Current();
50 worker_thread_ = worker;
55 SessionManager::~SessionManager() {
56 // Note: Session::Terminate occurs asynchronously, so it's too late to
57 // delete them now. They better be all gone.
58 ASSERT(session_map_.empty());
63 void SessionManager::AddClient(const std::string& content_type,
64 SessionClient* client) {
65 ASSERT(client_map_.find(content_type) == client_map_.end());
66 client_map_[content_type] = client;
69 void SessionManager::RemoveClient(const std::string& content_type) {
70 ClientMap::iterator iter = client_map_.find(content_type);
71 ASSERT(iter != client_map_.end());
72 client_map_.erase(iter);
75 SessionClient* SessionManager::GetClient(const std::string& content_type) {
76 ClientMap::iterator iter = client_map_.find(content_type);
77 return (iter != client_map_.end()) ? iter->second : NULL;
80 Session* SessionManager::CreateSession(const std::string& local_name,
81 const std::string& content_type) {
83 return CreateSession(id, local_name, content_type);
86 Session* SessionManager::CreateSession(const std::string& id,
87 const std::string& local_name,
88 const std::string& content_type) {
90 id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id;
91 return CreateSession(local_name, local_name, sid, content_type, false);
94 Session* SessionManager::CreateSession(
95 const std::string& local_name, const std::string& initiator_name,
96 const std::string& sid, const std::string& content_type,
97 bool received_initiate) {
98 SessionClient* client = GetClient(content_type);
99 ASSERT(client != NULL);
101 Session* session = new Session(this, local_name, initiator_name,
102 sid, content_type, client);
103 session->SetIdentity(transport_desc_factory_.identity());
104 session_map_[session->id()] = session;
105 session->SignalRequestSignaling.connect(
106 this, &SessionManager::OnRequestSignaling);
107 session->SignalOutgoingMessage.connect(
108 this, &SessionManager::OnOutgoingMessage);
109 session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
110 SignalSessionCreate(session, received_initiate);
111 session->client()->OnSessionCreate(session, received_initiate);
115 void SessionManager::DestroySession(Session* session) {
116 if (session != NULL) {
117 SessionMap::iterator it = session_map_.find(session->id());
118 if (it != session_map_.end()) {
119 SignalSessionDestroy(session);
120 session->client()->OnSessionDestroy(session);
121 session_map_.erase(it);
127 Session* SessionManager::GetSession(const std::string& sid) {
128 SessionMap::iterator it = session_map_.find(sid);
129 if (it != session_map_.end())
134 void SessionManager::TerminateAll() {
135 while (session_map_.begin() != session_map_.end()) {
136 Session* session = session_map_.begin()->second;
137 session->Terminate();
141 bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
142 return cricket::IsSessionMessage(stanza);
145 Session* SessionManager::FindSession(const std::string& sid,
146 const std::string& remote_name) {
147 SessionMap::iterator iter = session_map_.find(sid);
148 if (iter == session_map_.end())
151 Session* session = iter->second;
152 if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
158 void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
162 if (!ParseSessionMessage(stanza, &msg, &error)) {
163 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
168 Session* session = FindSession(msg.sid, msg.from);
170 session->OnIncomingMessage(msg);
173 if (msg.type != ACTION_SESSION_INITIATE) {
174 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
175 "unknown session", NULL);
179 std::string content_type;
180 if (!ParseContentType(msg.protocol, msg.action_elem,
181 &content_type, &error)) {
182 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
187 if (!GetClient(content_type)) {
188 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
189 "unknown content type: " + content_type, NULL);
193 session = CreateSession(msg.to, msg.initiator, msg.sid,
195 session->OnIncomingMessage(msg);
198 void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
199 const buzz::XmlElement* response_stanza) {
200 if (orig_stanza == NULL || response_stanza == NULL) {
206 if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
207 LOG(LS_WARNING) << "Error parsing incoming response: " << error.text
208 << ":" << orig_stanza;
212 Session* session = FindSession(msg.sid, msg.to);
214 // Also try the QN_FROM in the response stanza, in case we sent the request
215 // to a bare JID but got the response from a full JID.
216 std::string ack_from = response_stanza->Attr(buzz::QN_FROM);
217 session = FindSession(msg.sid, ack_from);
220 session->OnIncomingResponse(orig_stanza, response_stanza, msg);
224 void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
225 const buzz::XmlElement* error_stanza) {
228 if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
229 return; // TODO: log somewhere?
232 Session* session = FindSession(msg.sid, msg.to);
234 rtc::scoped_ptr<buzz::XmlElement> synthetic_error;
236 // A failed send is semantically equivalent to an error response, so we
237 // can just turn the former into the latter.
238 synthetic_error.reset(
239 CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
240 "cancel", "Recipient did not respond", NULL));
241 error_stanza = synthetic_error.get();
244 session->OnFailedSend(orig_stanza, error_stanza);
248 void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
249 const buzz::QName& name,
250 const std::string& type,
251 const std::string& text,
252 const buzz::XmlElement* extra_info) {
253 rtc::scoped_ptr<buzz::XmlElement> msg(
254 CreateErrorMessage(stanza, name, type, text, extra_info));
255 SignalOutgoingMessage(this, msg.get());
258 buzz::XmlElement* SessionManager::CreateErrorMessage(
259 const buzz::XmlElement* stanza,
260 const buzz::QName& name,
261 const std::string& type,
262 const std::string& text,
263 const buzz::XmlElement* extra_info) {
264 buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
265 iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
266 iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
267 iq->SetAttr(buzz::QN_TYPE, "error");
269 CopyXmlChildren(stanza, iq);
271 buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
272 error->SetAttr(buzz::QN_TYPE, type);
273 iq->AddElement(error);
275 // If the error name is not in the standard namespace, we have to first add
276 // some error from that namespace.
277 if (name.Namespace() != buzz::NS_STANZA) {
279 new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
281 error->AddElement(new buzz::XmlElement(name));
284 error->AddElement(new buzz::XmlElement(*extra_info));
286 if (text.size() > 0) {
287 // It's okay to always use English here. This text is for debugging
289 buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
290 text_elem->SetAttr(buzz::QN_XML_LANG, "en");
291 text_elem->SetBodyText(text);
292 error->AddElement(text_elem);
295 // TODO: Should we include error codes as well for SIP compatibility?
300 void SessionManager::OnOutgoingMessage(Session* session,
301 const buzz::XmlElement* stanza) {
302 SignalOutgoingMessage(this, stanza);
305 void SessionManager::OnErrorMessage(BaseSession* session,
306 const buzz::XmlElement* stanza,
307 const buzz::QName& name,
308 const std::string& type,
309 const std::string& text,
310 const buzz::XmlElement* extra_info) {
311 SendErrorMessage(stanza, name, type, text, extra_info);
314 void SessionManager::OnSignalingReady() {
315 for (SessionMap::iterator it = session_map_.begin();
316 it != session_map_.end();
318 it->second->OnSignalingReady();
322 void SessionManager::OnRequestSignaling(Session* session) {
323 SignalRequestSignaling();
326 } // namespace cricket