| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/protocol/jingle_session.h" | 5 #include "remoting/protocol/jingle_session.h" |
| 6 | 6 |
| 7 #include "base/base64.h" |
| 7 #include "base/bind.h" | 8 #include "base/bind.h" |
| 8 #include "base/location.h" | 9 #include "base/location.h" |
| 9 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
| 10 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
| 11 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
| 12 #include "crypto/hmac.h" | 13 #include "crypto/hmac.h" |
| 13 #include "crypto/rsa_private_key.h" | |
| 14 #include "net/base/net_errors.h" | 14 #include "net/base/net_errors.h" |
| 15 #include "net/socket/stream_socket.h" | 15 #include "net/socket/stream_socket.h" |
| 16 #include "remoting/base/constants.h" | 16 #include "remoting/base/constants.h" |
| 17 #include "remoting/protocol/auth_util.h" | 17 #include "remoting/protocol/auth_util.h" |
| 18 #include "remoting/protocol/authenticator.h" |
| 19 #include "remoting/protocol/channel_authenticator.h" |
| 18 #include "remoting/protocol/jingle_datagram_connector.h" | 20 #include "remoting/protocol/jingle_datagram_connector.h" |
| 19 #include "remoting/protocol/jingle_session_manager.h" | 21 #include "remoting/protocol/jingle_session_manager.h" |
| 20 #include "remoting/protocol/jingle_stream_connector.h" | 22 #include "remoting/protocol/jingle_stream_connector.h" |
| 21 #include "remoting/protocol/v1_client_channel_authenticator.h" | |
| 22 #include "remoting/protocol/v1_host_channel_authenticator.h" | |
| 23 #include "third_party/libjingle/source/talk/base/thread.h" | 23 #include "third_party/libjingle/source/talk/base/thread.h" |
| 24 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" | 24 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" |
| 25 #include "third_party/libjingle/source/talk/p2p/base/session.h" | 25 #include "third_party/libjingle/source/talk/p2p/base/session.h" |
| 26 #include "third_party/libjingle/source/talk/p2p/base/transport.h" | 26 #include "third_party/libjingle/source/talk/p2p/base/transport.h" |
| 27 | 27 |
| 28 using cricket::BaseSession; | 28 using cricket::BaseSession; |
| 29 | 29 |
| 30 namespace remoting { | 30 namespace remoting { |
| 31 namespace protocol { | 31 namespace protocol { |
| 32 | 32 |
| 33 // static | |
| 34 JingleSession* JingleSession::CreateClientSession( | |
| 35 JingleSessionManager* manager, const std::string& host_public_key) { | |
| 36 return new JingleSession(manager, "", NULL, host_public_key); | |
| 37 } | |
| 38 | |
| 39 // static | |
| 40 JingleSession* JingleSession::CreateServerSession( | |
| 41 JingleSessionManager* manager, | |
| 42 const std::string& certificate, | |
| 43 crypto::RSAPrivateKey* key) { | |
| 44 return new JingleSession(manager, certificate, key, ""); | |
| 45 } | |
| 46 | |
| 47 JingleSession::JingleSession( | 33 JingleSession::JingleSession( |
| 48 JingleSessionManager* jingle_session_manager, | 34 JingleSessionManager* jingle_session_manager, |
| 49 const std::string& local_cert, | 35 cricket::Session* cricket_session, |
| 50 crypto::RSAPrivateKey* local_private_key, | 36 Authenticator* authenticator) |
| 51 const std::string& peer_public_key) | |
| 52 : jingle_session_manager_(jingle_session_manager), | 37 : jingle_session_manager_(jingle_session_manager), |
| 53 local_cert_(local_cert), | 38 authenticator_(authenticator), |
| 54 state_(INITIALIZING), | 39 state_(INITIALIZING), |
| 55 error_(OK), | 40 error_(OK), |
| 56 closing_(false), | 41 closing_(false), |
| 57 cricket_session_(NULL), | 42 cricket_session_(cricket_session), |
| 58 config_set_(false), | 43 config_set_(false), |
| 59 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { | 44 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { |
| 60 // TODO(hclam): Need a better way to clone a key. | 45 jid_ = cricket_session_->remote_name(); |
| 61 if (local_private_key) { | 46 cricket_session_->SignalState.connect(this, &JingleSession::OnSessionState); |
| 62 std::vector<uint8> key_bytes; | 47 cricket_session_->SignalError.connect(this, &JingleSession::OnSessionError); |
| 63 CHECK(local_private_key->ExportPrivateKey(&key_bytes)); | |
| 64 local_private_key_.reset( | |
| 65 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes)); | |
| 66 CHECK(local_private_key_.get()); | |
| 67 } | |
| 68 } | 48 } |
| 69 | 49 |
| 70 JingleSession::~JingleSession() { | 50 JingleSession::~JingleSession() { |
| 71 // Reset the callback so that it's not called from Close(). | 51 // Reset the callback so that it's not called from Close(). |
| 72 state_change_callback_.Reset(); | 52 state_change_callback_.Reset(); |
| 73 Close(); | 53 Close(); |
| 74 jingle_session_manager_->SessionDestroyed(this); | 54 jingle_session_manager_->SessionDestroyed(this); |
| 75 } | 55 } |
| 76 | 56 |
| 77 void JingleSession::Init(cricket::Session* cricket_session) { | 57 void JingleSession::Initiate() { |
| 78 DCHECK(CalledOnValidThread()); | 58 DCHECK_EQ(authenticator_->state(), Authenticator::MESSAGE_READY); |
| 79 | 59 cricket_session_->Initiate( |
| 80 cricket_session_ = cricket_session; | 60 jid_, CreateSessionDescription(candidate_config()->Clone(), |
| 81 jid_ = cricket_session_->remote_name(); | 61 authenticator_->GetNextMessage())); |
| 82 cricket_session_->SignalState.connect( | |
| 83 this, &JingleSession::OnSessionState); | |
| 84 cricket_session_->SignalError.connect( | |
| 85 this, &JingleSession::OnSessionError); | |
| 86 } | 62 } |
| 87 | 63 |
| 88 void JingleSession::CloseInternal(int result, Error error) { | 64 void JingleSession::CloseInternal(int result, Error error) { |
| 89 DCHECK(CalledOnValidThread()); | 65 DCHECK(CalledOnValidThread()); |
| 90 | 66 |
| 91 if (state_ != FAILED && state_ != CLOSED && !closing_) { | 67 if (state_ != FAILED && state_ != CLOSED && !closing_) { |
| 92 closing_ = true; | 68 closing_ = true; |
| 93 | 69 |
| 94 STLDeleteContainerPairSecondPointers(channel_connectors_.begin(), | 70 STLDeleteContainerPairSecondPointers(channel_connectors_.begin(), |
| 95 channel_connectors_.end()); | 71 channel_connectors_.end()); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 194 } | 170 } |
| 195 | 171 |
| 196 void JingleSession::set_candidate_config( | 172 void JingleSession::set_candidate_config( |
| 197 const CandidateSessionConfig* candidate_config) { | 173 const CandidateSessionConfig* candidate_config) { |
| 198 DCHECK(CalledOnValidThread()); | 174 DCHECK(CalledOnValidThread()); |
| 199 DCHECK(!candidate_config_.get()); | 175 DCHECK(!candidate_config_.get()); |
| 200 DCHECK(candidate_config); | 176 DCHECK(candidate_config); |
| 201 candidate_config_.reset(candidate_config); | 177 candidate_config_.reset(candidate_config); |
| 202 } | 178 } |
| 203 | 179 |
| 204 const std::string& JingleSession::local_certificate() const { | |
| 205 DCHECK(CalledOnValidThread()); | |
| 206 return local_cert_; | |
| 207 } | |
| 208 | |
| 209 const SessionConfig& JingleSession::config() { | 180 const SessionConfig& JingleSession::config() { |
| 210 DCHECK(CalledOnValidThread()); | 181 DCHECK(CalledOnValidThread()); |
| 211 DCHECK(config_set_); | 182 DCHECK(config_set_); |
| 212 return config_; | 183 return config_; |
| 213 } | 184 } |
| 214 | 185 |
| 215 void JingleSession::set_config(const SessionConfig& config) { | 186 void JingleSession::set_config(const SessionConfig& config) { |
| 216 DCHECK(CalledOnValidThread()); | 187 DCHECK(CalledOnValidThread()); |
| 217 DCHECK(!config_set_); | 188 DCHECK(!config_set_); |
| 218 config_ = config; | 189 config_ = config; |
| 219 config_set_ = true; | 190 config_set_ = true; |
| 220 } | 191 } |
| 221 | 192 |
| 222 const std::string& JingleSession::initiator_token() { | |
| 223 DCHECK(CalledOnValidThread()); | |
| 224 return initiator_token_; | |
| 225 } | |
| 226 | |
| 227 void JingleSession::set_initiator_token(const std::string& initiator_token) { | |
| 228 DCHECK(CalledOnValidThread()); | |
| 229 initiator_token_ = initiator_token; | |
| 230 } | |
| 231 | |
| 232 const std::string& JingleSession::receiver_token() { | |
| 233 DCHECK(CalledOnValidThread()); | |
| 234 return receiver_token_; | |
| 235 } | |
| 236 | |
| 237 void JingleSession::set_receiver_token(const std::string& receiver_token) { | |
| 238 DCHECK(CalledOnValidThread()); | |
| 239 receiver_token_ = receiver_token; | |
| 240 } | |
| 241 | |
| 242 void JingleSession::set_shared_secret(const std::string& secret) { | |
| 243 DCHECK(CalledOnValidThread()); | |
| 244 shared_secret_ = secret; | |
| 245 } | |
| 246 | |
| 247 const std::string& JingleSession::shared_secret() { | |
| 248 DCHECK(CalledOnValidThread()); | |
| 249 return shared_secret_; | |
| 250 } | |
| 251 | |
| 252 | |
| 253 void JingleSession::Close() { | 193 void JingleSession::Close() { |
| 254 DCHECK(CalledOnValidThread()); | 194 DCHECK(CalledOnValidThread()); |
| 255 | 195 |
| 256 CloseInternal(net::ERR_CONNECTION_CLOSED, OK); | 196 CloseInternal(net::ERR_CONNECTION_CLOSED, OK); |
| 257 } | 197 } |
| 258 | 198 |
| 259 void JingleSession::OnSessionState( | 199 void JingleSession::OnSessionState( |
| 260 BaseSession* session, BaseSession::State state) { | 200 BaseSession* session, BaseSession::State state) { |
| 261 DCHECK(CalledOnValidThread()); | 201 DCHECK(CalledOnValidThread()); |
| 262 DCHECK_EQ(cricket_session_, session); | 202 DCHECK_EQ(cricket_session_, session); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 303 if (error != cricket::Session::ERROR_NONE) { | 243 if (error != cricket::Session::ERROR_NONE) { |
| 304 // TODO(sergeyu): Report different errors depending on |error|. | 244 // TODO(sergeyu): Report different errors depending on |error|. |
| 305 CloseInternal(net::ERR_CONNECTION_ABORTED, CHANNEL_CONNECTION_ERROR); | 245 CloseInternal(net::ERR_CONNECTION_ABORTED, CHANNEL_CONNECTION_ERROR); |
| 306 } | 246 } |
| 307 } | 247 } |
| 308 | 248 |
| 309 void JingleSession::OnInitiate() { | 249 void JingleSession::OnInitiate() { |
| 310 DCHECK(CalledOnValidThread()); | 250 DCHECK(CalledOnValidThread()); |
| 311 jid_ = cricket_session_->remote_name(); | 251 jid_ = cricket_session_->remote_name(); |
| 312 | 252 |
| 313 if (!cricket_session_->initiator()) { | |
| 314 const protocol::ContentDescription* content_description = | |
| 315 static_cast<const protocol::ContentDescription*>( | |
| 316 GetContentInfo()->description); | |
| 317 CHECK(content_description); | |
| 318 } | |
| 319 | |
| 320 if (cricket_session_->initiator()) { | 253 if (cricket_session_->initiator()) { |
| 321 // Set state to CONNECTING if this is an outgoing message. We need | 254 // Set state to CONNECTING if this is an outgoing message. We need |
| 322 // to post this task because channel creation works only after we | 255 // to post this task because channel creation works only after we |
| 323 // return from this method. This is because | 256 // return from this method. This is because |
| 324 // JingleChannelConnector::Connect() needs to call | 257 // JingleChannelConnector::Connect() needs to call |
| 325 // set_incoming_only() on P2PTransportChannel, but | 258 // set_incoming_only() on P2PTransportChannel, but |
| 326 // P2PTransportChannel is created only after we return from this | 259 // P2PTransportChannel is created only after we return from this |
| 327 // method. | 260 // method. |
| 328 // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. | 261 // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. |
| 329 jingle_session_manager_->message_loop_->PostTask( | 262 jingle_session_manager_->message_loop_->PostTask( |
| (...skipping 10 matching lines...) Expand all Loading... |
| 340 const cricket::SessionDescription* description) { | 273 const cricket::SessionDescription* description) { |
| 341 // We should only be called after ParseContent has succeeded, in which | 274 // We should only be called after ParseContent has succeeded, in which |
| 342 // case there will always be a Chromoting session configuration. | 275 // case there will always be a Chromoting session configuration. |
| 343 const cricket::ContentInfo* content = | 276 const cricket::ContentInfo* content = |
| 344 description->FirstContentByType(kChromotingXmlNamespace); | 277 description->FirstContentByType(kChromotingXmlNamespace); |
| 345 CHECK(content); | 278 CHECK(content); |
| 346 const protocol::ContentDescription* content_description = | 279 const protocol::ContentDescription* content_description = |
| 347 static_cast<const protocol::ContentDescription*>(content->description); | 280 static_cast<const protocol::ContentDescription*>(content->description); |
| 348 CHECK(content_description); | 281 CHECK(content_description); |
| 349 | 282 |
| 350 remote_cert_ = content_description->certificate(); | 283 // Process authenticator message. |
| 351 if (remote_cert_.empty()) { | 284 const buzz::XmlElement* auth_message = |
| 352 LOG(ERROR) << "Connection response does not specify certificate"; | 285 content_description->authenticator_message(); |
| 286 if (!auth_message) { |
| 287 DLOG(WARNING) << "Received session-accept without authentication message " |
| 288 << auth_message->Str(); |
| 353 return false; | 289 return false; |
| 354 } | 290 } |
| 355 | 291 |
| 292 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); |
| 293 authenticator_->ProcessMessage(auth_message); |
| 294 // Support for more than two auth message is not implemented yet. |
| 295 DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE && |
| 296 authenticator_->state() != Authenticator::MESSAGE_READY); |
| 297 |
| 298 if (authenticator_->state() != Authenticator::ACCEPTED) { |
| 299 return false; |
| 300 } |
| 301 |
| 356 SessionConfig config; | 302 SessionConfig config; |
| 357 if (!content_description->config()->GetFinalConfig(&config)) { | 303 if (!content_description->config()->GetFinalConfig(&config)) { |
| 358 LOG(ERROR) << "Connection response does not specify configuration"; | 304 LOG(ERROR) << "Connection response does not specify configuration"; |
| 359 return false; | 305 return false; |
| 360 } | 306 } |
| 361 if (!candidate_config()->IsSupported(config)) { | 307 if (!candidate_config()->IsSupported(config)) { |
| 362 LOG(ERROR) << "Connection response specifies an invalid configuration"; | 308 LOG(ERROR) << "Connection response specifies an invalid configuration"; |
| 363 return false; | 309 return false; |
| 364 } | 310 } |
| 365 | 311 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 384 } | 330 } |
| 385 | 331 |
| 386 void JingleSession::OnTerminate() { | 332 void JingleSession::OnTerminate() { |
| 387 DCHECK(CalledOnValidThread()); | 333 DCHECK(CalledOnValidThread()); |
| 388 CloseInternal(net::ERR_CONNECTION_ABORTED, OK); | 334 CloseInternal(net::ERR_CONNECTION_ABORTED, OK); |
| 389 } | 335 } |
| 390 | 336 |
| 391 void JingleSession::AcceptConnection() { | 337 void JingleSession::AcceptConnection() { |
| 392 SetState(CONNECTING); | 338 SetState(CONNECTING); |
| 393 | 339 |
| 394 if (!jingle_session_manager_->AcceptConnection(this, cricket_session_)) { | 340 const cricket::SessionDescription* session_description = |
| 341 cricket_session_->remote_description(); |
| 342 const cricket::ContentInfo* content = |
| 343 session_description->FirstContentByType(kChromotingXmlNamespace); |
| 344 |
| 345 CHECK(content); |
| 346 const ContentDescription* content_description = |
| 347 static_cast<const ContentDescription*>(content->description); |
| 348 candidate_config_.reset(content_description->config()->Clone()); |
| 349 |
| 350 SessionManager::IncomingSessionResponse response = |
| 351 jingle_session_manager_->AcceptConnection(this); |
| 352 if (response != SessionManager::ACCEPT) { |
| 353 if (response == SessionManager::INCOMPATIBLE) { |
| 354 cricket_session_->TerminateWithReason( |
| 355 cricket::STR_TERMINATE_INCOMPATIBLE_PARAMETERS); |
| 356 } else { |
| 357 cricket_session_->TerminateWithReason(cricket::STR_TERMINATE_DECLINE); |
| 358 } |
| 395 Close(); | 359 Close(); |
| 396 // Release session so that JingleSessionManager::SessionDestroyed() | 360 // Release session so that JingleSessionManager::SessionDestroyed() |
| 397 // doesn't try to call cricket::SessionManager::DestroySession() for it. | 361 // doesn't try to call cricket::SessionManager::DestroySession() for it. |
| 398 ReleaseSession(); | 362 ReleaseSession(); |
| 399 delete this; | 363 delete this; |
| 400 return; | 364 return; |
| 401 } | 365 } |
| 402 | 366 |
| 403 if (!VerifySupportAuthToken(jid_, shared_secret_, initiator_token())) | 367 const buzz::XmlElement* auth_message = |
| 368 content_description->authenticator_message(); |
| 369 if (!auth_message) { |
| 370 DLOG(WARNING) << "Received session-initiate without authenticator message."; |
| 371 CloseInternal(net::ERR_CONNECTION_FAILED, INCOMPATIBLE_PROTOCOL); |
| 372 return; |
| 373 } |
| 374 |
| 375 authenticator_.reset( |
| 376 jingle_session_manager_->CreateAuthenticator(jid(), auth_message)); |
| 377 if (!authenticator_.get()) { |
| 378 CloseInternal(net::ERR_CONNECTION_FAILED, INCOMPATIBLE_PROTOCOL); |
| 379 return; |
| 380 } |
| 381 |
| 382 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); |
| 383 authenticator_->ProcessMessage(auth_message); |
| 384 // Support for more than two auth message is not implemented yet. |
| 385 DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE); |
| 386 if (authenticator_->state() == Authenticator::REJECTED) { |
| 404 CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); | 387 CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); |
| 388 return; |
| 389 } |
| 390 |
| 391 // Connection must be configured by the AcceptConnection() callback. |
| 392 CandidateSessionConfig* candidate_config = |
| 393 CandidateSessionConfig::CreateFrom(config()); |
| 394 |
| 395 buzz::XmlElement* auth_reply = NULL; |
| 396 if (authenticator_->state() == Authenticator::MESSAGE_READY) |
| 397 auth_reply = authenticator_->GetNextMessage(); |
| 398 DCHECK_EQ(authenticator_->state(), Authenticator::ACCEPTED); |
| 399 cricket_session_->Accept( |
| 400 CreateSessionDescription(candidate_config, auth_reply)); |
| 405 } | 401 } |
| 406 | 402 |
| 407 void JingleSession::AddChannelConnector( | 403 void JingleSession::AddChannelConnector( |
| 408 const std::string& name, JingleChannelConnector* connector) { | 404 const std::string& name, JingleChannelConnector* connector) { |
| 409 DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); | 405 DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); |
| 410 | 406 |
| 411 const std::string& content_name = GetContentInfo()->name; | 407 const std::string& content_name = GetContentInfo()->name; |
| 412 cricket::TransportChannel* raw_channel = | 408 cricket::TransportChannel* raw_channel = |
| 413 cricket_session_->CreateChannel(content_name, name); | 409 cricket_session_->CreateChannel(content_name, name); |
| 414 | 410 |
| 415 if (!jingle_session_manager_->allow_nat_traversal_ && | 411 if (!jingle_session_manager_->allow_nat_traversal_ && |
| 416 !cricket_session_->initiator()) { | 412 !cricket_session_->initiator()) { |
| 417 // Don't make outgoing connections from the host to client when | 413 // Don't make outgoing connections from the host to client when |
| 418 // NAT traversal is disabled. | 414 // NAT traversal is disabled. |
| 419 raw_channel->GetP2PChannel()->set_incoming_only(true); | 415 raw_channel->GetP2PChannel()->set_incoming_only(true); |
| 420 } | 416 } |
| 421 | 417 |
| 422 channel_connectors_[name] = connector; | 418 channel_connectors_[name] = connector; |
| 423 ChannelAuthenticator* authenticator; | 419 ChannelAuthenticator* authenticator = |
| 424 if (cricket_session_->initiator()) { | 420 authenticator_->CreateChannelAuthenticator(); |
| 425 authenticator = new V1ClientChannelAuthenticator( | |
| 426 remote_cert_, shared_secret_); | |
| 427 } else { | |
| 428 authenticator = new V1HostChannelAuthenticator( | |
| 429 local_cert_, local_private_key_.get(), shared_secret_); | |
| 430 } | |
| 431 connector->Connect(authenticator, raw_channel); | 421 connector->Connect(authenticator, raw_channel); |
| 432 | 422 |
| 433 // Workaround bug in libjingle - it doesn't connect channels if they | 423 // Workaround bug in libjingle - it doesn't connect channels if they |
| 434 // are created after the session is accepted. See crbug.com/89384. | 424 // are created after the session is accepted. See crbug.com/89384. |
| 435 // TODO(sergeyu): Fix the bug and remove this line. | 425 // TODO(sergeyu): Fix the bug and remove this line. |
| 436 cricket_session_->GetTransport(content_name)->ConnectChannels(); | 426 cricket_session_->GetTransport(content_name)->ConnectChannels(); |
| 437 } | 427 } |
| 438 | 428 |
| 439 void JingleSession::OnChannelConnectorFinished( | 429 void JingleSession::OnChannelConnectorFinished( |
| 440 const std::string& name, JingleChannelConnector* connector) { | 430 const std::string& name, JingleChannelConnector* connector) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 464 if (new_state != state_) { | 454 if (new_state != state_) { |
| 465 DCHECK_NE(state_, CLOSED); | 455 DCHECK_NE(state_, CLOSED); |
| 466 DCHECK_NE(state_, FAILED); | 456 DCHECK_NE(state_, FAILED); |
| 467 | 457 |
| 468 state_ = new_state; | 458 state_ = new_state; |
| 469 if (!state_change_callback_.is_null()) | 459 if (!state_change_callback_.is_null()) |
| 470 state_change_callback_.Run(new_state); | 460 state_change_callback_.Run(new_state); |
| 471 } | 461 } |
| 472 } | 462 } |
| 473 | 463 |
| 464 // static |
| 465 cricket::SessionDescription* JingleSession::CreateSessionDescription( |
| 466 const CandidateSessionConfig* config, |
| 467 const buzz::XmlElement* authenticator_message) { |
| 468 cricket::SessionDescription* desc = new cricket::SessionDescription(); |
| 469 desc->AddContent( |
| 470 ContentDescription::kChromotingContentName, kChromotingXmlNamespace, |
| 471 new ContentDescription(config, authenticator_message)); |
| 472 return desc; |
| 473 } |
| 474 |
| 474 } // namespace protocol | 475 } // namespace protocol |
| 475 } // namespace remoting | 476 } // namespace remoting |
| OLD | NEW |