blob: 855f0db1111591adb51a1637da017ba7294955ef [file] [log] [blame]
/*
* libjingle
* Copyright 2004--2006, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/base/basicdefs.h"
#include "talk/base/basictypes.h"
#include "talk/base/common.h"
#include "talk/base/helpers.h"
#include "talk/base/logging.h"
#include "talk/base/stringutils.h"
#include "talk/p2p/base/transportchannel.h"
#include "talk/xmllite/xmlelement.h"
#include "pseudotcpchannel.h"
#include "tunnelsessionclient.h"
namespace cricket {
const std::string NS_TUNNEL("http://www.google.com/talk/tunnel");
const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description");
const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type");
enum {
MSG_CLOCK = 1,
MSG_DESTROY,
MSG_TERMINATE,
MSG_EVENT,
MSG_CREATE_TUNNEL,
};
struct EventData : public talk_base::MessageData {
int event, error;
EventData(int ev, int err = 0) : event(ev), error(err) { }
};
struct CreateTunnelData : public talk_base::MessageData {
buzz::Jid jid;
std::string description;
talk_base::Thread* thread;
talk_base::StreamInterface* stream;
};
extern const talk_base::ConstantLabel SESSION_STATES[];
const talk_base::ConstantLabel SESSION_STATES[] = {
KLABEL(Session::STATE_INIT),
KLABEL(Session::STATE_SENTINITIATE),
KLABEL(Session::STATE_RECEIVEDINITIATE),
KLABEL(Session::STATE_SENTACCEPT),
KLABEL(Session::STATE_RECEIVEDACCEPT),
KLABEL(Session::STATE_SENTMODIFY),
KLABEL(Session::STATE_RECEIVEDMODIFY),
KLABEL(Session::STATE_SENTREJECT),
KLABEL(Session::STATE_RECEIVEDREJECT),
KLABEL(Session::STATE_SENTREDIRECT),
KLABEL(Session::STATE_SENTTERMINATE),
KLABEL(Session::STATE_RECEIVEDTERMINATE),
KLABEL(Session::STATE_INPROGRESS),
KLABEL(Session::STATE_DEINIT),
LASTLABEL
};
///////////////////////////////////////////////////////////////////////////////
// TunnelSessionDescription
///////////////////////////////////////////////////////////////////////////////
struct TunnelSessionDescription : public SessionDescription {
std::string description;
TunnelSessionDescription(const std::string& desc) : description(desc) { }
};
///////////////////////////////////////////////////////////////////////////////
// TunnelSessionClient
///////////////////////////////////////////////////////////////////////////////
TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
SessionManager* manager)
: jid_(jid), session_manager_(manager), shutdown_(false) {
// Register ourselves as the handler of tunnel sessions.
session_manager_->AddClient(NS_TUNNEL, this);
}
TunnelSessionClient::~TunnelSessionClient() {
shutdown_ = true;
for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
it != sessions_.end();
++it) {
Session* session = (*it)->ReleaseSession(true);
session_manager_->DestroySession(session);
}
session_manager_->RemoveClient(NS_TUNNEL);
}
const SessionDescription* TunnelSessionClient::CreateSessionDescription(
const buzz::XmlElement* element) {
if (const buzz::XmlElement* type_elem = element->FirstNamed(QN_TUNNEL_TYPE)) {
return new TunnelSessionDescription(type_elem->BodyText());
}
ASSERT(false);
return 0;
}
buzz::XmlElement* TunnelSessionClient::TranslateSessionDescription(
const SessionDescription* description) {
const TunnelSessionDescription* desc =
static_cast<const TunnelSessionDescription*>(description);
buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
type_elem->SetBodyText(desc->description);
root->AddElement(type_elem);
return root;
}
void TunnelSessionClient::OnSessionCreate(Session* session, bool received) {
LOG(LS_INFO) << "TunnelSessionClient::OnSessionCreate: received=" << received;
ASSERT(session_manager_->signaling_thread()->IsCurrent());
if (received)
sessions_.push_back(
new TunnelSession(this, session, talk_base::Thread::Current()));
}
void TunnelSessionClient::OnSessionDestroy(Session* session) {
LOG(LS_INFO) << "TunnelSessionClient::OnSessionDestroy";
ASSERT(session_manager_->signaling_thread()->IsCurrent());
if (shutdown_)
return;
for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
it != sessions_.end();
++it) {
if ((*it)->HasSession(session)) {
VERIFY((*it)->ReleaseSession(false) == session);
sessions_.erase(it);
return;
}
}
}
talk_base::StreamInterface* TunnelSessionClient::CreateTunnel(
const buzz::Jid& to, const std::string& description) {
// Valid from any thread
CreateTunnelData data;
data.jid = to;
data.description = description;
data.thread = talk_base::Thread::Current();
session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
return data.stream;
}
talk_base::StreamInterface* TunnelSessionClient::AcceptTunnel(
Session* session) {
ASSERT(session_manager_->signaling_thread()->IsCurrent());
TunnelSession* tunnel = NULL;
for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
it != sessions_.end();
++it) {
if ((*it)->HasSession(session)) {
tunnel = *it;
break;
}
}
ASSERT(tunnel != NULL);
const TunnelSessionDescription* in_desc =
static_cast<const TunnelSessionDescription*>(
session->remote_description());
TunnelSessionDescription* out_desc = new TunnelSessionDescription(
in_desc->description);
session->Accept(out_desc);
return tunnel->GetStream();
}
void TunnelSessionClient::DeclineTunnel(Session* session) {
ASSERT(session_manager_->signaling_thread()->IsCurrent());
session->Reject();
}
void TunnelSessionClient::OnMessage(talk_base::Message* pmsg) {
if (pmsg->message_id == MSG_CREATE_TUNNEL) {
ASSERT(session_manager_->signaling_thread()->IsCurrent());
CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
Session* session = session_manager_->CreateSession(jid_.Str(), NS_TUNNEL);
TunnelSession* tunnel = new TunnelSession(this, session, data->thread);
sessions_.push_back(tunnel);
TunnelSessionDescription* desc = new TunnelSessionDescription(data->description);
session->Initiate(data->jid.Str(), NULL, desc);
data->stream = tunnel->GetStream();
}
}
///////////////////////////////////////////////////////////////////////////////
// TunnelSession
///////////////////////////////////////////////////////////////////////////////
//
// Signalling thread methods
//
TunnelSession::TunnelSession(TunnelSessionClient* client, Session* session,
talk_base::Thread* stream_thread)
: client_(client), session_(session), channel_(NULL) {
ASSERT(client_ != NULL);
ASSERT(session_ != NULL);
session_->SignalState.connect(this, &TunnelSession::OnSessionState);
channel_ = new PseudoTcpChannel(stream_thread, session_);
channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
}
TunnelSession::~TunnelSession() {
ASSERT(client_ != NULL);
ASSERT(session_ == NULL);
ASSERT(channel_ == NULL);
}
talk_base::StreamInterface* TunnelSession::GetStream() {
ASSERT(channel_ != NULL);
return channel_->GetStream();
}
bool TunnelSession::HasSession(Session* session) {
ASSERT(NULL != session_);
return (session_ == session);
}
Session* TunnelSession::ReleaseSession(bool channel_exists) {
ASSERT(NULL != session_);
ASSERT(NULL != channel_);
Session* session = session_;
session_->SignalState.disconnect(this);
session_ = NULL;
if (channel_exists)
channel_->SignalChannelClosed.disconnect(this);
channel_ = NULL;
delete this;
return session;
}
void TunnelSession::OnSessionState(Session* session, Session::State state) {
LOG(LS_INFO) << "TunnelSession::OnSessionState("
<< talk_base::nonnull(
talk_base::FindLabel(state, SESSION_STATES), "Unknown")
<< ")";
ASSERT(session == session_);
switch (state) {
case Session::STATE_RECEIVEDINITIATE:
OnInitiate();
break;
case Session::STATE_SENTACCEPT:
case Session::STATE_RECEIVEDACCEPT:
OnAccept();
break;
case Session::STATE_SENTTERMINATE:
case Session::STATE_RECEIVEDTERMINATE:
OnTerminate();
break;
case Session::STATE_DEINIT:
// ReleaseSession should have been called before this.
ASSERT(false);
break;
}
}
void TunnelSession::OnInitiate() {
const TunnelSessionDescription* in_desc =
static_cast<const TunnelSessionDescription*>(
session_->remote_description());
ASSERT(client_ != NULL);
ASSERT(session_ != NULL);
client_->SignalIncomingTunnel(client_,
buzz::Jid(session_->remote_name()),
in_desc->description,
session_);
}
void TunnelSession::OnAccept() {
ASSERT(channel_ != NULL);
VERIFY(channel_->Connect("tcp"));
}
void TunnelSession::OnTerminate() {
}
void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
ASSERT(channel_ == channel);
ASSERT(session_ != NULL);
session_->Terminate();
}
///////////////////////////////////////////////////////////////////////////////
} // namespace cricket