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