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