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
39namespace cricket {
40
41SessionManager::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
53SessionManager::~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
60void 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
66void 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
72SessionClient* 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
77Session* 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
84Session* 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
104void 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
116Session* 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
123void SessionManager::TerminateAll() {
124  while (session_map_.begin() != session_map_.end()) {
125    Session* session = session_map_.begin()->second;
126    session->Terminate();
127  }
128}
129
130bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
131  return cricket::IsSessionMessage(stanza);
132}
133
134Session* 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
147void 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
187void 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
194void 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
218void 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
228buzz::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
270void SessionManager::OnOutgoingMessage(Session* session,
271                                       const buzz::XmlElement* stanza) {
272  SignalOutgoingMessage(this, stanza);
273}
274
275void 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
284void SessionManager::OnSignalingReady() {
285  for (SessionMap::iterator it = session_map_.begin();
286      it != session_map_.end();
287      ++it) {
288    it->second->OnSignalingReady();
289  }
290}
291
292void SessionManager::OnRequestSignaling(Session* session) {
293  SignalRequestSignaling();
294}
295
296}  // namespace cricket
297