blob: 6b3d60a53553e73fa067c32870c36bb7738d6c8a [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2004--2005, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
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.
15 *
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.
26 */
27
28#include "talk/p2p/base/sessionmanager.h"
29
30#include "talk/base/common.h"
31#include "talk/base/helpers.h"
32#include "talk/base/logging.h"
33#include "talk/base/scoped_ptr.h"
34#include "talk/base/stringencode.h"
35#include "talk/p2p/base/constants.h"
36#include "talk/p2p/base/session.h"
37#include "talk/p2p/base/sessionmessages.h"
38#include "talk/xmpp/constants.h"
39#include "talk/xmpp/jid.h"
40
41namespace cricket {
42
43SessionManager::SessionManager(PortAllocator *allocator,
44 talk_base::Thread *worker) {
45 allocator_ = allocator;
46 signaling_thread_ = talk_base::Thread::Current();
47 if (worker == NULL) {
48 worker_thread_ = talk_base::Thread::Current();
49 } else {
50 worker_thread_ = worker;
51 }
52 timeout_ = 50;
53}
54
55SessionManager::~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());
59 // TerminateAll();
60 SignalDestroyed();
61}
62
63void 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;
67}
68
69void 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);
73}
74
75SessionClient* 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;
78}
79
80Session* SessionManager::CreateSession(const std::string& local_name,
81 const std::string& content_type) {
82 return CreateSession(local_name, local_name,
83 talk_base::ToString(talk_base::CreateRandomId64()),
84 content_type, false);
85}
86
87Session* SessionManager::CreateSession(
88 const std::string& local_name, const std::string& initiator_name,
89 const std::string& sid, const std::string& content_type,
90 bool received_initiate) {
91 SessionClient* client = GetClient(content_type);
92 ASSERT(client != NULL);
93
94 Session* session = new Session(this, local_name, initiator_name,
95 sid, content_type, client);
96 session->set_identity(transport_desc_factory_.identity());
97 session_map_[session->id()] = session;
98 session->SignalRequestSignaling.connect(
99 this, &SessionManager::OnRequestSignaling);
100 session->SignalOutgoingMessage.connect(
101 this, &SessionManager::OnOutgoingMessage);
102 session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
103 SignalSessionCreate(session, received_initiate);
104 session->client()->OnSessionCreate(session, received_initiate);
105 return session;
106}
107
108void SessionManager::DestroySession(Session* session) {
109 if (session != NULL) {
110 SessionMap::iterator it = session_map_.find(session->id());
111 if (it != session_map_.end()) {
112 SignalSessionDestroy(session);
113 session->client()->OnSessionDestroy(session);
114 session_map_.erase(it);
115 delete session;
116 }
117 }
118}
119
120Session* SessionManager::GetSession(const std::string& sid) {
121 SessionMap::iterator it = session_map_.find(sid);
122 if (it != session_map_.end())
123 return it->second;
124 return NULL;
125}
126
127void SessionManager::TerminateAll() {
128 while (session_map_.begin() != session_map_.end()) {
129 Session* session = session_map_.begin()->second;
130 session->Terminate();
131 }
132}
133
134bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
135 return cricket::IsSessionMessage(stanza);
136}
137
138Session* SessionManager::FindSession(const std::string& sid,
139 const std::string& remote_name) {
140 SessionMap::iterator iter = session_map_.find(sid);
141 if (iter == session_map_.end())
142 return NULL;
143
144 Session* session = iter->second;
145 if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
146 return NULL;
147
148 return session;
149}
150
151void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
152 SessionMessage msg;
153 ParseError error;
154
155 if (!ParseSessionMessage(stanza, &msg, &error)) {
156 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
157 error.text, NULL);
158 return;
159 }
160
161 Session* session = FindSession(msg.sid, msg.from);
162 if (session) {
163 session->OnIncomingMessage(msg);
164 return;
165 }
166 if (msg.type != ACTION_SESSION_INITIATE) {
167 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
168 "unknown session", NULL);
169 return;
170 }
171
172 std::string content_type;
173 if (!ParseContentType(msg.protocol, msg.action_elem,
174 &content_type, &error)) {
175 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
176 error.text, NULL);
177 return;
178 }
179
180 if (!GetClient(content_type)) {
181 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
182 "unknown content type: " + content_type, NULL);
183 return;
184 }
185
186 session = CreateSession(msg.to, msg.initiator, msg.sid,
187 content_type, true);
188 session->OnIncomingMessage(msg);
189}
190
191void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
192 const buzz::XmlElement* response_stanza) {
193 if (orig_stanza == NULL || response_stanza == NULL) {
194 return;
195 }
196
197 SessionMessage msg;
198 ParseError error;
199 if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
200 LOG(LS_WARNING) << "Error parsing incoming response: " << error.text
201 << ":" << orig_stanza;
202 return;
203 }
204
205 Session* session = FindSession(msg.sid, msg.to);
206 if (session) {
207 session->OnIncomingResponse(orig_stanza, response_stanza, msg);
208 }
209}
210
211void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
212 const buzz::XmlElement* error_stanza) {
213 SessionMessage msg;
214 ParseError error;
215 if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
216 return; // TODO: log somewhere?
217 }
218
219 Session* session = FindSession(msg.sid, msg.to);
220 if (session) {
221 talk_base::scoped_ptr<buzz::XmlElement> synthetic_error;
222 if (!error_stanza) {
223 // A failed send is semantically equivalent to an error response, so we
224 // can just turn the former into the latter.
225 synthetic_error.reset(
226 CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
227 "cancel", "Recipient did not respond", NULL));
228 error_stanza = synthetic_error.get();
229 }
230
231 session->OnFailedSend(orig_stanza, error_stanza);
232 }
233}
234
235void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
236 const buzz::QName& name,
237 const std::string& type,
238 const std::string& text,
239 const buzz::XmlElement* extra_info) {
240 talk_base::scoped_ptr<buzz::XmlElement> msg(
241 CreateErrorMessage(stanza, name, type, text, extra_info));
242 SignalOutgoingMessage(this, msg.get());
243}
244
245buzz::XmlElement* SessionManager::CreateErrorMessage(
246 const buzz::XmlElement* stanza,
247 const buzz::QName& name,
248 const std::string& type,
249 const std::string& text,
250 const buzz::XmlElement* extra_info) {
251 buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
252 iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
253 iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
254 iq->SetAttr(buzz::QN_TYPE, "error");
255
256 CopyXmlChildren(stanza, iq);
257
258 buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
259 error->SetAttr(buzz::QN_TYPE, type);
260 iq->AddElement(error);
261
262 // If the error name is not in the standard namespace, we have to first add
263 // some error from that namespace.
264 if (name.Namespace() != buzz::NS_STANZA) {
265 error->AddElement(
266 new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
267 }
268 error->AddElement(new buzz::XmlElement(name));
269
270 if (extra_info)
271 error->AddElement(new buzz::XmlElement(*extra_info));
272
273 if (text.size() > 0) {
274 // It's okay to always use English here. This text is for debugging
275 // purposes only.
276 buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
277 text_elem->SetAttr(buzz::QN_XML_LANG, "en");
278 text_elem->SetBodyText(text);
279 error->AddElement(text_elem);
280 }
281
282 // TODO: Should we include error codes as well for SIP compatibility?
283
284 return iq;
285}
286
287void SessionManager::OnOutgoingMessage(Session* session,
288 const buzz::XmlElement* stanza) {
289 SignalOutgoingMessage(this, stanza);
290}
291
292void SessionManager::OnErrorMessage(BaseSession* session,
293 const buzz::XmlElement* stanza,
294 const buzz::QName& name,
295 const std::string& type,
296 const std::string& text,
297 const buzz::XmlElement* extra_info) {
298 SendErrorMessage(stanza, name, type, text, extra_info);
299}
300
301void SessionManager::OnSignalingReady() {
302 for (SessionMap::iterator it = session_map_.begin();
303 it != session_map_.end();
304 ++it) {
305 it->second->OnSignalingReady();
306 }
307}
308
309void SessionManager::OnRequestSignaling(Session* session) {
310 SignalRequestSignaling();
311}
312
313} // namespace cricket