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/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"
40
41namespace cricket {
42
43SessionManager::SessionManager(PortAllocator *allocator,
44                               rtc::Thread *worker) {
45  allocator_ = allocator;
46  signaling_thread_ = rtc::Thread::Current();
47  if (worker == NULL) {
48    worker_thread_ = rtc::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  std::string id;
83  return CreateSession(id, local_name, content_type);
84}
85
86Session* SessionManager::CreateSession(const std::string& id,
87                                       const std::string& local_name,
88                                       const std::string& content_type) {
89  std::string sid =
90      id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id;
91  return CreateSession(local_name, local_name, sid, content_type, false);
92}
93
94Session* 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);
100
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);
112  return session;
113}
114
115void 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);
122      delete session;
123    }
124  }
125}
126
127Session* SessionManager::GetSession(const std::string& sid) {
128  SessionMap::iterator it = session_map_.find(sid);
129  if (it != session_map_.end())
130    return it->second;
131  return NULL;
132}
133
134void SessionManager::TerminateAll() {
135  while (session_map_.begin() != session_map_.end()) {
136    Session* session = session_map_.begin()->second;
137    session->Terminate();
138  }
139}
140
141bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
142  return cricket::IsSessionMessage(stanza);
143}
144
145Session* 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())
149    return NULL;
150
151  Session* session = iter->second;
152  if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
153    return NULL;
154
155  return session;
156}
157
158void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
159  SessionMessage msg;
160  ParseError error;
161
162  if (!ParseSessionMessage(stanza, &msg, &error)) {
163    SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
164                     error.text, NULL);
165    return;
166  }
167
168  Session* session = FindSession(msg.sid, msg.from);
169  if (session) {
170    session->OnIncomingMessage(msg);
171    return;
172  }
173  if (msg.type != ACTION_SESSION_INITIATE) {
174    SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
175                     "unknown session", NULL);
176    return;
177  }
178
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",
183                     error.text, NULL);
184    return;
185  }
186
187  if (!GetClient(content_type)) {
188    SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
189                     "unknown content type: " + content_type, NULL);
190    return;
191  }
192
193  session = CreateSession(msg.to, msg.initiator, msg.sid,
194                          content_type, true);
195  session->OnIncomingMessage(msg);
196}
197
198void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
199    const buzz::XmlElement* response_stanza) {
200  if (orig_stanza == NULL || response_stanza == NULL) {
201    return;
202  }
203
204  SessionMessage msg;
205  ParseError error;
206  if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
207    LOG(LS_WARNING) << "Error parsing incoming response: " << error.text
208                    << ":" << orig_stanza;
209    return;
210  }
211
212  Session* session = FindSession(msg.sid, msg.to);
213  if (!session) {
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);
218  }
219  if (session) {
220    session->OnIncomingResponse(orig_stanza, response_stanza, msg);
221  }
222}
223
224void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
225                                  const buzz::XmlElement* error_stanza) {
226  SessionMessage msg;
227  ParseError error;
228  if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
229    return;  // TODO: log somewhere?
230  }
231
232  Session* session = FindSession(msg.sid, msg.to);
233  if (session) {
234    rtc::scoped_ptr<buzz::XmlElement> synthetic_error;
235    if (!error_stanza) {
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();
242    }
243
244    session->OnFailedSend(orig_stanza, error_stanza);
245  }
246}
247
248void 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());
256}
257
258buzz::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");
268
269  CopyXmlChildren(stanza, iq);
270
271  buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
272  error->SetAttr(buzz::QN_TYPE, type);
273  iq->AddElement(error);
274
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) {
278     error->AddElement(
279         new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
280  }
281  error->AddElement(new buzz::XmlElement(name));
282
283  if (extra_info)
284    error->AddElement(new buzz::XmlElement(*extra_info));
285
286  if (text.size() > 0) {
287    // It's okay to always use English here.  This text is for debugging
288    // purposes only.
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);
293  }
294
295  // TODO: Should we include error codes as well for SIP compatibility?
296
297  return iq;
298}
299
300void SessionManager::OnOutgoingMessage(Session* session,
301                                       const buzz::XmlElement* stanza) {
302  SignalOutgoingMessage(this, stanza);
303}
304
305void 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);
312}
313
314void SessionManager::OnSignalingReady() {
315  for (SessionMap::iterator it = session_map_.begin();
316      it != session_map_.end();
317      ++it) {
318    it->second->OnSignalingReady();
319  }
320}
321
322void SessionManager::OnRequestSignaling(Session* session) {
323  SignalRequestSignaling();
324}
325
326}  // namespace cricket
327