summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbcb704366cb5e333a626c18c308c7e0448a8e69f (patch)
treef0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/jabber/jingle/libjingle/talk/p2p/client
downloadtdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz
tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/jabber/jingle/libjingle/talk/p2p/client')
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am11
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc667
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h172
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc545
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h104
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc149
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h85
7 files changed, 1733 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am
new file mode 100644
index 00000000..2bdd95ff
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am
@@ -0,0 +1,11 @@
+libcricketp2pclient_la_SOURCES = sessionclient.cc \
+ basicportallocator.cc \
+ socketmonitor.cc
+
+noinst_HEADERS = basicportallocator.h \
+ sessionclient.h \
+ socketmonitor.h
+
+AM_CPPFLAGS = -I$(srcdir)/../../.. -DLINUX -DPOSIX -DINTERNAL_BUILD
+
+noinst_LTLIBRARIES = libcricketp2pclient.la
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc
new file mode 100644
index 00000000..5192595c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc
@@ -0,0 +1,667 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, 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.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/tcpport.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/helpers.h"
+#include <cassert>
+
+namespace {
+
+const uint32 MSG_CONFIG_START = 1;
+const uint32 MSG_CONFIG_READY = 2;
+const uint32 MSG_ALLOCATE = 3;
+const uint32 MSG_ALLOCATION_PHASE = 4;
+const uint32 MSG_SHAKE = 5;
+
+const uint32 ALLOCATE_DELAY = 250;
+const uint32 ALLOCATION_STEP_DELAY = 1 * 1000;
+
+const int PHASE_UDP = 0;
+const int PHASE_RELAY = 1;
+const int PHASE_TCP = 2;
+const int PHASE_SSLTCP = 3;
+const int kNumPhases = 4;
+
+const float PREF_LOCAL_UDP = 1.0f;
+const float PREF_LOCAL_STUN = 0.9f;
+const float PREF_LOCAL_TCP = 0.8f;
+const float PREF_RELAY = 0.5f;
+
+const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants
+const float RELAY_BACKUP_PREF_MODIFIER = -0.2f;
+
+
+// Returns the phase in which a given local candidate (or rather, the port that
+// gave rise to that local candidate) would have been created.
+int LocalCandidateToPhase(const cricket::Candidate& candidate) {
+ cricket::ProtocolType proto;
+ bool result = cricket::StringToProto(candidate.protocol().c_str(), proto);
+ if (result) {
+ if (candidate.type() == cricket::LOCAL_PORT_TYPE) {
+ switch (proto) {
+ case cricket::PROTO_UDP: return PHASE_UDP;
+ case cricket::PROTO_TCP: return PHASE_TCP;
+ default: assert(false);
+ }
+ } else if (candidate.type() == cricket::STUN_PORT_TYPE) {
+ return PHASE_UDP;
+ } else if (candidate.type() == cricket::RELAY_PORT_TYPE) {
+ switch (proto) {
+ case cricket::PROTO_UDP: return PHASE_RELAY;
+ case cricket::PROTO_TCP: return PHASE_TCP;
+ case cricket::PROTO_SSLTCP: return PHASE_SSLTCP;
+ default: assert(false);
+ }
+ } else {
+ assert(false);
+ }
+ } else {
+ assert(false);
+ }
+ return PHASE_UDP; // reached only with assert failure
+}
+
+const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds
+const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds
+
+int ShakeDelay() {
+ int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1;
+ return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range;
+}
+
+}
+
+namespace cricket {
+
+// Performs the allocation of ports, in a sequenced (timed) manner, for a given
+// network and IP address.
+class AllocationSequence: public MessageHandler {
+public:
+ AllocationSequence(BasicPortAllocatorSession* session,
+ Network* network,
+ PortConfiguration* config);
+ ~AllocationSequence();
+
+ // Determines whether this sequence is operating on an equivalent network
+ // setup to the one given.
+ bool IsEquivalent(Network* network);
+
+ // Starts and stops the sequence. When started, it will continue allocating
+ // new ports on its own timed schedule.
+ void Start();
+ void Stop();
+
+ // MessageHandler:
+ void OnMessage(Message* msg);
+
+ void EnableProtocol(ProtocolType proto);
+ bool ProtocolEnabled(ProtocolType proto) const;
+
+private:
+ BasicPortAllocatorSession* session_;
+ Network* network_;
+ uint32 ip_;
+ PortConfiguration* config_;
+ bool running_;
+ int step_;
+ int step_of_phase_[kNumPhases];
+
+ typedef std::vector<ProtocolType> ProtocolList;
+ ProtocolList protocols_;
+
+ void CreateUDPPorts();
+ void CreateTCPPorts();
+ void CreateStunPorts();
+ void CreateRelayPorts();
+};
+
+
+// BasicPortAllocator
+
+BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager)
+ : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(NULL), relay_address_(NULL) {
+}
+
+BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager, SocketAddress* stun_address, SocketAddress *relay_address)
+ : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(stun_address), relay_address_(relay_address) {
+}
+
+BasicPortAllocator::~BasicPortAllocator() {
+}
+
+int BasicPortAllocator::best_writable_phase() const {
+ // If we are configured with an HTTP proxy, the best bet is to use the relay
+ if ((best_writable_phase_ == -1)
+ && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) {
+ return PHASE_RELAY;
+ }
+ return best_writable_phase_;
+}
+
+PortAllocatorSession *BasicPortAllocator::CreateSession(const std::string &name) {
+ return new BasicPortAllocatorSession(this, name, stun_address_, relay_address_);
+}
+
+void BasicPortAllocator::AddWritablePhase(int phase) {
+ if ((best_writable_phase_ == -1) || (phase < best_writable_phase_))
+ best_writable_phase_ = phase;
+}
+
+// BasicPortAllocatorSession
+
+BasicPortAllocatorSession::BasicPortAllocatorSession(
+ BasicPortAllocator *allocator,
+ const std::string &name)
+ : allocator_(allocator), name_(name), network_thread_(NULL),
+ config_thread_(NULL), allocation_started_(false), running_(false),
+ stun_address_(NULL), relay_address_(NULL) {
+}
+
+BasicPortAllocatorSession::BasicPortAllocatorSession(
+ BasicPortAllocator *allocator,
+ const std::string &name,
+ SocketAddress *stun_address,
+ SocketAddress *relay_address)
+ : allocator_(allocator), name_(name), network_thread_(NULL),
+ config_thread_(NULL), allocation_started_(false), running_(false),
+ stun_address_(stun_address), relay_address_(relay_address) {
+}
+
+BasicPortAllocatorSession::~BasicPortAllocatorSession() {
+ if (config_thread_ != NULL)
+ config_thread_->Clear(this);
+ if (network_thread_ != NULL)
+ network_thread_->Clear(this);
+
+ std::vector<PortData>::iterator it;
+ for (it = ports_.begin(); it != ports_.end(); it++)
+ delete it->port;
+
+ for (uint32 i = 0; i < configs_.size(); ++i)
+ delete configs_[i];
+
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ delete sequences_[i];
+}
+
+void BasicPortAllocatorSession::GetInitialPorts() {
+ network_thread_ = Thread::Current();
+ if (!config_thread_)
+ config_thread_ = network_thread_;
+
+ config_thread_->Post(this, MSG_CONFIG_START);
+
+ if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHAKER)
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+void BasicPortAllocatorSession::StartGetAllPorts() {
+ assert(Thread::Current() == network_thread_);
+ running_ = true;
+ if (allocation_started_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ sequences_[i]->Start();
+ for (size_t i = 0; i < ports_.size(); ++i)
+ ports_[i].port->Start();
+}
+
+void BasicPortAllocatorSession::StopGetAllPorts() {
+ assert(Thread::Current() == network_thread_);
+ running_ = false;
+ network_thread_->Clear(this, MSG_ALLOCATE);
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ sequences_[i]->Stop();
+}
+
+void BasicPortAllocatorSession::OnMessage(Message *message) {
+ switch (message->message_id) {
+ case MSG_CONFIG_START:
+ assert(Thread::Current() == config_thread_);
+ GetPortConfigurations();
+ break;
+
+ case MSG_CONFIG_READY:
+ assert(Thread::Current() == network_thread_);
+ OnConfigReady(static_cast<PortConfiguration*>(message->pdata));
+ break;
+
+ case MSG_ALLOCATE:
+ assert(Thread::Current() == network_thread_);
+ OnAllocate();
+ break;
+
+ case MSG_SHAKE:
+ assert(Thread::Current() == network_thread_);
+ OnShake();
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+void BasicPortAllocatorSession::GetPortConfigurations() {
+ PortConfiguration* config = NULL;
+ if (stun_address_ != NULL)
+ config = new PortConfiguration(*stun_address_,
+ CreateRandomString(16),
+ CreateRandomString(16),
+ "");
+ PortConfiguration::PortList ports;
+ if (relay_address_ != NULL) {
+ ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP));
+ config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER);
+ }
+
+ ConfigReady(config);
+}
+
+void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) {
+ network_thread_->Post(this, MSG_CONFIG_READY, config);
+}
+
+// Adds a configuration to the list.
+void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) {
+ if (config)
+ configs_.push_back(config);
+
+ AllocatePorts();
+}
+
+void BasicPortAllocatorSession::AllocatePorts() {
+ assert(Thread::Current() == network_thread_);
+
+ if (allocator_->proxy().type != PROXY_NONE)
+ Port::set_proxy(allocator_->proxy());
+
+ network_thread_->Post(this, MSG_ALLOCATE);
+}
+
+// For each network, see if we have a sequence that covers it already. If not,
+// create a new sequence to create the appropriate ports.
+void BasicPortAllocatorSession::OnAllocate() {
+ std::vector<Network*> networks;
+ allocator_->network_manager()->GetNetworks(networks);
+
+ for (uint32 i = 0; i < networks.size(); ++i) {
+ if (HasEquivalentSequence(networks[i]))
+ continue;
+
+ PortConfiguration* config = NULL;
+ if (configs_.size() > 0)
+ config = configs_.back();
+
+ AllocationSequence* sequence =
+ new AllocationSequence(this, networks[i], config);
+ if (running_)
+ sequence->Start();
+
+ sequences_.push_back(sequence);
+ }
+
+ allocation_started_ = true;
+ if (running_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+}
+
+bool BasicPortAllocatorSession::HasEquivalentSequence(Network* network) {
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ if (sequences_[i]->IsEquivalent(network))
+ return true;
+ return false;
+}
+
+void BasicPortAllocatorSession::AddAllocatedPort(Port* port,
+ AllocationSequence * seq,
+ float pref,
+ bool prepare_address) {
+ if (!port)
+ return;
+
+ port->set_name(name_);
+ port->set_preference(pref);
+ port->set_generation(generation());
+ PortData data;
+ data.port = port;
+ data.sequence = seq;
+ data.ready = false;
+ ports_.push_back(data);
+ port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady);
+ port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated);
+ port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed);
+ if (prepare_address)
+ port->PrepareAddress();
+ if (running_)
+ port->Start();
+}
+
+void BasicPortAllocatorSession::OnAddressReady(Port *port) {
+ assert(Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator it = std::find(ports_.begin(), ports_.end(), port);
+ assert(it != ports_.end());
+ assert(!it->ready);
+ it->ready = true;
+ SignalPortReady(this, port);
+
+ // Only accumulate the candidates whose protocol has been enabled
+ std::vector<Candidate> candidates;
+ const std::vector<Candidate>& potentials = port->candidates();
+ for (size_t i=0; i<potentials.size(); ++i) {
+ ProtocolType pvalue;
+ if (!StringToProto(potentials[i].protocol().c_str(), pvalue))
+ continue;
+ if (it->sequence->ProtocolEnabled(pvalue)) {
+ candidates.push_back(potentials[i]);
+ }
+ }
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto) {
+ std::vector<Candidate> candidates;
+ for (std::vector<PortData>::iterator it = ports_.begin(); it != ports_.end(); ++it) {
+ if (!it->ready || (it->sequence != seq))
+ continue;
+
+ const std::vector<Candidate>& potentials = it->port->candidates();
+ for (size_t i=0; i<potentials.size(); ++i) {
+ ProtocolType pvalue;
+ if (!StringToProto(potentials[i].protocol().c_str(), pvalue))
+ continue;
+ if (pvalue == proto) {
+ candidates.push_back(potentials[i]);
+ }
+ }
+ }
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void BasicPortAllocatorSession::OnPortDestroyed(Port* port) {
+ assert(Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator iter =
+ find(ports_.begin(), ports_.end(), port);
+ assert(iter != ports_.end());
+ ports_.erase(iter);
+
+ LOG(INFO) << "Removed port from allocator: "
+ << static_cast<int>(ports_.size()) << " remaining";
+}
+
+void BasicPortAllocatorSession::OnConnectionCreated(Port* port, Connection* conn) {
+ conn->SignalStateChange.connect(this, &BasicPortAllocatorSession::OnConnectionStateChange);
+}
+
+void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) {
+ if (conn->write_state() == Connection::STATE_WRITABLE)
+ allocator_->AddWritablePhase(LocalCandidateToPhase(conn->local_candidate()));
+}
+
+void BasicPortAllocatorSession::OnShake() {
+ LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<";
+
+ std::vector<Port*> ports;
+ std::vector<Connection*> connections;
+
+ for (size_t i = 0; i < ports_.size(); ++i) {
+ if (ports_[i].ready)
+ ports.push_back(ports_[i].port);
+ }
+
+ for (size_t i = 0; i < ports.size(); ++i) {
+ Port::AddressMap::const_iterator iter;
+ for (iter = ports[i]->connections().begin();
+ iter != ports[i]->connections().end();
+ ++iter) {
+ connections.push_back(iter->second);
+ }
+ }
+
+ LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and "
+ << (int)connections.size() << " connections";
+
+ for (size_t i = 0; i < connections.size(); ++i)
+ connections[i]->Destroy();
+
+ if (running_ || (ports.size() > 0) || (connections.size() > 0))
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+// AllocationSequence
+
+AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session,
+ Network* network,
+ PortConfiguration* config)
+ : session_(session), network_(network), ip_(network->ip()), config_(config),
+ running_(false), step_(0) {
+
+ // All of the phases up until the best-writable phase so far run in step 0.
+ // The other phases follow sequentially in the steps after that. If there is
+ // no best-writable so far, then only phase 0 occurs in step 0.
+ int last_phase_in_step_zero =
+ _max(0, session->allocator()->best_writable_phase());
+ for (int phase = 0; phase < kNumPhases; ++phase)
+ step_of_phase_[phase] = _max(0, phase - last_phase_in_step_zero);
+
+ // Immediately perform phase 0.
+ OnMessage(NULL);
+}
+
+AllocationSequence::~AllocationSequence() {
+ session_->network_thread()->Clear(this);
+}
+
+bool AllocationSequence::IsEquivalent(Network* network) {
+ return (network == network_) && (ip_ == network->ip());
+}
+
+void AllocationSequence::Start() {
+ running_ = true;
+ session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY,
+ this,
+ MSG_ALLOCATION_PHASE);
+}
+
+void AllocationSequence::Stop() {
+ running_ = false;
+ session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE);
+}
+
+void AllocationSequence::OnMessage(Message* msg) {
+ assert(Thread::Current() == session_->network_thread());
+ if (msg)
+ assert(msg->message_id == MSG_ALLOCATION_PHASE);
+
+ // Perform all of the phases in the current step.
+ for (int phase = 0; phase < kNumPhases; phase++) {
+ if (step_of_phase_[phase] != step_)
+ continue;
+
+ switch (phase) {
+ case PHASE_UDP:
+ LOG(INFO) << "Phase=UDP Step=" << step_;
+ CreateUDPPorts();
+ CreateStunPorts();
+ EnableProtocol(PROTO_UDP);
+ break;
+
+ case PHASE_RELAY:
+ LOG(INFO) << "Phase=RELAY Step=" << step_;
+ CreateRelayPorts();
+ break;
+
+ case PHASE_TCP:
+ LOG(INFO) << "Phase=TCP Step=" << step_;
+ CreateTCPPorts();
+ EnableProtocol(PROTO_TCP);
+ break;
+
+ case PHASE_SSLTCP:
+ LOG(INFO) << "Phase=SSLTCP Step=" << step_;
+ EnableProtocol(PROTO_SSLTCP);
+ break;
+
+ default:
+ // Nothing else we can do.
+ return;
+ }
+ }
+
+ // TODO: use different delays for each stage
+ step_ += 1;
+ if (running_) {
+ session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY,
+ this,
+ MSG_ALLOCATION_PHASE);
+ }
+}
+
+void AllocationSequence::EnableProtocol(ProtocolType proto) {
+ if (!ProtocolEnabled(proto)) {
+ protocols_.push_back(proto);
+ session_->OnProtocolEnabled(this, proto);
+ }
+}
+
+bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const {
+ for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) {
+ if (*it == proto)
+ return true;
+ }
+ return false;
+}
+
+void AllocationSequence::CreateUDPPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_UDP)
+ return;
+
+ Port* port = new UDPPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0));
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP);
+}
+
+void AllocationSequence::CreateTCPPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_TCP)
+ return;
+
+ Port* port = new TCPPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0));
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP);
+}
+
+void AllocationSequence::CreateStunPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_STUN)
+ return;
+
+ if (!config_ || config_->stun_address.IsAny())
+ return;
+
+ Port* port = new StunPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0), config_->stun_address);
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN);
+}
+
+void AllocationSequence::CreateRelayPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY)
+ return;
+
+ if (!config_)
+ return;
+
+ PortConfiguration::RelayList::const_iterator relay;
+ for (relay = config_->relays.begin();
+ relay != config_->relays.end();
+ ++relay) {
+
+ RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0),
+ config_->username, config_->password,
+ config_->magic_cookie);
+ // Note: We must add the allocated port before we add addresses because
+ // the latter will create candidates that need name and preference
+ // settings. However, we also can't prepare the address (normally
+ // done by AddAllocatedPort) until we have these addresses. So we
+ // wait to do that until below.
+ session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier, false);
+
+ // Add the addresses of this protocol.
+ PortConfiguration::PortList::const_iterator relay_port;
+ for (relay_port = relay->ports.begin();
+ relay_port != relay->ports.end();
+ ++relay_port) {
+ port->AddServerAddress(*relay_port);
+ port->AddExternalAddress(*relay_port);
+ }
+
+ // Start fetching an address for this port.
+ port->PrepareAddress();
+ }
+}
+
+// PortConfiguration
+
+PortConfiguration::PortConfiguration(const SocketAddress& sa,
+ const std::string& un,
+ const std::string& pw,
+ const std::string& mc)
+ : stun_address(sa), username(un), password(pw), magic_cookie(mc) {
+}
+
+void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) {
+ RelayServer relay;
+ relay.ports = ports;
+ relay.pref_modifier = pref_modifier;
+ relays.push_back(relay);
+}
+
+bool PortConfiguration::SupportsProtocol(
+ const PortConfiguration::RelayServer& relay, ProtocolType type) {
+ PortConfiguration::PortList::const_iterator relay_port;
+ for (relay_port = relay.ports.begin();
+ relay_port != relay.ports.end();
+ ++relay_port) {
+ if (relay_port->proto == type)
+ return true;
+ }
+ return false;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h
new file mode 100644
index 00000000..0f7b96b4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h
@@ -0,0 +1,172 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, 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.
+ */
+
+#ifndef _BASICPORTALLOCATOR_H_
+#define _BASICPORTALLOCATOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/portallocator.h"
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+class BasicPortAllocator: public PortAllocator {
+public:
+ BasicPortAllocator(NetworkManager* network_manager);
+ BasicPortAllocator(NetworkManager* network_manager, SocketAddress *stun_server, SocketAddress *relay_server);
+ virtual ~BasicPortAllocator();
+
+ NetworkManager* network_manager() { return network_manager_; }
+
+ // Returns the best (highest preference) phase that has produced a port that
+ // produced a writable connection. If no writable connections have been
+ // produced, this returns -1.
+ int best_writable_phase() const;
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name);
+
+ // Called whenever a connection becomes writable with the argument being the
+ // phase that the corresponding port was created in.
+ void AddWritablePhase(int phase);
+
+private:
+ NetworkManager* network_manager_;
+ SocketAddress* stun_address_;
+ SocketAddress* relay_address_;
+ int best_writable_phase_;
+};
+
+struct PortConfiguration;
+class AllocationSequence;
+
+class BasicPortAllocatorSession: public PortAllocatorSession, public MessageHandler {
+public:
+ BasicPortAllocatorSession(BasicPortAllocator *allocator,
+ const std::string &name);
+ BasicPortAllocatorSession(BasicPortAllocator *allocator,
+ const std::string &name,
+ SocketAddress *stun_address,
+ SocketAddress *relay_address);
+ ~BasicPortAllocatorSession();
+
+ BasicPortAllocator* allocator() { return allocator_; }
+ const std::string& name() const { return name_; }
+ Thread* network_thread() { return network_thread_; }
+
+ Thread* config_thread() { return config_thread_; }
+ void set_config_thread(Thread* thread) { config_thread_ = thread; }
+
+ virtual void GetInitialPorts();
+ virtual void StartGetAllPorts();
+ virtual void StopGetAllPorts();
+ virtual bool IsGettingAllPorts() { return running_; }
+
+protected:
+ // Starts the process of getting the port configurations.
+ virtual void GetPortConfigurations();
+
+ // Adds a port configuration that is now ready. Once we have one for each
+ // network (or a timeout occurs), we will start allocating ports.
+ void ConfigReady(PortConfiguration* config);
+
+ // MessageHandler. Can be overriden if message IDs do not conflict.
+ virtual void OnMessage(Message *message);
+
+private:
+ void OnConfigReady(PortConfiguration* config);
+ void OnConfigTimeout();
+ void AllocatePorts();
+ void OnAllocate();
+ bool HasEquivalentSequence(Network* network);
+ void AddAllocatedPort(Port* port, AllocationSequence * seq, float pref, bool prepare_address = true);
+ void OnAddressReady(Port *port);
+ void OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto);
+ void OnPortDestroyed(Port* port);
+ void OnConnectionCreated(Port* port, Connection* conn);
+ void OnConnectionStateChange(Connection* conn);
+ void OnShake();
+
+ BasicPortAllocator *allocator_;
+ std::string name_;
+ Thread* network_thread_;
+ Thread* config_thread_;
+ bool configuration_done_;
+ bool allocation_started_;
+ bool running_; // set when StartGetAllPorts is called
+ std::vector<PortConfiguration*> configs_;
+ std::vector<AllocationSequence*> sequences_;
+ SocketAddress *stun_address_;
+ SocketAddress *relay_address_;
+
+ struct PortData {
+ Port * port;
+ AllocationSequence * sequence;
+ bool ready;
+
+ bool operator==(Port * rhs) const { return (port == rhs); }
+ };
+ std::vector<PortData> ports_;
+
+ friend class AllocationSequence;
+};
+
+// Records configuration information useful in creating ports.
+struct PortConfiguration: public MessageData {
+ SocketAddress stun_address;
+ std::string username;
+ std::string password;
+ std::string magic_cookie;
+
+ typedef std::vector<ProtocolAddress> PortList;
+ struct RelayServer {
+ PortList ports;
+ float pref_modifier; // added to the protocol modifier to get the
+ // preference for this particular server
+ };
+
+ typedef std::vector<RelayServer> RelayList;
+ RelayList relays;
+
+ PortConfiguration(const SocketAddress& stun_address,
+ const std::string& username,
+ const std::string& password,
+ const std::string& magic_cookie);
+
+ // Adds another relay server, with the given ports and modifier, to the list.
+ void AddRelay(const PortList& ports, float pref_modifier);
+
+ // Determines whether the given relay server supports the given protocol.
+ static bool SupportsProtocol(const PortConfiguration::RelayServer& relay,
+ ProtocolType type);
+};
+
+} // namespace cricket
+
+#endif // _BASICPORTALLOCATOR_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc
new file mode 100644
index 00000000..09b38a52
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc
@@ -0,0 +1,545 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, 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.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmllite/xmlprinter.h"
+#include <iostream>
+#undef SetPort
+
+namespace {
+
+// We only allow usernames to be this many characters or fewer.
+const size_t kMaxUsernameSize = 16;
+
+}
+
+namespace cricket {
+
+#if 0
+>>>>>>
+<iq from="..." to="..." type="set" id="27">
+ <session xmlns="http://www.google.com/session" type="initiate" id="Dr45JU8A34DF" initiator="...">
+ <description xmlns="http://www.whoever.com/whatever">
+ ...
+ </description>
+ </session>
+</iq>
+
+<<<<<<
+<iq from="..." to="..." type="result" id="27"/>
+
+>>>>>>
+<iq from="..." to="..." type="set" id="28">
+ <session xmlns="http://www.google.com/session" type="candidates" id="Dr45JU8A34DF" initiator="...">
+ <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/>
+ <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/>
+ <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/>
+ </session>
+</iq>>
+
+<<<<<<
+<iq from="..." to="..." type="result" id="28"/>
+
+#endif
+
+const std::string NS_GOOGLESESSION("http://www.google.com/session");
+const buzz::QName QN_GOOGLESESSION_SESSION(true, NS_GOOGLESESSION, "session");
+const buzz::QName QN_GOOGLESESSION_CANDIDATE(true, NS_GOOGLESESSION, "candidate");
+const buzz::QName QN_GOOGLESESSION_TARGET(true, NS_GOOGLESESSION, "target");
+const buzz::QName QN_GOOGLESESSION_COOKIE(true, NS_GOOGLESESSION, "cookie");
+const buzz::QName QN_GOOGLESESSION_REGARDING(true, NS_GOOGLESESSION, "regarding");
+
+const buzz::QName QN_TYPE(true, buzz::STR_EMPTY, "type");
+const buzz::QName QN_ID(true, buzz::STR_EMPTY, "id");
+const buzz::QName QN_INITIATOR(true, buzz::STR_EMPTY, "initiator");
+const buzz::QName QN_NAME(true, buzz::STR_EMPTY, "name");
+const buzz::QName QN_PORT(true, buzz::STR_EMPTY, "port");
+const buzz::QName QN_NETWORK(true, buzz::STR_EMPTY, "network");
+const buzz::QName QN_GENERATION(true, buzz::STR_EMPTY, "generation");
+const buzz::QName QN_ADDRESS(true, buzz::STR_EMPTY, "address");
+const buzz::QName QN_USERNAME(true, buzz::STR_EMPTY, "username");
+const buzz::QName QN_PASSWORD(true, buzz::STR_EMPTY, "password");
+const buzz::QName QN_PREFERENCE(true, buzz::STR_EMPTY, "preference");
+const buzz::QName QN_PROTOCOL(true, buzz::STR_EMPTY, "protocol");
+const buzz::QName QN_KEY(true, buzz::STR_EMPTY, "key");
+
+class XmlCookie: public SessionMessage::Cookie {
+public:
+ XmlCookie(const buzz::XmlElement* elem)
+ : elem_(new buzz::XmlElement(*elem)) {
+ }
+
+ virtual ~XmlCookie() {
+ delete elem_;
+ }
+
+ const buzz::XmlElement* elem() const { return elem_; }
+
+ virtual Cookie* Copy() {
+ return new XmlCookie(elem_);
+ }
+
+private:
+ buzz::XmlElement* elem_;
+};
+
+SessionClient::SessionClient(SessionManager *session_manager) {
+ session_manager_ = session_manager;
+ session_manager_->SignalSessionCreate.connect(this, &SessionClient::OnSessionCreateSlot);
+ session_manager_->SignalSessionDestroy.connect(this, &SessionClient::OnSessionDestroySlot);
+}
+
+SessionClient::~SessionClient() {
+}
+
+void SessionClient::OnSessionCreateSlot(Session *session, bool received_initiate) {
+ // Does this session belong to this session client?
+ if (session->name() == GetSessionDescriptionName()) {
+ session->SignalOutgoingMessage.connect(this, &SessionClient::OnOutgoingMessage);
+ OnSessionCreate(session, received_initiate);
+ }
+}
+
+void SessionClient::OnSessionDestroySlot(Session *session) {
+ if (session->name() == GetSessionDescriptionName()) {
+ session->SignalOutgoingMessage.disconnect(this);
+ OnSessionDestroy(session);
+ }
+}
+
+bool SessionClient::IsClientStanza(const buzz::XmlElement *stanza) {
+ // Is it a IQ set stanza?
+ if (stanza->Name() != buzz::QN_IQ)
+ return false;
+ if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET)
+ return false;
+
+ // Make sure it has the right child element
+ const buzz::XmlElement* element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ if (element == NULL)
+ return false;
+
+ // Is it one of the allowed types?
+ std::string type;
+ if (element->HasAttr(QN_TYPE)) {
+ type = element->Attr(QN_TYPE);
+ if (type != "initiate" && type != "accept" && type != "modify" &&
+ type != "candidates" && type != "reject" && type != "redirect" &&
+ type != "terminate") {
+ return false;
+ }
+ }
+
+ // Does this client own the session description namespace?
+ buzz::QName qn_session_desc(GetSessionDescriptionName(), "description");
+ const buzz::XmlElement* description = element->FirstNamed(qn_session_desc);
+ if (type == "initiate" || type == "accept" || type == "modify") {
+ if (description == NULL)
+ return false;
+ } else {
+ if (description != NULL)
+ return false;
+ }
+
+ // It's good
+ return true;
+}
+
+void SessionClient::OnIncomingStanza(const buzz::XmlElement *stanza) {
+ SessionMessage message;
+ if (!ParseIncomingMessage(stanza, message))
+ return;
+
+ session_manager_->OnIncomingMessage(message);
+}
+
+void SessionClient::OnFailedSend(const buzz::XmlElement *original_stanza,
+ const buzz::XmlElement *failure_stanza) {
+ SessionMessage message;
+ if (!ParseIncomingMessage(original_stanza, message))
+ return;
+
+ // Note the from/to represents the *original* stanza and not the from/to
+ // on any return path
+ session_manager_->OnIncomingError(message);
+}
+
+bool SessionClient::ParseIncomingMessage(const buzz::XmlElement *stanza,
+ SessionMessage& message) {
+ // Parse stanza into SessionMessage
+ const buzz::XmlElement* element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ std::string type = element->Attr(QN_TYPE);
+ if (type == "initiate" || type == "accept" || type == "modify") {
+ ParseInitiateAcceptModify(stanza, message);
+ } else if (type == "candidates") {
+ ParseCandidates(stanza, message);
+ } else if (type == "reject" || type == "terminate") {
+ ParseRejectTerminate(stanza, message);
+ } else if (type == "redirect") {
+ ParseRedirect(stanza, message);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void SessionClient::ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message) {
+ if (stanza->HasAttr(buzz::QN_FROM))
+ message.set_from(stanza->Attr(buzz::QN_FROM));
+ if (stanza->HasAttr(buzz::QN_TO))
+ message.set_to(stanza->Attr(buzz::QN_TO));
+
+ const buzz::XmlElement *element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ if (element->HasAttr(QN_ID))
+ message.session_id().set_id_str(element->Attr(QN_ID));
+
+ if (element->HasAttr(QN_INITIATOR))
+ message.session_id().set_initiator(element->Attr(QN_INITIATOR));
+
+ std::string type = element->Attr(QN_TYPE);
+ if (type == "initiate") {
+ message.set_type(SessionMessage::TYPE_INITIATE);
+ } else if (type == "accept") {
+ message.set_type(SessionMessage::TYPE_ACCEPT);
+ } else if (type == "modify") {
+ message.set_type(SessionMessage::TYPE_MODIFY);
+ } else if (type == "candidates") {
+ message.set_type(SessionMessage::TYPE_CANDIDATES);
+ } else if (type == "reject") {
+ message.set_type(SessionMessage::TYPE_REJECT);
+ } else if (type == "redirect") {
+ message.set_type(SessionMessage::TYPE_REDIRECT);
+ } else if (type == "terminate") {
+ message.set_type(SessionMessage::TYPE_TERMINATE);
+ } else {
+ assert(false);
+ }
+}
+
+void SessionClient::ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Pull the standard header pieces out
+ ParseHeader(stanza, message);
+
+ // Parse session description
+ const buzz::XmlElement *session
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ buzz::QName qn_session_desc(GetSessionDescriptionName(), "description");
+ const buzz::XmlElement* desc_elem = session->FirstNamed(qn_session_desc);
+ const SessionDescription *description = NULL;
+ if (desc_elem)
+ description = CreateSessionDescription(desc_elem);
+ message.set_name(GetSessionDescriptionName());
+ message.set_description(description);
+}
+
+void SessionClient::ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Pull the standard header pieces out
+ ParseHeader(stanza, message);
+
+ // Parse candidates and session description
+ std::vector<Candidate> candidates;
+ const buzz::XmlElement *element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ const buzz::XmlElement *child = element->FirstElement();
+ while (child != NULL) {
+ if (child->Name() == QN_GOOGLESESSION_CANDIDATE) {
+ Candidate candidate;
+ if (ParseCandidate(child, &candidate))
+ candidates.push_back(candidate);
+ }
+ child = child->NextElement();
+ }
+ message.set_name(GetSessionDescriptionName());
+ message.set_candidates(candidates);
+}
+
+void SessionClient::ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Reject and terminate are very simple
+ ParseHeader(stanza, message);
+}
+
+bool SessionClient::ParseCandidate(const buzz::XmlElement *child,
+ Candidate* candidate) {
+ // Check for all of the required attributes.
+ if (!child->HasAttr(QN_NAME) ||
+ !child->HasAttr(QN_ADDRESS) ||
+ !child->HasAttr(QN_PORT) ||
+ !child->HasAttr(QN_USERNAME) ||
+ !child->HasAttr(QN_PREFERENCE) ||
+ !child->HasAttr(QN_PROTOCOL) ||
+ !child->HasAttr(QN_GENERATION)) {
+ LOG(LERROR) << "Candidate missing required attribute";
+ return false;
+ }
+
+ SocketAddress address;
+ address.SetIP(child->Attr(QN_ADDRESS));
+ std::istringstream ist(child->Attr(QN_PORT));
+ int port;
+ ist >> port;
+ address.SetPort(port);
+
+ if (address.IsAny()) {
+ LOG(LERROR) << "Candidate has address 0";
+ return false;
+ }
+
+ // Always disallow addresses that refer to the local host.
+ if (address.IsLocalIP()) {
+ LOG(LERROR) << "Candidate has local IP address";
+ return false;
+ }
+
+ // Disallow all ports below 1024, except for 80 and 443 on public addresses.
+ if (port < 1024) {
+ if ((port != 80) && (port != 443)) {
+ LOG(LERROR) << "Candidate has port below 1024, not 80 or 443";
+ return false;
+ }
+ if (address.IsPrivateIP()) {
+ LOG(LERROR) << "Candidate has port of 80 or 443 with private IP address";
+ return false;
+ }
+ }
+
+ candidate->set_name(child->Attr(QN_NAME));
+ candidate->set_address(address);
+ candidate->set_username(child->Attr(QN_USERNAME));
+ candidate->set_preference_str(child->Attr(QN_PREFERENCE));
+ candidate->set_protocol(child->Attr(QN_PROTOCOL));
+ candidate->set_generation_str(child->Attr(QN_GENERATION));
+
+ // Check that the username is not too long and does not use any bad chars.
+ if (candidate->username().size() > kMaxUsernameSize) {
+ LOG(LERROR) << "Candidate username is too long";
+ return false;
+ }
+ if (!IsBase64Encoded(candidate->username())) {
+ LOG(LERROR) << "Candidate username has non-base64 encoded characters";
+ return false;
+ }
+
+ // Look for the non-required attributes.
+ if (child->HasAttr(QN_PASSWORD))
+ candidate->set_password(child->Attr(QN_PASSWORD));
+ if (child->HasAttr(QN_TYPE))
+ candidate->set_type(child->Attr(QN_TYPE));
+ if (child->HasAttr(QN_NETWORK))
+ candidate->set_network_name(child->Attr(QN_NETWORK));
+
+ return true;
+}
+
+void SessionClient::ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Pull the standard header pieces out
+ ParseHeader(stanza, message);
+ const buzz::XmlElement *session = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ // Parse the target and cookie.
+
+ const buzz::XmlElement* target = session->FirstNamed(QN_GOOGLESESSION_TARGET);
+ if (target)
+ message.set_redirect_target(target->Attr(QN_NAME));
+
+ const buzz::XmlElement* cookie = session->FirstNamed(QN_GOOGLESESSION_COOKIE);
+ if (cookie)
+ message.set_redirect_cookie(new XmlCookie(cookie));
+}
+
+void SessionClient::OnOutgoingMessage(Session *session, const SessionMessage &message) {
+ // Translate the message into an XMPP stanza
+
+ buzz::XmlElement *result = NULL;
+ switch (message.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ case SessionMessage::TYPE_ACCEPT:
+ case SessionMessage::TYPE_MODIFY:
+ result = TranslateInitiateAcceptModify(message);
+ break;
+
+ case SessionMessage::TYPE_CANDIDATES:
+ result = TranslateCandidates(message);
+ break;
+
+ case SessionMessage::TYPE_REJECT:
+ case SessionMessage::TYPE_TERMINATE:
+ result = TranslateRejectTerminate(message);
+ break;
+
+ case SessionMessage::TYPE_REDIRECT:
+ result = TranslateRedirect(message);
+ break;
+ }
+
+ // Send the stanza. Note that SessionClient is passing on ownership
+ // of result.
+ if (result != NULL) {
+ SignalSendStanza(this, result);
+ }
+}
+
+buzz::XmlElement *SessionClient::TranslateHeader(const SessionMessage &message) {
+ buzz::XmlElement *result = new buzz::XmlElement(buzz::QN_IQ);
+ result->AddAttr(buzz::QN_TO, message.to());
+ result->AddAttr(buzz::QN_TYPE, buzz::STR_SET);
+ buzz::XmlElement *session = new buzz::XmlElement(QN_GOOGLESESSION_SESSION, true);
+ result->AddElement(session);
+ switch (message.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ session->AddAttr(QN_TYPE, "initiate");
+ break;
+ case SessionMessage::TYPE_ACCEPT:
+ session->AddAttr(QN_TYPE, "accept");
+ break;
+ case SessionMessage::TYPE_MODIFY:
+ session->AddAttr(QN_TYPE, "modify");
+ break;
+ case SessionMessage::TYPE_CANDIDATES:
+ session->AddAttr(QN_TYPE, "candidates");
+ break;
+ case SessionMessage::TYPE_REJECT:
+ session->AddAttr(QN_TYPE, "reject");
+ break;
+ case SessionMessage::TYPE_REDIRECT:
+ session->AddAttr(QN_TYPE, "redirect");
+ break;
+ case SessionMessage::TYPE_TERMINATE:
+ session->AddAttr(QN_TYPE, "terminate");
+ break;
+ }
+ session->AddAttr(QN_ID, message.session_id().id_str());
+ session->AddAttr(QN_INITIATOR, message.session_id().initiator());
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateCandidate(const Candidate &candidate) {
+ buzz::XmlElement *result = new buzz::XmlElement(QN_GOOGLESESSION_CANDIDATE);
+ result->AddAttr(QN_NAME, candidate.name());
+ result->AddAttr(QN_ADDRESS, candidate.address().IPAsString());
+ result->AddAttr(QN_PORT, candidate.address().PortAsString());
+ result->AddAttr(QN_USERNAME, candidate.username());
+ result->AddAttr(QN_PASSWORD, candidate.password());
+ result->AddAttr(QN_PREFERENCE, candidate.preference_str());
+ result->AddAttr(QN_PROTOCOL, candidate.protocol());
+ result->AddAttr(QN_TYPE, candidate.type());
+ result->AddAttr(QN_NETWORK, candidate.network_name());
+ result->AddAttr(QN_GENERATION, candidate.generation_str());
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateInitiateAcceptModify(const SessionMessage &message) {
+ // Header info common to all message types
+ buzz::XmlElement *result = TranslateHeader(message);
+ buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ // Candidates
+ assert(message.candidates().size() == 0);
+
+ // Session Description
+ buzz::XmlElement* description = TranslateSessionDescription(message.description());
+ assert(description->Name().LocalPart() == "description");
+ assert(description->Name().Namespace() == GetSessionDescriptionName());
+ session->AddElement(description);
+
+ if (message.redirect_cookie() != NULL) {
+ const buzz::XmlElement* cookie =
+ reinterpret_cast<XmlCookie*>(message.redirect_cookie())->elem();
+ for (const buzz::XmlElement* elem = cookie->FirstElement(); elem; elem = elem->NextElement())
+ session->AddElement(new buzz::XmlElement(*elem));
+ }
+
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateCandidates(const SessionMessage &message) {
+ // Header info common to all message types
+ buzz::XmlElement *result = TranslateHeader(message);
+ buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ // Candidates
+ std::vector<Candidate>::const_iterator it;
+ for (it = message.candidates().begin(); it != message.candidates().end(); it++)
+ session->AddElement(TranslateCandidate(*it));
+
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateRejectTerminate(const SessionMessage &message) {
+ // These messages are simple, and only have a header
+ return TranslateHeader(message);
+}
+
+buzz::XmlElement *SessionClient::TranslateRedirect(const SessionMessage &message) {
+ // Header info common to all message types
+ buzz::XmlElement *result = TranslateHeader(message);
+ buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ assert(message.candidates().size() == 0);
+ assert(message.description() == NULL);
+
+ assert(message.redirect_target().size() > 0);
+ buzz::XmlElement* target = new buzz::XmlElement(QN_GOOGLESESSION_TARGET);
+ target->AddAttr(QN_NAME, message.redirect_target());
+ session->AddElement(target);
+
+ buzz::XmlElement* cookie = new buzz::XmlElement(QN_GOOGLESESSION_COOKIE);
+ session->AddElement(cookie);
+
+ // If the message does not have a redirect cookie, then this is a redirect
+ // initiated by us. We will automatically add a regarding cookie.
+ if (message.redirect_cookie() == NULL) {
+ buzz::XmlElement* regarding = new buzz::XmlElement(QN_GOOGLESESSION_REGARDING);
+ regarding->AddAttr(QN_NAME, GetJid().BareJid().Str());
+ cookie->AddElement(regarding);
+ } else {
+ const buzz::XmlElement* cookie_elem =
+ reinterpret_cast<const XmlCookie*>(message.redirect_cookie())->elem();
+ const buzz::XmlElement* elem;
+ for (elem = cookie_elem->FirstElement(); elem; elem = elem->NextElement())
+ cookie->AddElement(new buzz::XmlElement(*elem));
+ }
+
+ return result;
+}
+
+SessionManager *SessionClient::session_manager() {
+ return session_manager_;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h
new file mode 100644
index 00000000..69a18422
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h
@@ -0,0 +1,104 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, 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.
+ */
+
+#ifndef _SESSIONCLIENT_H_
+#define _SESSIONCLIENT_H_
+
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmessage.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/jid.h"
+namespace cricket {
+
+// Generic XMPP session client. This class knows how to translate
+// a SessionMessage to and from XMPP stanzas. The SessionDescription
+// is a custom description implemented by the client.
+
+// This class knows how to talk to the session manager, however the
+// session manager doesn't have knowledge of a particular SessionClient.
+
+class SessionClient : public sigslot::has_slots<> {
+public:
+ SessionClient(SessionManager *psm);
+ virtual ~SessionClient();
+
+ // Call this method to determine if a stanza is for this session client
+ bool IsClientStanza(const buzz::XmlElement *stanza);
+
+ // Call this method to deliver a stanza to this session client
+ void OnIncomingStanza(const buzz::XmlElement *stanza);
+
+ // Call this whenever an error is recieved in response to an outgoing
+ // session IQ. Include the original stanza and any failure stanza. If
+ // the failure is due to a time out, the failure_stanza should be NULL
+ void OnFailedSend(const buzz::XmlElement* original_stanza,
+ const buzz::XmlElement* failure_stanza);
+
+ SessionManager *session_manager();
+
+ // Implement this method for stanza sending
+ sigslot::signal2<SessionClient*, const buzz::XmlElement*> SignalSendStanza;
+
+protected:
+ // Override these to know when sessions belonging to this client create/destroy
+
+ virtual void OnSessionCreate(Session * /*session*/, bool /*received_initiate*/) {}
+ virtual void OnSessionDestroy(Session * /*session*/) {}
+
+ // Implement these methods for a custom session description
+ virtual const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element) = 0;
+ virtual buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description) = 0;
+ virtual const std::string &GetSessionDescriptionName() = 0;
+ virtual const buzz::Jid &GetJid() const = 0;
+
+ SessionManager *session_manager_;
+
+private:
+ void OnSessionCreateSlot(Session *session, bool received_initiate);
+ void OnSessionDestroySlot(Session *session);
+ void OnOutgoingMessage(Session *session, const SessionMessage &message);
+ void ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message);
+ bool ParseCandidate(const buzz::XmlElement *child, Candidate* candidate);
+ bool ParseIncomingMessage(const buzz::XmlElement *stanza,
+ SessionMessage& message);
+ void ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message);
+ void ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message);
+ void ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message);
+ void ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message);
+ buzz::XmlElement *TranslateHeader(const SessionMessage &message);
+ buzz::XmlElement *TranslateCandidate(const Candidate &candidate);
+ buzz::XmlElement *TranslateInitiateAcceptModify(const SessionMessage &message);
+ buzz::XmlElement *TranslateCandidates(const SessionMessage &message);
+ buzz::XmlElement *TranslateRejectTerminate(const SessionMessage &message);
+ buzz::XmlElement *TranslateRedirect(const SessionMessage &message);
+
+};
+
+} // namespace cricket
+
+#endif // _SESSIONCLIENT_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc
new file mode 100644
index 00000000..dd9fa67c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc
@@ -0,0 +1,149 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, 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 "socketmonitor.h"
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_MONITOR_POLL = 1;
+const uint32 MSG_MONITOR_START = 2;
+const uint32 MSG_MONITOR_STOP = 3;
+const uint32 MSG_MONITOR_SIGNAL = 4;
+
+SocketMonitor::SocketMonitor(P2PSocket *socket, Thread *monitor_thread) {
+ socket_ = socket;
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+SocketMonitor::~SocketMonitor() {
+ socket_->thread()->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void SocketMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 250)
+ rate_ = 250;
+ socket_->thread()->Post(this, MSG_MONITOR_START);
+}
+
+void SocketMonitor::Stop() {
+ socket_->thread()->Post(this, MSG_MONITOR_STOP);
+}
+
+void SocketMonitor::OnMessage(Message *message) {
+ CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ assert(Thread::Current() == socket_->thread());
+ if (!monitoring_) {
+ monitoring_ = true;
+ socket_->SignalConnectionMonitor.connect(this, &SocketMonitor::OnConnectionMonitor);
+ PollSocket(true);
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ assert(Thread::Current() == socket_->thread());
+ if (monitoring_) {
+ monitoring_ = false;
+ socket_->SignalConnectionMonitor.disconnect(this);
+ socket_->thread()->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ assert(Thread::Current() == socket_->thread());
+ PollSocket(true);
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ assert(Thread::Current() == monitoring_thread_);
+ std::vector<ConnectionInfo> infos = connection_infos_;
+ crit_.Leave();
+ SignalUpdate(this, infos);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void SocketMonitor::OnConnectionMonitor(P2PSocket *socket) {
+ CritScope cs(&crit_);
+ if (monitoring_)
+ PollSocket(false);
+}
+
+void SocketMonitor::PollSocket(bool poll) {
+ CritScope cs(&crit_);
+ assert(Thread::Current() == socket_->thread());
+
+ // Gather connection infos
+
+ connection_infos_.clear();
+ const std::vector<Connection *> &connections = socket_->connections();
+ std::vector<Connection *>::const_iterator it;
+ for (it = connections.begin(); it != connections.end(); it++) {
+ Connection *connection = *it;
+ ConnectionInfo info;
+ info.best_connection = socket_->best_connection() == connection;
+ info.readable = connection->read_state() == Connection::STATE_READABLE;
+ info.writable = connection->write_state() == Connection::STATE_WRITABLE;
+ info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT;
+ info.new_connection = false; // connection->new_connection();
+ info.rtt = connection->rtt();
+ info.sent_total_bytes = connection->sent_total_bytes();
+ info.sent_bytes_second = connection->sent_bytes_second();
+ info.recv_total_bytes = connection->recv_total_bytes();
+ info.recv_bytes_second = connection->recv_bytes_second();
+ info.local_candidate = connection->local_candidate();
+ info.remote_candidate = connection->remote_candidate();
+ info.est_quality = connection->port()->network()->quality();
+ info.key = reinterpret_cast<void *>(connection);
+ connection_infos_.push_back(info);
+ }
+
+ // Signal the monitoring thread, start another poll timer
+
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ if (poll)
+ socket_->thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+P2PSocket *SocketMonitor::socket() {
+ return socket_;
+}
+
+Thread *SocketMonitor::monitor_thread() {
+ return monitoring_thread_;
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h
new file mode 100644
index 00000000..549e90b6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, 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.
+ */
+
+#ifndef _SOCKETMONITOR_H_
+#define _SOCKETMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/criticalsection.h"
+#include "talk/p2p/base/p2psocket.h"
+#include "talk/p2p/base/port.h"
+#include <vector>
+
+namespace cricket {
+
+struct ConnectionInfo {
+ bool best_connection;
+ bool writable;
+ bool readable;
+ bool timeout;
+ bool new_connection;
+ size_t rtt;
+ size_t sent_total_bytes;
+ size_t sent_bytes_second;
+ size_t recv_total_bytes;
+ size_t recv_bytes_second;
+ Candidate local_candidate;
+ Candidate remote_candidate;
+ double est_quality;
+ void *key;
+};
+
+class SocketMonitor : public MessageHandler, public sigslot::has_slots<> {
+public:
+ SocketMonitor(P2PSocket *socket, Thread *monitor_thread);
+ ~SocketMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ P2PSocket *socket();
+ Thread *monitor_thread();
+
+ sigslot::signal2<SocketMonitor *, const std::vector<ConnectionInfo> &> SignalUpdate;
+
+protected:
+ void OnMessage(Message *message);
+ void OnConnectionMonitor(P2PSocket *socket);
+ void PollSocket(bool poll);
+
+ std::vector<ConnectionInfo> connection_infos_;
+ P2PSocket *socket_;
+ Thread *monitoring_thread_;
+ CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _SOCKETMONITOR_H_