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