| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license | |
| 5 * that can be found in the LICENSE file in the root of the source | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | |
| 11 #include "webrtc/libjingle/session/sessionmanager.h" | |
| 12 | |
| 13 #include "webrtc/base/common.h" | |
| 14 #include "webrtc/base/helpers.h" | |
| 15 #include "webrtc/base/logging.h" | |
| 16 #include "webrtc/base/scoped_ptr.h" | |
| 17 #include "webrtc/base/stringencode.h" | |
| 18 #include "webrtc/libjingle/session/p2ptransportparser.h" | |
| 19 #include "webrtc/libjingle/session/sessionmessages.h" | |
| 20 #include "webrtc/libjingle/xmpp/constants.h" | |
| 21 #include "webrtc/libjingle/xmpp/jid.h" | |
| 22 #include "webrtc/p2p/base/constants.h" | |
| 23 | |
| 24 namespace cricket { | |
| 25 | |
| 26 bool BadMessage(const buzz::QName type, | |
| 27 const std::string& text, | |
| 28 MessageError* err) { | |
| 29 err->SetType(type); | |
| 30 err->SetText(text); | |
| 31 return false; | |
| 32 } | |
| 33 | |
| 34 | |
| 35 Session::Session(SessionManager* session_manager, | |
| 36 const std::string& local_name, | |
| 37 const std::string& initiator_name, | |
| 38 const std::string& sid, | |
| 39 const std::string& content_type, | |
| 40 SessionClient* client) | |
| 41 : BaseSession(session_manager->signaling_thread(), | |
| 42 session_manager->worker_thread(), | |
| 43 session_manager->port_allocator(), | |
| 44 sid, content_type, initiator_name == local_name) { | |
| 45 ASSERT(client != NULL); | |
| 46 session_manager_ = session_manager; | |
| 47 local_name_ = local_name; | |
| 48 initiator_name_ = initiator_name; | |
| 49 transport_parser_ = new P2PTransportParser(); | |
| 50 client_ = client; | |
| 51 initiate_acked_ = false; | |
| 52 current_protocol_ = PROTOCOL_HYBRID; | |
| 53 } | |
| 54 | |
| 55 Session::~Session() { | |
| 56 delete transport_parser_; | |
| 57 } | |
| 58 | |
| 59 bool Session::Initiate(const std::string& to, | |
| 60 const SessionDescription* sdesc) { | |
| 61 ASSERT(signaling_thread()->IsCurrent()); | |
| 62 SessionError error; | |
| 63 | |
| 64 // Only from STATE_INIT | |
| 65 if (state() != STATE_INIT) | |
| 66 return false; | |
| 67 | |
| 68 // Setup for signaling. | |
| 69 set_remote_name(to); | |
| 70 set_local_description(sdesc); | |
| 71 if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()), | |
| 72 &error)) { | |
| 73 LOG(LS_ERROR) << "Could not create transports: " << error.text; | |
| 74 return false; | |
| 75 } | |
| 76 | |
| 77 if (!SendInitiateMessage(sdesc, &error)) { | |
| 78 LOG(LS_ERROR) << "Could not send initiate message: " << error.text; | |
| 79 return false; | |
| 80 } | |
| 81 | |
| 82 // We need to connect transport proxy and impl here so that we can process | |
| 83 // the TransportDescriptions. | |
| 84 SpeculativelyConnectAllTransportChannels(); | |
| 85 | |
| 86 PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL); | |
| 87 SetState(Session::STATE_SENTINITIATE); | |
| 88 return true; | |
| 89 } | |
| 90 | |
| 91 bool Session::Accept(const SessionDescription* sdesc) { | |
| 92 ASSERT(signaling_thread()->IsCurrent()); | |
| 93 | |
| 94 // Only if just received initiate | |
| 95 if (state() != STATE_RECEIVEDINITIATE) | |
| 96 return false; | |
| 97 | |
| 98 // Setup for signaling. | |
| 99 set_local_description(sdesc); | |
| 100 | |
| 101 SessionError error; | |
| 102 if (!SendAcceptMessage(sdesc, &error)) { | |
| 103 LOG(LS_ERROR) << "Could not send accept message: " << error.text; | |
| 104 return false; | |
| 105 } | |
| 106 // TODO(juberti): Add BUNDLE support to transport-info messages. | |
| 107 PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL); | |
| 108 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. | |
| 109 SetState(Session::STATE_SENTACCEPT); | |
| 110 return true; | |
| 111 } | |
| 112 | |
| 113 bool Session::Reject(const std::string& reason) { | |
| 114 ASSERT(signaling_thread()->IsCurrent()); | |
| 115 | |
| 116 // Reject is sent in response to an initiate or modify, to reject the | |
| 117 // request | |
| 118 if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY) | |
| 119 return false; | |
| 120 | |
| 121 SessionError error; | |
| 122 if (!SendRejectMessage(reason, &error)) { | |
| 123 LOG(LS_ERROR) << "Could not send reject message: " << error.text; | |
| 124 return false; | |
| 125 } | |
| 126 | |
| 127 SetState(STATE_SENTREJECT); | |
| 128 return true; | |
| 129 } | |
| 130 | |
| 131 bool Session::TerminateWithReason(const std::string& reason) { | |
| 132 ASSERT(signaling_thread()->IsCurrent()); | |
| 133 | |
| 134 // Either side can terminate, at any time. | |
| 135 switch (state()) { | |
| 136 case STATE_SENTTERMINATE: | |
| 137 case STATE_RECEIVEDTERMINATE: | |
| 138 return false; | |
| 139 | |
| 140 case STATE_SENTREJECT: | |
| 141 case STATE_RECEIVEDREJECT: | |
| 142 // We don't need to send terminate if we sent or received a reject... | |
| 143 // it's implicit. | |
| 144 break; | |
| 145 | |
| 146 default: | |
| 147 SessionError error; | |
| 148 if (!SendTerminateMessage(reason, &error)) { | |
| 149 LOG(LS_ERROR) << "Could not send terminate message: " << error.text; | |
| 150 return false; | |
| 151 } | |
| 152 break; | |
| 153 } | |
| 154 | |
| 155 SetState(STATE_SENTTERMINATE); | |
| 156 return true; | |
| 157 } | |
| 158 | |
| 159 bool Session::SendInfoMessage(const XmlElements& elems, | |
| 160 const std::string& remote_name) { | |
| 161 ASSERT(signaling_thread()->IsCurrent()); | |
| 162 SessionError error; | |
| 163 if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) { | |
| 164 LOG(LS_ERROR) << "Could not send info message " << error.text; | |
| 165 return false; | |
| 166 } | |
| 167 return true; | |
| 168 } | |
| 169 | |
| 170 bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) { | |
| 171 XmlElements elems; | |
| 172 WriteError write_error; | |
| 173 if (!WriteDescriptionInfo(current_protocol_, | |
| 174 contents, | |
| 175 GetContentParsers(), | |
| 176 &elems, &write_error)) { | |
| 177 LOG(LS_ERROR) << "Could not write description info message: " | |
| 178 << write_error.text; | |
| 179 return false; | |
| 180 } | |
| 181 SessionError error; | |
| 182 if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) { | |
| 183 LOG(LS_ERROR) << "Could not send description info message: " | |
| 184 << error.text; | |
| 185 return false; | |
| 186 } | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 TransportInfos Session::GetEmptyTransportInfos( | |
| 191 const ContentInfos& contents) const { | |
| 192 TransportInfos tinfos; | |
| 193 for (ContentInfos::const_iterator content = contents.begin(); | |
| 194 content != contents.end(); ++content) { | |
| 195 tinfos.push_back(TransportInfo(content->name, | |
| 196 TransportDescription(transport_type(), | |
| 197 std::string(), | |
| 198 std::string()))); | |
| 199 } | |
| 200 return tinfos; | |
| 201 } | |
| 202 | |
| 203 bool Session::OnRemoteCandidates( | |
| 204 const TransportInfos& tinfos, ParseError* error) { | |
| 205 for (TransportInfos::const_iterator tinfo = tinfos.begin(); | |
| 206 tinfo != tinfos.end(); ++tinfo) { | |
| 207 std::string str_error; | |
| 208 if (!BaseSession::OnRemoteCandidates( | |
| 209 tinfo->content_name, tinfo->description.candidates, &str_error)) { | |
| 210 return BadParse(str_error, error); | |
| 211 } | |
| 212 } | |
| 213 return true; | |
| 214 } | |
| 215 | |
| 216 bool Session::CreateTransportProxies(const TransportInfos& tinfos, | |
| 217 SessionError* error) { | |
| 218 for (TransportInfos::const_iterator tinfo = tinfos.begin(); | |
| 219 tinfo != tinfos.end(); ++tinfo) { | |
| 220 if (tinfo->description.transport_type != transport_type()) { | |
| 221 error->SetText("No supported transport in offer."); | |
| 222 return false; | |
| 223 } | |
| 224 | |
| 225 GetOrCreateTransportProxy(tinfo->content_name); | |
| 226 } | |
| 227 return true; | |
| 228 } | |
| 229 | |
| 230 TransportParserMap Session::GetTransportParsers() { | |
| 231 TransportParserMap parsers; | |
| 232 parsers[transport_type()] = transport_parser_; | |
| 233 return parsers; | |
| 234 } | |
| 235 | |
| 236 CandidateTranslatorMap Session::GetCandidateTranslators() { | |
| 237 CandidateTranslatorMap translators; | |
| 238 // NOTE: This technique makes it impossible to parse G-ICE | |
| 239 // candidates in session-initiate messages because the channels | |
| 240 // aren't yet created at that point. Since we don't use candidates | |
| 241 // in session-initiate messages, we should be OK. Once we switch to | |
| 242 // ICE, this translation shouldn't be necessary. | |
| 243 for (TransportMap::const_iterator iter = transport_proxies().begin(); | |
| 244 iter != transport_proxies().end(); ++iter) { | |
| 245 translators[iter->first] = iter->second; | |
| 246 } | |
| 247 return translators; | |
| 248 } | |
| 249 | |
| 250 ContentParserMap Session::GetContentParsers() { | |
| 251 ContentParserMap parsers; | |
| 252 parsers[content_type()] = client_; | |
| 253 // We need to be able parse both RTP-based and SCTP-based Jingle | |
| 254 // with the same client. | |
| 255 if (content_type() == NS_JINGLE_RTP) { | |
| 256 parsers[NS_JINGLE_DRAFT_SCTP] = client_; | |
| 257 } | |
| 258 return parsers; | |
| 259 } | |
| 260 | |
| 261 void Session::OnTransportRequestSignaling(Transport* transport) { | |
| 262 ASSERT(signaling_thread()->IsCurrent()); | |
| 263 TransportProxy* transproxy = GetTransportProxy(transport); | |
| 264 ASSERT(transproxy != NULL); | |
| 265 if (transproxy) { | |
| 266 // Reset candidate allocation status for the transport proxy. | |
| 267 transproxy->set_candidates_allocated(false); | |
| 268 } | |
| 269 SignalRequestSignaling(this); | |
| 270 } | |
| 271 | |
| 272 void Session::OnTransportConnecting(Transport* transport) { | |
| 273 // This is an indication that we should begin watching the writability | |
| 274 // state of the transport. | |
| 275 OnTransportWritable(transport); | |
| 276 } | |
| 277 | |
| 278 void Session::OnTransportWritable(Transport* transport) { | |
| 279 ASSERT(signaling_thread()->IsCurrent()); | |
| 280 | |
| 281 // If the transport is not writable, start a timer to make sure that it | |
| 282 // becomes writable within a reasonable amount of time. If it does not, we | |
| 283 // terminate since we can't actually send data. If the transport is writable, | |
| 284 // cancel the timer. Note that writability transitions may occur repeatedly | |
| 285 // during the lifetime of the session. | |
| 286 signaling_thread()->Clear(this, MSG_TIMEOUT); | |
| 287 if (transport->HasChannels() && !transport->writable()) { | |
| 288 signaling_thread()->PostDelayed( | |
| 289 session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); | |
| 290 } | |
| 291 } | |
| 292 | |
| 293 void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy, | |
| 294 const Candidates& candidates) { | |
| 295 ASSERT(signaling_thread()->IsCurrent()); | |
| 296 if (transproxy != NULL) { | |
| 297 if (initiator() && !initiate_acked_) { | |
| 298 // TODO: This is to work around server re-ordering | |
| 299 // messages. We send the candidates once the session-initiate | |
| 300 // is acked. Once we have fixed the server to guarantee message | |
| 301 // order, we can remove this case. | |
| 302 transproxy->AddUnsentCandidates(candidates); | |
| 303 } else { | |
| 304 if (!transproxy->negotiated()) { | |
| 305 transproxy->AddSentCandidates(candidates); | |
| 306 } | |
| 307 SessionError error; | |
| 308 if (!SendTransportInfoMessage(transproxy, candidates, &error)) { | |
| 309 LOG(LS_ERROR) << "Could not send transport info message: " | |
| 310 << error.text; | |
| 311 return; | |
| 312 } | |
| 313 } | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 void Session::OnIncomingMessage(const SessionMessage& msg) { | |
| 318 ASSERT(signaling_thread()->IsCurrent()); | |
| 319 ASSERT(state() == STATE_INIT || msg.from == remote_name()); | |
| 320 | |
| 321 if (current_protocol_== PROTOCOL_HYBRID) { | |
| 322 if (msg.protocol == PROTOCOL_GINGLE) { | |
| 323 current_protocol_ = PROTOCOL_GINGLE; | |
| 324 } else { | |
| 325 current_protocol_ = PROTOCOL_JINGLE; | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 bool valid = false; | |
| 330 MessageError error; | |
| 331 switch (msg.type) { | |
| 332 case ACTION_SESSION_INITIATE: | |
| 333 valid = OnInitiateMessage(msg, &error); | |
| 334 break; | |
| 335 case ACTION_SESSION_INFO: | |
| 336 valid = OnInfoMessage(msg); | |
| 337 break; | |
| 338 case ACTION_SESSION_ACCEPT: | |
| 339 valid = OnAcceptMessage(msg, &error); | |
| 340 break; | |
| 341 case ACTION_SESSION_REJECT: | |
| 342 valid = OnRejectMessage(msg, &error); | |
| 343 break; | |
| 344 case ACTION_SESSION_TERMINATE: | |
| 345 valid = OnTerminateMessage(msg, &error); | |
| 346 break; | |
| 347 case ACTION_TRANSPORT_INFO: | |
| 348 valid = OnTransportInfoMessage(msg, &error); | |
| 349 break; | |
| 350 case ACTION_TRANSPORT_ACCEPT: | |
| 351 valid = OnTransportAcceptMessage(msg, &error); | |
| 352 break; | |
| 353 case ACTION_DESCRIPTION_INFO: | |
| 354 valid = OnDescriptionInfoMessage(msg, &error); | |
| 355 break; | |
| 356 default: | |
| 357 valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST, | |
| 358 "unknown session message type", | |
| 359 &error); | |
| 360 } | |
| 361 | |
| 362 if (valid) { | |
| 363 SendAcknowledgementMessage(msg.stanza); | |
| 364 } else { | |
| 365 SignalErrorMessage(this, msg.stanza, error.type, | |
| 366 "modify", error.text, NULL); | |
| 367 } | |
| 368 } | |
| 369 | |
| 370 void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza, | |
| 371 const buzz::XmlElement* response_stanza, | |
| 372 const SessionMessage& msg) { | |
| 373 ASSERT(signaling_thread()->IsCurrent()); | |
| 374 | |
| 375 if (msg.type == ACTION_SESSION_INITIATE) { | |
| 376 OnInitiateAcked(); | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 void Session::OnInitiateAcked() { | |
| 381 // TODO: This is to work around server re-ordering | |
| 382 // messages. We send the candidates once the session-initiate | |
| 383 // is acked. Once we have fixed the server to guarantee message | |
| 384 // order, we can remove this case. | |
| 385 if (!initiate_acked_) { | |
| 386 initiate_acked_ = true; | |
| 387 SessionError error; | |
| 388 SendAllUnsentTransportInfoMessages(&error); | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, | |
| 393 const buzz::XmlElement* error_stanza) { | |
| 394 ASSERT(signaling_thread()->IsCurrent()); | |
| 395 | |
| 396 SessionMessage msg; | |
| 397 ParseError parse_error; | |
| 398 if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) { | |
| 399 LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text | |
| 400 << ":" << orig_stanza; | |
| 401 return; | |
| 402 } | |
| 403 | |
| 404 // If the error is a session redirect, call OnRedirectError, which will | |
| 405 // continue the session with a new remote JID. | |
| 406 SessionRedirect redirect; | |
| 407 if (FindSessionRedirect(error_stanza, &redirect)) { | |
| 408 SessionError error; | |
| 409 if (!OnRedirectError(redirect, &error)) { | |
| 410 // TODO: Should we send a message back? The standard | |
| 411 // says nothing about it. | |
| 412 std::ostringstream desc; | |
| 413 desc << "Failed to redirect: " << error.text; | |
| 414 LOG(LS_ERROR) << desc.str(); | |
| 415 SetError(ERROR_RESPONSE, desc.str()); | |
| 416 } | |
| 417 return; | |
| 418 } | |
| 419 | |
| 420 std::string error_type = "cancel"; | |
| 421 | |
| 422 const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); | |
| 423 if (error) { | |
| 424 error_type = error->Attr(buzz::QN_TYPE); | |
| 425 | |
| 426 LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n" | |
| 427 << "in response to:\n" << orig_stanza->Str(); | |
| 428 } else { | |
| 429 // don't crash if <error> is missing | |
| 430 LOG(LS_ERROR) << "Session error without <error/> element, ignoring"; | |
| 431 return; | |
| 432 } | |
| 433 | |
| 434 if (msg.type == ACTION_TRANSPORT_INFO) { | |
| 435 // Transport messages frequently generate errors because they are sent right | |
| 436 // when we detect a network failure. For that reason, we ignore such | |
| 437 // errors, because if we do not establish writability again, we will | |
| 438 // terminate anyway. The exceptions are transport-specific error tags, | |
| 439 // which we pass on to the respective transport. | |
| 440 } else if ((error_type != "continue") && (error_type != "wait")) { | |
| 441 // We do not set an error if the other side said it is okay to continue | |
| 442 // (possibly after waiting). These errors can be ignored. | |
| 443 SetError(ERROR_RESPONSE, ""); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 bool Session::OnInitiateMessage(const SessionMessage& msg, | |
| 448 MessageError* error) { | |
| 449 if (!CheckState(STATE_INIT, error)) | |
| 450 return false; | |
| 451 | |
| 452 SessionInitiate init; | |
| 453 if (!ParseSessionInitiate(msg.protocol, msg.action_elem, | |
| 454 GetContentParsers(), GetTransportParsers(), | |
| 455 GetCandidateTranslators(), | |
| 456 &init, error)) | |
| 457 return false; | |
| 458 | |
| 459 SessionError session_error; | |
| 460 if (!CreateTransportProxies(init.transports, &session_error)) { | |
| 461 return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE, | |
| 462 session_error.text, error); | |
| 463 } | |
| 464 | |
| 465 set_remote_name(msg.from); | |
| 466 set_initiator_name(msg.initiator); | |
| 467 set_remote_description(new SessionDescription(init.ClearContents(), | |
| 468 init.transports, | |
| 469 init.groups)); | |
| 470 // Updating transport with TransportDescription. | |
| 471 PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL); | |
| 472 SetState(STATE_RECEIVEDINITIATE); | |
| 473 | |
| 474 // Users of Session may listen to state change and call Reject(). | |
| 475 if (state() != STATE_SENTREJECT) { | |
| 476 if (!OnRemoteCandidates(init.transports, error)) | |
| 477 return false; | |
| 478 | |
| 479 // TODO(juberti): Auto-generate and push down the local transport answer. | |
| 480 // This is necessary for trickling to work with RFC 5245 ICE. | |
| 481 } | |
| 482 return true; | |
| 483 } | |
| 484 | |
| 485 bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) { | |
| 486 if (!CheckState(STATE_SENTINITIATE, error)) | |
| 487 return false; | |
| 488 | |
| 489 SessionAccept accept; | |
| 490 if (!ParseSessionAccept(msg.protocol, msg.action_elem, | |
| 491 GetContentParsers(), GetTransportParsers(), | |
| 492 GetCandidateTranslators(), | |
| 493 &accept, error)) { | |
| 494 return false; | |
| 495 } | |
| 496 | |
| 497 // If we get an accept, we can assume the initiate has been | |
| 498 // received, even if we haven't gotten an IQ response. | |
| 499 OnInitiateAcked(); | |
| 500 | |
| 501 set_remote_description(new SessionDescription(accept.ClearContents(), | |
| 502 accept.transports, | |
| 503 accept.groups)); | |
| 504 // Updating transport with TransportDescription. | |
| 505 PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL); | |
| 506 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. | |
| 507 SetState(STATE_RECEIVEDACCEPT); | |
| 508 | |
| 509 if (!OnRemoteCandidates(accept.transports, error)) | |
| 510 return false; | |
| 511 | |
| 512 return true; | |
| 513 } | |
| 514 | |
| 515 bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) { | |
| 516 if (!CheckState(STATE_SENTINITIATE, error)) | |
| 517 return false; | |
| 518 | |
| 519 SetState(STATE_RECEIVEDREJECT); | |
| 520 return true; | |
| 521 } | |
| 522 | |
| 523 bool Session::OnInfoMessage(const SessionMessage& msg) { | |
| 524 SignalInfoMessage(this, msg.action_elem); | |
| 525 return true; | |
| 526 } | |
| 527 | |
| 528 bool Session::OnTerminateMessage(const SessionMessage& msg, | |
| 529 MessageError* error) { | |
| 530 SessionTerminate term; | |
| 531 if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error)) | |
| 532 return false; | |
| 533 | |
| 534 SignalReceivedTerminateReason(this, term.reason); | |
| 535 if (term.debug_reason != buzz::STR_EMPTY) { | |
| 536 LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason; | |
| 537 } | |
| 538 | |
| 539 SetState(STATE_RECEIVEDTERMINATE); | |
| 540 return true; | |
| 541 } | |
| 542 | |
| 543 bool Session::OnTransportInfoMessage(const SessionMessage& msg, | |
| 544 MessageError* error) { | |
| 545 TransportInfos tinfos; | |
| 546 if (!ParseTransportInfos(msg.protocol, msg.action_elem, | |
| 547 initiator_description()->contents(), | |
| 548 GetTransportParsers(), GetCandidateTranslators(), | |
| 549 &tinfos, error)) | |
| 550 return false; | |
| 551 | |
| 552 if (!OnRemoteCandidates(tinfos, error)) | |
| 553 return false; | |
| 554 | |
| 555 return true; | |
| 556 } | |
| 557 | |
| 558 bool Session::OnTransportAcceptMessage(const SessionMessage& msg, | |
| 559 MessageError* error) { | |
| 560 // TODO: Currently here only for compatibility with | |
| 561 // Gingle 1.1 clients (notably, Google Voice). | |
| 562 return true; | |
| 563 } | |
| 564 | |
| 565 bool Session::OnDescriptionInfoMessage(const SessionMessage& msg, | |
| 566 MessageError* error) { | |
| 567 if (!CheckState(STATE_INPROGRESS, error)) | |
| 568 return false; | |
| 569 | |
| 570 DescriptionInfo description_info; | |
| 571 if (!ParseDescriptionInfo(msg.protocol, msg.action_elem, | |
| 572 GetContentParsers(), GetTransportParsers(), | |
| 573 GetCandidateTranslators(), | |
| 574 &description_info, error)) { | |
| 575 return false; | |
| 576 } | |
| 577 | |
| 578 ContentInfos& updated_contents = description_info.contents; | |
| 579 | |
| 580 // TODO: Currently, reflector sends back | |
| 581 // video stream updates even for an audio-only call, which causes | |
| 582 // this to fail. Put this back once reflector is fixed. | |
| 583 // | |
| 584 // ContentInfos::iterator it; | |
| 585 // First, ensure all updates are valid before modifying remote_description_. | |
| 586 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { | |
| 587 // if (remote_description()->GetContentByName(it->name) == NULL) { | |
| 588 // return false; | |
| 589 // } | |
| 590 // } | |
| 591 | |
| 592 // TODO: We used to replace contents from an update, but | |
| 593 // that no longer works with partial updates. We need to figure out | |
| 594 // a way to merge patial updates into contents. For now, users of | |
| 595 // Session should listen to SignalRemoteDescriptionUpdate and handle | |
| 596 // updates. They should not expect remote_description to be the | |
| 597 // latest value. | |
| 598 // | |
| 599 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { | |
| 600 // remote_description()->RemoveContentByName(it->name); | |
| 601 // remote_description()->AddContent(it->name, it->type, it->description); | |
| 602 // } | |
| 603 // } | |
| 604 | |
| 605 SignalRemoteDescriptionUpdate(this, updated_contents); | |
| 606 return true; | |
| 607 } | |
| 608 | |
| 609 bool BareJidsEqual(const std::string& name1, | |
| 610 const std::string& name2) { | |
| 611 buzz::Jid jid1(name1); | |
| 612 buzz::Jid jid2(name2); | |
| 613 | |
| 614 return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2); | |
| 615 } | |
| 616 | |
| 617 bool Session::OnRedirectError(const SessionRedirect& redirect, | |
| 618 SessionError* error) { | |
| 619 MessageError message_error; | |
| 620 if (!CheckState(STATE_SENTINITIATE, &message_error)) { | |
| 621 return BadWrite(message_error.text, error); | |
| 622 } | |
| 623 | |
| 624 if (!BareJidsEqual(remote_name(), redirect.target)) | |
| 625 return BadWrite("Redirection not allowed: must be the same bare jid.", | |
| 626 error); | |
| 627 | |
| 628 // When we receive a redirect, we point the session at the new JID | |
| 629 // and resend the candidates. | |
| 630 set_remote_name(redirect.target); | |
| 631 return (SendInitiateMessage(local_description(), error) && | |
| 632 ResendAllTransportInfoMessages(error)); | |
| 633 } | |
| 634 | |
| 635 bool Session::CheckState(State expected, MessageError* error) { | |
| 636 if (state() != expected) { | |
| 637 // The server can deliver messages out of order/repeated for various | |
| 638 // reasons. For example, if the server does not recive our iq response, | |
| 639 // it could assume that the iq it sent was lost, and will then send | |
| 640 // it again. Ideally, we should implement reliable messaging with | |
| 641 // duplicate elimination. | |
| 642 return BadMessage(buzz::QN_STANZA_NOT_ALLOWED, | |
| 643 "message not allowed in current state", | |
| 644 error); | |
| 645 } | |
| 646 return true; | |
| 647 } | |
| 648 | |
| 649 void Session::SetError(Error error, const std::string& error_desc) { | |
| 650 BaseSession::SetError(error, error_desc); | |
| 651 if (error != ERROR_NONE) | |
| 652 signaling_thread()->Post(this, MSG_ERROR); | |
| 653 } | |
| 654 | |
| 655 void Session::OnMessage(rtc::Message* pmsg) { | |
| 656 // preserve this because BaseSession::OnMessage may modify it | |
| 657 State orig_state = state(); | |
| 658 | |
| 659 BaseSession::OnMessage(pmsg); | |
| 660 | |
| 661 switch (pmsg->message_id) { | |
| 662 case MSG_ERROR: | |
| 663 TerminateWithReason(STR_TERMINATE_ERROR); | |
| 664 break; | |
| 665 | |
| 666 case MSG_STATE: | |
| 667 switch (orig_state) { | |
| 668 case STATE_SENTREJECT: | |
| 669 case STATE_RECEIVEDREJECT: | |
| 670 // Assume clean termination. | |
| 671 Terminate(); | |
| 672 break; | |
| 673 | |
| 674 case STATE_SENTTERMINATE: | |
| 675 case STATE_RECEIVEDTERMINATE: | |
| 676 session_manager_->DestroySession(this); | |
| 677 break; | |
| 678 | |
| 679 default: | |
| 680 // Explicitly ignoring some states here. | |
| 681 break; | |
| 682 } | |
| 683 break; | |
| 684 } | |
| 685 } | |
| 686 | |
| 687 bool Session::SendInitiateMessage(const SessionDescription* sdesc, | |
| 688 SessionError* error) { | |
| 689 SessionInitiate init; | |
| 690 init.contents = sdesc->contents(); | |
| 691 init.transports = GetEmptyTransportInfos(init.contents); | |
| 692 init.groups = sdesc->groups(); | |
| 693 return SendMessage(ACTION_SESSION_INITIATE, init, error); | |
| 694 } | |
| 695 | |
| 696 bool Session::WriteSessionAction( | |
| 697 SignalingProtocol protocol, const SessionInitiate& init, | |
| 698 XmlElements* elems, WriteError* error) { | |
| 699 return WriteSessionInitiate(protocol, init.contents, init.transports, | |
| 700 GetContentParsers(), GetTransportParsers(), | |
| 701 GetCandidateTranslators(), init.groups, | |
| 702 elems, error); | |
| 703 } | |
| 704 | |
| 705 bool Session::SendAcceptMessage(const SessionDescription* sdesc, | |
| 706 SessionError* error) { | |
| 707 XmlElements elems; | |
| 708 if (!WriteSessionAccept(current_protocol_, | |
| 709 sdesc->contents(), | |
| 710 GetEmptyTransportInfos(sdesc->contents()), | |
| 711 GetContentParsers(), GetTransportParsers(), | |
| 712 GetCandidateTranslators(), sdesc->groups(), | |
| 713 &elems, error)) { | |
| 714 return false; | |
| 715 } | |
| 716 return SendMessage(ACTION_SESSION_ACCEPT, elems, error); | |
| 717 } | |
| 718 | |
| 719 bool Session::SendRejectMessage(const std::string& reason, | |
| 720 SessionError* error) { | |
| 721 SessionTerminate term(reason); | |
| 722 return SendMessage(ACTION_SESSION_REJECT, term, error); | |
| 723 } | |
| 724 | |
| 725 bool Session::SendTerminateMessage(const std::string& reason, | |
| 726 SessionError* error) { | |
| 727 SessionTerminate term(reason); | |
| 728 return SendMessage(ACTION_SESSION_TERMINATE, term, error); | |
| 729 } | |
| 730 | |
| 731 bool Session::WriteSessionAction(SignalingProtocol protocol, | |
| 732 const SessionTerminate& term, | |
| 733 XmlElements* elems, WriteError* error) { | |
| 734 WriteSessionTerminate(protocol, term, elems); | |
| 735 return true; | |
| 736 } | |
| 737 | |
| 738 bool Session::SendTransportInfoMessage(const TransportInfo& tinfo, | |
| 739 SessionError* error) { | |
| 740 return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error); | |
| 741 } | |
| 742 | |
| 743 bool Session::SendTransportInfoMessage(const TransportProxy* transproxy, | |
| 744 const Candidates& candidates, | |
| 745 SessionError* error) { | |
| 746 return SendTransportInfoMessage(TransportInfo(transproxy->content_name(), | |
| 747 TransportDescription(transproxy->type(), std::vector<std::string>(), | |
| 748 std::string(), std::string(), ICEMODE_FULL, | |
| 749 CONNECTIONROLE_NONE, NULL, candidates)), error); | |
| 750 } | |
| 751 | |
| 752 bool Session::WriteSessionAction(SignalingProtocol protocol, | |
| 753 const TransportInfo& tinfo, | |
| 754 XmlElements* elems, WriteError* error) { | |
| 755 TransportInfos tinfos; | |
| 756 tinfos.push_back(tinfo); | |
| 757 return WriteTransportInfos(protocol, tinfos, | |
| 758 GetTransportParsers(), GetCandidateTranslators(), | |
| 759 elems, error); | |
| 760 } | |
| 761 | |
| 762 bool Session::ResendAllTransportInfoMessages(SessionError* error) { | |
| 763 for (TransportMap::const_iterator iter = transport_proxies().begin(); | |
| 764 iter != transport_proxies().end(); ++iter) { | |
| 765 TransportProxy* transproxy = iter->second; | |
| 766 if (transproxy->sent_candidates().size() > 0) { | |
| 767 if (!SendTransportInfoMessage( | |
| 768 transproxy, transproxy->sent_candidates(), error)) { | |
| 769 LOG(LS_ERROR) << "Could not resend transport info messages: " | |
| 770 << error->text; | |
| 771 return false; | |
| 772 } | |
| 773 transproxy->ClearSentCandidates(); | |
| 774 } | |
| 775 } | |
| 776 return true; | |
| 777 } | |
| 778 | |
| 779 bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) { | |
| 780 for (TransportMap::const_iterator iter = transport_proxies().begin(); | |
| 781 iter != transport_proxies().end(); ++iter) { | |
| 782 TransportProxy* transproxy = iter->second; | |
| 783 if (transproxy->unsent_candidates().size() > 0) { | |
| 784 if (!SendTransportInfoMessage( | |
| 785 transproxy, transproxy->unsent_candidates(), error)) { | |
| 786 LOG(LS_ERROR) << "Could not send unsent transport info messages: " | |
| 787 << error->text; | |
| 788 return false; | |
| 789 } | |
| 790 transproxy->ClearUnsentCandidates(); | |
| 791 } | |
| 792 } | |
| 793 return true; | |
| 794 } | |
| 795 | |
| 796 bool Session::SendMessage(ActionType type, const XmlElements& action_elems, | |
| 797 SessionError* error) { | |
| 798 return SendMessage(type, action_elems, remote_name(), error); | |
| 799 } | |
| 800 | |
| 801 bool Session::SendMessage(ActionType type, const XmlElements& action_elems, | |
| 802 const std::string& remote_name, SessionError* error) { | |
| 803 rtc::scoped_ptr<buzz::XmlElement> stanza( | |
| 804 new buzz::XmlElement(buzz::QN_IQ)); | |
| 805 | |
| 806 SessionMessage msg(current_protocol_, type, id(), initiator_name()); | |
| 807 msg.to = remote_name; | |
| 808 WriteSessionMessage(msg, action_elems, stanza.get()); | |
| 809 | |
| 810 SignalOutgoingMessage(this, stanza.get()); | |
| 811 return true; | |
| 812 } | |
| 813 | |
| 814 template <typename Action> | |
| 815 bool Session::SendMessage(ActionType type, const Action& action, | |
| 816 SessionError* error) { | |
| 817 rtc::scoped_ptr<buzz::XmlElement> stanza( | |
| 818 new buzz::XmlElement(buzz::QN_IQ)); | |
| 819 if (!WriteActionMessage(type, action, stanza.get(), error)) | |
| 820 return false; | |
| 821 | |
| 822 SignalOutgoingMessage(this, stanza.get()); | |
| 823 return true; | |
| 824 } | |
| 825 | |
| 826 template <typename Action> | |
| 827 bool Session::WriteActionMessage(ActionType type, const Action& action, | |
| 828 buzz::XmlElement* stanza, | |
| 829 WriteError* error) { | |
| 830 if (current_protocol_ == PROTOCOL_HYBRID) { | |
| 831 if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error)) | |
| 832 return false; | |
| 833 if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error)) | |
| 834 return false; | |
| 835 } else { | |
| 836 if (!WriteActionMessage(current_protocol_, type, action, stanza, error)) | |
| 837 return false; | |
| 838 } | |
| 839 return true; | |
| 840 } | |
| 841 | |
| 842 template <typename Action> | |
| 843 bool Session::WriteActionMessage(SignalingProtocol protocol, | |
| 844 ActionType type, const Action& action, | |
| 845 buzz::XmlElement* stanza, WriteError* error) { | |
| 846 XmlElements action_elems; | |
| 847 if (!WriteSessionAction(protocol, action, &action_elems, error)) | |
| 848 return false; | |
| 849 | |
| 850 SessionMessage msg(protocol, type, id(), initiator_name()); | |
| 851 msg.to = remote_name(); | |
| 852 | |
| 853 WriteSessionMessage(msg, action_elems, stanza); | |
| 854 return true; | |
| 855 } | |
| 856 | |
| 857 void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) { | |
| 858 rtc::scoped_ptr<buzz::XmlElement> ack( | |
| 859 new buzz::XmlElement(buzz::QN_IQ)); | |
| 860 ack->SetAttr(buzz::QN_TO, remote_name()); | |
| 861 ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); | |
| 862 ack->SetAttr(buzz::QN_TYPE, "result"); | |
| 863 | |
| 864 SignalOutgoingMessage(this, ack.get()); | |
| 865 } | |
| 866 | |
| 867 SessionManager::SessionManager(PortAllocator *allocator, | |
| 868 rtc::Thread *worker) { | |
| 869 allocator_ = allocator; | |
| 870 signaling_thread_ = rtc::Thread::Current(); | |
| 871 if (worker == NULL) { | |
| 872 worker_thread_ = rtc::Thread::Current(); | |
| 873 } else { | |
| 874 worker_thread_ = worker; | |
| 875 } | |
| 876 timeout_ = 50; | |
| 877 } | |
| 878 | |
| 879 SessionManager::~SessionManager() { | |
| 880 // Note: Session::Terminate occurs asynchronously, so it's too late to | |
| 881 // delete them now. They better be all gone. | |
| 882 ASSERT(session_map_.empty()); | |
| 883 // TerminateAll(); | |
| 884 SignalDestroyed(); | |
| 885 } | |
| 886 | |
| 887 void SessionManager::AddClient(const std::string& content_type, | |
| 888 SessionClient* client) { | |
| 889 ASSERT(client_map_.find(content_type) == client_map_.end()); | |
| 890 client_map_[content_type] = client; | |
| 891 } | |
| 892 | |
| 893 void SessionManager::RemoveClient(const std::string& content_type) { | |
| 894 ClientMap::iterator iter = client_map_.find(content_type); | |
| 895 ASSERT(iter != client_map_.end()); | |
| 896 client_map_.erase(iter); | |
| 897 } | |
| 898 | |
| 899 SessionClient* SessionManager::GetClient(const std::string& content_type) { | |
| 900 ClientMap::iterator iter = client_map_.find(content_type); | |
| 901 return (iter != client_map_.end()) ? iter->second : NULL; | |
| 902 } | |
| 903 | |
| 904 Session* SessionManager::CreateSession(const std::string& local_name, | |
| 905 const std::string& content_type) { | |
| 906 std::string id; | |
| 907 return CreateSession(id, local_name, content_type); | |
| 908 } | |
| 909 | |
| 910 Session* SessionManager::CreateSession(const std::string& id, | |
| 911 const std::string& local_name, | |
| 912 const std::string& content_type) { | |
| 913 std::string sid = | |
| 914 id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id; | |
| 915 return CreateSession(local_name, local_name, sid, content_type, false); | |
| 916 } | |
| 917 | |
| 918 Session* SessionManager::CreateSession( | |
| 919 const std::string& local_name, const std::string& initiator_name, | |
| 920 const std::string& sid, const std::string& content_type, | |
| 921 bool received_initiate) { | |
| 922 SessionClient* client = GetClient(content_type); | |
| 923 ASSERT(client != NULL); | |
| 924 | |
| 925 Session* session = new Session(this, local_name, initiator_name, | |
| 926 sid, content_type, client); | |
| 927 session->SetIdentity(transport_desc_factory_.identity()); | |
| 928 session_map_[session->id()] = session; | |
| 929 session->SignalRequestSignaling.connect( | |
| 930 this, &SessionManager::OnRequestSignaling); | |
| 931 session->SignalOutgoingMessage.connect( | |
| 932 this, &SessionManager::OnOutgoingMessage); | |
| 933 session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage); | |
| 934 SignalSessionCreate(session, received_initiate); | |
| 935 session->client()->OnSessionCreate(session, received_initiate); | |
| 936 return session; | |
| 937 } | |
| 938 | |
| 939 void SessionManager::DestroySession(Session* session) { | |
| 940 if (session != NULL) { | |
| 941 SessionMap::iterator it = session_map_.find(session->id()); | |
| 942 if (it != session_map_.end()) { | |
| 943 SignalSessionDestroy(session); | |
| 944 session->client()->OnSessionDestroy(session); | |
| 945 session_map_.erase(it); | |
| 946 delete session; | |
| 947 } | |
| 948 } | |
| 949 } | |
| 950 | |
| 951 Session* SessionManager::GetSession(const std::string& sid) { | |
| 952 SessionMap::iterator it = session_map_.find(sid); | |
| 953 if (it != session_map_.end()) | |
| 954 return it->second; | |
| 955 return NULL; | |
| 956 } | |
| 957 | |
| 958 void SessionManager::TerminateAll() { | |
| 959 while (session_map_.begin() != session_map_.end()) { | |
| 960 Session* session = session_map_.begin()->second; | |
| 961 session->Terminate(); | |
| 962 } | |
| 963 } | |
| 964 | |
| 965 bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { | |
| 966 return cricket::IsSessionMessage(stanza); | |
| 967 } | |
| 968 | |
| 969 Session* SessionManager::FindSession(const std::string& sid, | |
| 970 const std::string& remote_name) { | |
| 971 SessionMap::iterator iter = session_map_.find(sid); | |
| 972 if (iter == session_map_.end()) | |
| 973 return NULL; | |
| 974 | |
| 975 Session* session = iter->second; | |
| 976 if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name())) | |
| 977 return NULL; | |
| 978 | |
| 979 return session; | |
| 980 } | |
| 981 | |
| 982 void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) { | |
| 983 SessionMessage msg; | |
| 984 ParseError error; | |
| 985 | |
| 986 if (!ParseSessionMessage(stanza, &msg, &error)) { | |
| 987 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
| 988 error.text, NULL); | |
| 989 return; | |
| 990 } | |
| 991 | |
| 992 Session* session = FindSession(msg.sid, msg.from); | |
| 993 if (session) { | |
| 994 session->OnIncomingMessage(msg); | |
| 995 return; | |
| 996 } | |
| 997 if (msg.type != ACTION_SESSION_INITIATE) { | |
| 998 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
| 999 "unknown session", NULL); | |
| 1000 return; | |
| 1001 } | |
| 1002 | |
| 1003 std::string content_type; | |
| 1004 if (!ParseContentType(msg.protocol, msg.action_elem, | |
| 1005 &content_type, &error)) { | |
| 1006 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
| 1007 error.text, NULL); | |
| 1008 return; | |
| 1009 } | |
| 1010 | |
| 1011 if (!GetClient(content_type)) { | |
| 1012 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
| 1013 "unknown content type: " + content_type, NULL); | |
| 1014 return; | |
| 1015 } | |
| 1016 | |
| 1017 session = CreateSession(msg.to, msg.initiator, msg.sid, | |
| 1018 content_type, true); | |
| 1019 session->OnIncomingMessage(msg); | |
| 1020 } | |
| 1021 | |
| 1022 void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza, | |
| 1023 const buzz::XmlElement* response_stanza) { | |
| 1024 if (orig_stanza == NULL || response_stanza == NULL) { | |
| 1025 return; | |
| 1026 } | |
| 1027 | |
| 1028 SessionMessage msg; | |
| 1029 ParseError error; | |
| 1030 if (!ParseSessionMessage(orig_stanza, &msg, &error)) { | |
| 1031 LOG(LS_WARNING) << "Error parsing incoming response: " << error.text | |
| 1032 << ":" << orig_stanza; | |
| 1033 return; | |
| 1034 } | |
| 1035 | |
| 1036 Session* session = FindSession(msg.sid, msg.to); | |
| 1037 if (!session) { | |
| 1038 // Also try the QN_FROM in the response stanza, in case we sent the request | |
| 1039 // to a bare JID but got the response from a full JID. | |
| 1040 std::string ack_from = response_stanza->Attr(buzz::QN_FROM); | |
| 1041 session = FindSession(msg.sid, ack_from); | |
| 1042 } | |
| 1043 if (session) { | |
| 1044 session->OnIncomingResponse(orig_stanza, response_stanza, msg); | |
| 1045 } | |
| 1046 } | |
| 1047 | |
| 1048 void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, | |
| 1049 const buzz::XmlElement* error_stanza) { | |
| 1050 SessionMessage msg; | |
| 1051 ParseError error; | |
| 1052 if (!ParseSessionMessage(orig_stanza, &msg, &error)) { | |
| 1053 return; // TODO: log somewhere? | |
| 1054 } | |
| 1055 | |
| 1056 Session* session = FindSession(msg.sid, msg.to); | |
| 1057 if (session) { | |
| 1058 rtc::scoped_ptr<buzz::XmlElement> synthetic_error; | |
| 1059 if (!error_stanza) { | |
| 1060 // A failed send is semantically equivalent to an error response, so we | |
| 1061 // can just turn the former into the latter. | |
| 1062 synthetic_error.reset( | |
| 1063 CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, | |
| 1064 "cancel", "Recipient did not respond", NULL)); | |
| 1065 error_stanza = synthetic_error.get(); | |
| 1066 } | |
| 1067 | |
| 1068 session->OnFailedSend(orig_stanza, error_stanza); | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, | |
| 1073 const buzz::QName& name, | |
| 1074 const std::string& type, | |
| 1075 const std::string& text, | |
| 1076 const buzz::XmlElement* extra_info) { | |
| 1077 rtc::scoped_ptr<buzz::XmlElement> msg( | |
| 1078 CreateErrorMessage(stanza, name, type, text, extra_info)); | |
| 1079 SignalOutgoingMessage(this, msg.get()); | |
| 1080 } | |
| 1081 | |
| 1082 buzz::XmlElement* SessionManager::CreateErrorMessage( | |
| 1083 const buzz::XmlElement* stanza, | |
| 1084 const buzz::QName& name, | |
| 1085 const std::string& type, | |
| 1086 const std::string& text, | |
| 1087 const buzz::XmlElement* extra_info) { | |
| 1088 buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); | |
| 1089 iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); | |
| 1090 iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); | |
| 1091 iq->SetAttr(buzz::QN_TYPE, "error"); | |
| 1092 | |
| 1093 CopyXmlChildren(stanza, iq); | |
| 1094 | |
| 1095 buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); | |
| 1096 error->SetAttr(buzz::QN_TYPE, type); | |
| 1097 iq->AddElement(error); | |
| 1098 | |
| 1099 // If the error name is not in the standard namespace, we have to first add | |
| 1100 // some error from that namespace. | |
| 1101 if (name.Namespace() != buzz::NS_STANZA) { | |
| 1102 error->AddElement( | |
| 1103 new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); | |
| 1104 } | |
| 1105 error->AddElement(new buzz::XmlElement(name)); | |
| 1106 | |
| 1107 if (extra_info) | |
| 1108 error->AddElement(new buzz::XmlElement(*extra_info)); | |
| 1109 | |
| 1110 if (text.size() > 0) { | |
| 1111 // It's okay to always use English here. This text is for debugging | |
| 1112 // purposes only. | |
| 1113 buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); | |
| 1114 text_elem->SetAttr(buzz::QN_XML_LANG, "en"); | |
| 1115 text_elem->SetBodyText(text); | |
| 1116 error->AddElement(text_elem); | |
| 1117 } | |
| 1118 | |
| 1119 // TODO: Should we include error codes as well for SIP compatibility? | |
| 1120 | |
| 1121 return iq; | |
| 1122 } | |
| 1123 | |
| 1124 void SessionManager::OnOutgoingMessage(Session* session, | |
| 1125 const buzz::XmlElement* stanza) { | |
| 1126 SignalOutgoingMessage(this, stanza); | |
| 1127 } | |
| 1128 | |
| 1129 void SessionManager::OnErrorMessage(BaseSession* session, | |
| 1130 const buzz::XmlElement* stanza, | |
| 1131 const buzz::QName& name, | |
| 1132 const std::string& type, | |
| 1133 const std::string& text, | |
| 1134 const buzz::XmlElement* extra_info) { | |
| 1135 SendErrorMessage(stanza, name, type, text, extra_info); | |
| 1136 } | |
| 1137 | |
| 1138 void SessionManager::OnSignalingReady() { | |
| 1139 for (SessionMap::iterator it = session_map_.begin(); | |
| 1140 it != session_map_.end(); | |
| 1141 ++it) { | |
| 1142 it->second->OnSignalingReady(); | |
| 1143 } | |
| 1144 } | |
| 1145 | |
| 1146 void SessionManager::OnRequestSignaling(Session* session) { | |
| 1147 SignalRequestSignaling(); | |
| 1148 } | |
| 1149 | |
| 1150 } // namespace cricket | |
| OLD | NEW |