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/logging.h" 33 #include "talk/base/scoped_ptr.h" 34 #include "talk/base/stringencode.h" 35 #include "talk/p2p/base/constants.h" 36 #include "talk/p2p/base/session.h" 37 #include "talk/p2p/base/sessionmessages.h" 38 #include "talk/xmpp/constants.h" 39 #include "talk/xmpp/jid.h" 40 41 namespace cricket { 42 43 SessionManager::SessionManager(PortAllocator *allocator, 44 talk_base::Thread *worker) { 45 allocator_ = allocator; 46 signaling_thread_ = talk_base::Thread::Current(); 47 if (worker == NULL) { 48 worker_thread_ = talk_base::Thread::Current(); 49 } else { 50 worker_thread_ = worker; 51 } 52 timeout_ = 50; 53 } 54 55 SessionManager::~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 63 void 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 69 void 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 75 SessionClient* 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 80 Session* 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 86 Session* 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() ? talk_base::ToString(talk_base::CreateRandomId64()) : id; 91 return CreateSession(local_name, local_name, sid, content_type, false); 92 } 93 94 Session* 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 115 void 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 127 Session* 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 134 void SessionManager::TerminateAll() { 135 while (session_map_.begin() != session_map_.end()) { 136 Session* session = session_map_.begin()->second; 137 session->Terminate(); 138 } 139 } 140 141 bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { 142 return cricket::IsSessionMessage(stanza); 143 } 144 145 Session* 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 158 void 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 198 void 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 session->OnIncomingResponse(orig_stanza, response_stanza, msg); 215 } 216 } 217 218 void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, 219 const buzz::XmlElement* error_stanza) { 220 SessionMessage msg; 221 ParseError error; 222 if (!ParseSessionMessage(orig_stanza, &msg, &error)) { 223 return; // TODO: log somewhere? 224 } 225 226 Session* session = FindSession(msg.sid, msg.to); 227 if (session) { 228 talk_base::scoped_ptr<buzz::XmlElement> synthetic_error; 229 if (!error_stanza) { 230 // A failed send is semantically equivalent to an error response, so we 231 // can just turn the former into the latter. 232 synthetic_error.reset( 233 CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, 234 "cancel", "Recipient did not respond", NULL)); 235 error_stanza = synthetic_error.get(); 236 } 237 238 session->OnFailedSend(orig_stanza, error_stanza); 239 } 240 } 241 242 void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, 243 const buzz::QName& name, 244 const std::string& type, 245 const std::string& text, 246 const buzz::XmlElement* extra_info) { 247 talk_base::scoped_ptr<buzz::XmlElement> msg( 248 CreateErrorMessage(stanza, name, type, text, extra_info)); 249 SignalOutgoingMessage(this, msg.get()); 250 } 251 252 buzz::XmlElement* SessionManager::CreateErrorMessage( 253 const buzz::XmlElement* stanza, 254 const buzz::QName& name, 255 const std::string& type, 256 const std::string& text, 257 const buzz::XmlElement* extra_info) { 258 buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); 259 iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); 260 iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); 261 iq->SetAttr(buzz::QN_TYPE, "error"); 262 263 CopyXmlChildren(stanza, iq); 264 265 buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); 266 error->SetAttr(buzz::QN_TYPE, type); 267 iq->AddElement(error); 268 269 // If the error name is not in the standard namespace, we have to first add 270 // some error from that namespace. 271 if (name.Namespace() != buzz::NS_STANZA) { 272 error->AddElement( 273 new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); 274 } 275 error->AddElement(new buzz::XmlElement(name)); 276 277 if (extra_info) 278 error->AddElement(new buzz::XmlElement(*extra_info)); 279 280 if (text.size() > 0) { 281 // It's okay to always use English here. This text is for debugging 282 // purposes only. 283 buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); 284 text_elem->SetAttr(buzz::QN_XML_LANG, "en"); 285 text_elem->SetBodyText(text); 286 error->AddElement(text_elem); 287 } 288 289 // TODO: Should we include error codes as well for SIP compatibility? 290 291 return iq; 292 } 293 294 void SessionManager::OnOutgoingMessage(Session* session, 295 const buzz::XmlElement* stanza) { 296 SignalOutgoingMessage(this, stanza); 297 } 298 299 void SessionManager::OnErrorMessage(BaseSession* session, 300 const buzz::XmlElement* stanza, 301 const buzz::QName& name, 302 const std::string& type, 303 const std::string& text, 304 const buzz::XmlElement* extra_info) { 305 SendErrorMessage(stanza, name, type, text, extra_info); 306 } 307 308 void SessionManager::OnSignalingReady() { 309 for (SessionMap::iterator it = session_map_.begin(); 310 it != session_map_.end(); 311 ++it) { 312 it->second->OnSignalingReady(); 313 } 314 } 315 316 void SessionManager::OnRequestSignaling(Session* session) { 317 SignalRequestSignaling(); 318 } 319 320 } // namespace cricket 321