• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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