mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-09-24 15:54:12 +00:00
862 lines
24 KiB
C++
862 lines
24 KiB
C++
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#endif
|
|
|
|
#include "protocol_shoutcastClient.h"
|
|
#include "stats.h"
|
|
#include "agentList.h"
|
|
#include "services/stdServiceImpl.h"
|
|
#include <map>
|
|
|
|
using namespace std;
|
|
using namespace uniString;
|
|
using namespace stringUtil;
|
|
|
|
static AOL_namespace::mutex g_statLock;
|
|
|
|
#define DEBUG_LOG(...) do { if (gOptions.statsDebug()) DLOG(__VA_ARGS__); } while (0)
|
|
|
|
#define LOGNAME "[STATS] "
|
|
|
|
const utf8 EMPTY_AGENT("**EMPTY**");
|
|
|
|
struct statTableEntry_t
|
|
{
|
|
size_t m_peakListeners;
|
|
size_t m_totalStreamHits;
|
|
typedef map<size_t, stats::clientData_t*> clientEntry_t;
|
|
clientEntry_t m_clientData;
|
|
|
|
statTableEntry_t() : m_peakListeners(0), m_totalStreamHits(0) {}
|
|
};
|
|
|
|
typedef map<streamData::streamID_t,statTableEntry_t> streamStatTable_t;
|
|
|
|
static streamStatTable_t g_streamStatTable;
|
|
static size_t g_totalClients;
|
|
static size_t g_uniqueClientId;
|
|
|
|
const size_t stats::getNewClientId()
|
|
{
|
|
return ++g_uniqueClientId;
|
|
}
|
|
|
|
static bool _kickNonRipClientFromStream(const streamData::streamID_t id) throw()
|
|
{
|
|
bool kicked = false;
|
|
|
|
// first try our stream
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
statTableEntry_t::clientEntry_t &ce = ste.m_clientData;
|
|
|
|
for (statTableEntry_t::clientEntry_t::iterator ci = ce.begin(); (ci != ce.end()) && (!kicked); ++ci)
|
|
{
|
|
if ((!(*ci).second->m_kicked) && (!(*ci).second->m_ripClient))
|
|
{
|
|
(*ci).second->m_kicked = true;
|
|
if ((*ci).second->m_client)
|
|
{
|
|
(*ci).second->m_client->kickNextRound();
|
|
}
|
|
kicked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return kicked;
|
|
}
|
|
|
|
static bool _kickRandomNonRipClient(const streamData::streamID_t id, const bool anyStream) throw()
|
|
{
|
|
bool kicked = _kickNonRipClientFromStream(id);
|
|
if ((!kicked) && anyStream)
|
|
{
|
|
for (streamStatTable_t::const_iterator i = g_streamStatTable.begin(); (i != g_streamStatTable.end()) && (!kicked); ++i)
|
|
{
|
|
kicked = _kickNonRipClientFromStream((*i).first);
|
|
}
|
|
}
|
|
|
|
return kicked;
|
|
}
|
|
|
|
const int stats::addClient(const streamData::streamID_t id, const utf8 &hostName,
|
|
const utf8 &ipAddr, const utf8 &userAgent, const bool ripClient,
|
|
const size_t clientUnique, protocol_shoutcastClient *client)
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
// to prevent some of the stats / logs getting skewed
|
|
// then we filter out the SHOUTcast site 'test' users
|
|
if (isUserAgentOfficial(toLower(userAgent)))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
statTableEntry_t &ste = g_streamStatTable[id];
|
|
++ste.m_totalStreamHits;
|
|
|
|
if (ste.m_clientData.find(clientUnique) == ste.m_clientData.end())
|
|
{
|
|
// seen this crop up a bit and seem dodgy so rejecting (may change based on usage feedback)
|
|
if (userAgent.empty() && gOptions.blockEmptyUserAgent())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// this will check against a blocked 'user agent' list
|
|
// so we can give stations a means to block bad clients
|
|
// e.g. Winamp/5.0 or Bass/2.x or something like that
|
|
if (!userAgent.empty() && g_agentList.find(userAgent, ((gOptions.read_stream_agentFile(id) && !gOptions.stream_agentFile(id).empty()) ? id : 0)))
|
|
{
|
|
return -2;
|
|
}
|
|
|
|
streamData::streamInfo info;
|
|
streamData::extraInfo extra;
|
|
streamData::getStreamInfo(id, info, extra);
|
|
const int _maxUser = gOptions.maxUser(),
|
|
maxUsers = ((info.m_streamMaxUser > 0) && (info.m_streamMaxUser < _maxUser) ? info.m_streamMaxUser : _maxUser);
|
|
|
|
const size_t num_clients = ste.m_clientData.size();
|
|
const bool over = ((maxUsers && ((int)num_clients >= maxUsers)) ||
|
|
(_maxUser && ((int)g_totalClients >= _maxUser)));
|
|
if (over && !ripClient)
|
|
{
|
|
return 0; // too many, and we are not allowed to boot anyone
|
|
}
|
|
|
|
if (over && ripClient) // too many, and we are allowed to try to boot someone...
|
|
{
|
|
// if total reserved is already at the listener limit then nothing else to do
|
|
if (getTotalRipClients(id) < num_clients)
|
|
{
|
|
// we only allow the new listener to join as long as we can kick someone
|
|
if (!_kickRandomNonRipClient(id, true))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ste.m_clientData[clientUnique] = new stats::clientData_t(hostName, ipAddr, ripClient, client);
|
|
ste.m_peakListeners = max(ste.m_clientData.size(), ste.m_peakListeners);
|
|
++g_totalClients;
|
|
|
|
DEBUG_LOG(LOGNAME "System wide listener total now " + tos(g_totalClients));
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// hacky, to maintain a count in here, should be one in sd
|
|
long stats::getUserCount (const streamData::streamID_t id)
|
|
{
|
|
long c = 0;
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
statTableEntry_t &ste = g_streamStatTable[id];
|
|
c = ste.m_clientData.size();
|
|
}
|
|
return c;
|
|
}
|
|
|
|
|
|
void stats::removeClient(const streamData::streamID_t id, const size_t clientUnique)
|
|
{
|
|
if ((id > 0) && (g_totalClients > 0))
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.find(clientUnique);
|
|
if (i != ste.m_clientData.end())
|
|
{
|
|
delete i->second;
|
|
ste.m_clientData.erase(clientUnique);
|
|
|
|
g_totalClients -= 1;
|
|
DEBUG_LOG(LOGNAME "System wide listener total now " + tos(g_totalClients));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get stats. Some values, like newSessions and newConnects set a flag so that clients are only counted once.
|
|
// This is fine for touch reporting, but sometimes we want to fetch this information and, since we are not touching YP,
|
|
// do not want to set the flags indicating that things have been counted. The "resetAccumulators" flag controls this.
|
|
// Set to true for touch, false otherwise
|
|
void stats::getStats(streamData::streamID_t id, statsData_t &data, bool resetAccumulators) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
|
|
data.peakListeners = ste.m_peakListeners;
|
|
data.totalStreamHits = ste.m_totalStreamHits;
|
|
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
set<utf8> ipTable;
|
|
|
|
time_t t = ::time(NULL);
|
|
data.connectedListeners = ste.m_clientData.size();
|
|
__uint64 total_listen_time = 0;
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
if (ipTable.find((*i).second->m_ipAddr) == ipTable.end())
|
|
{
|
|
++data.uniqueListeners;
|
|
}
|
|
ipTable.insert((*i).second->m_ipAddr);
|
|
}
|
|
|
|
total_listen_time += (t - (*i).second->m_client->getStartTime());
|
|
if (!(*i).second->m_connectCounted)
|
|
{
|
|
if (resetAccumulators)
|
|
{
|
|
(*i).second->m_connectCounted = true;
|
|
}
|
|
++data.newConnects;
|
|
}
|
|
|
|
if ((!(*i).second->m_fiveMinuteCumeCounted) && ((t - (*i).second->m_client->getStartTime()) > (5 * 60)))
|
|
{
|
|
if (resetAccumulators)
|
|
{
|
|
(*i).second->m_fiveMinuteCumeCounted = true;
|
|
}
|
|
++data.newSessions;
|
|
}
|
|
}
|
|
|
|
data.avgUserListenTime = (data.connectedListeners ? (int)(total_listen_time / data.connectedListeners) : 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stats::getFinalStats() throw()
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
size_t totalPeak = 0;
|
|
utf8 msg;
|
|
|
|
if (!g_streamStatTable.empty())
|
|
{
|
|
for (streamStatTable_t::const_iterator ti = g_streamStatTable.begin(); ti != g_streamStatTable.end(); ++ti)
|
|
{
|
|
size_t peakListeners = (*ti).second.m_peakListeners;
|
|
totalPeak += peakListeners;
|
|
msg += (msg.size() > 0 ? ",#" : "#") + tos((*ti).first) + ":" + tos(peakListeners);
|
|
}
|
|
}
|
|
|
|
if (totalPeak > 0)
|
|
{
|
|
msg += (!g_streamStatTable.empty() ? "," : (utf8)"") + "Total: " + tos(totalPeak);
|
|
ILOG(LOGNAME "Peak numbers: " + msg);
|
|
}
|
|
}
|
|
|
|
const streamData::streamIDs_t stats::getActiveStreamIds() throw()
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
streamData::streamIDs_t activeIds;
|
|
|
|
for (streamStatTable_t::const_iterator ti = g_streamStatTable.begin(); ti != g_streamStatTable.end(); ++ti)
|
|
{
|
|
// we check this to make sure that we're only
|
|
// adding 'active' details and skip inactive
|
|
const statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
if (activeIds.find((*ti).first) == activeIds.end())
|
|
{
|
|
activeIds.insert((*ti).first);
|
|
}
|
|
}
|
|
}
|
|
|
|
return activeIds;
|
|
}
|
|
|
|
const size_t stats::getTotalUniqueListeners() throw()
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
size_t uniqueListeners = 0;
|
|
|
|
set<utf8> ipTable;
|
|
|
|
for (streamStatTable_t::const_iterator ti = g_streamStatTable.begin(); ti != g_streamStatTable.end(); ++ti)
|
|
{
|
|
const statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
if (ipTable.find((*i).second->m_ipAddr) == ipTable.end())
|
|
{
|
|
++uniqueListeners;
|
|
}
|
|
ipTable.insert((*i).second->m_ipAddr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return uniqueListeners;
|
|
}
|
|
|
|
const size_t stats::getTotalRipClients(streamData::streamID_t id) throw()
|
|
{
|
|
size_t uniqueListeners = 0;
|
|
|
|
if (id > 0)
|
|
{
|
|
streamStatTable_t::const_iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
const statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked && (*i).second->m_ripClient)
|
|
{
|
|
++uniqueListeners;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return uniqueListeners;
|
|
}
|
|
|
|
void stats::updateRipClients(const streamData::streamID_t id, const uniString::utf8& ripAddr, const bool mode)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
if (id)
|
|
{
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked && ((*i).second->m_ipAddr == ripAddr))
|
|
{
|
|
(*i).second->m_ripClient = mode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (streamStatTable_t::iterator i = g_streamStatTable.begin(); i != g_streamStatTable.end(); ++i)
|
|
{
|
|
statTableEntry_t &ste = (*i).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator c = ste.m_clientData.begin(); c != ste.m_clientData.end(); ++c)
|
|
{
|
|
if (!(*c).second->m_kicked && ((*c).second->m_ipAddr == ripAddr))
|
|
{
|
|
(*c).second->m_ripClient = mode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stats::resetStats(const streamData::streamID_t id) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
// reset peak and stream hits to current client connection level
|
|
ste.m_totalStreamHits = ste.m_peakListeners = ste.m_clientData.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
void stats::updatePeak(const streamData::streamID_t id, const size_t peakListeners) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
statTableEntry_t &ste = ((ti != g_streamStatTable.end()) ? (*ti).second : g_streamStatTable[id]);
|
|
ste.m_peakListeners = max(peakListeners, ste.m_peakListeners);
|
|
}
|
|
}
|
|
|
|
void stats::updateTriggers(const streamData::streamID_t id, const size_t unique) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked && ((*i).first == unique))
|
|
{
|
|
++(*i).second->m_triggers;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool sortCurrentClientDataByTime(const stats::currentClientData_t* a, const stats::currentClientData_t* b)
|
|
{
|
|
return (a->m_startTime < b->m_startTime);
|
|
}
|
|
|
|
// get all client data blocks for stream
|
|
void stats::getClientDataForStream(const streamData::streamID_t id, currentClientList_t &client_data) throw()
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
if (id > 0)
|
|
{
|
|
streamStatTable_t::const_iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
const statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
currentClientData_t* client = new currentClientData_t((*i).second->m_hostName, (*i).second->m_ipAddr,
|
|
(*i).second->m_client->getXFF(), (*i).second->m_client->getUserAgent(),
|
|
(*i).second->m_client->getReferer(), (*i).second->m_triggers,
|
|
(*i).second->m_client->getUnique(), (*i).second->m_client->getStartTime(),
|
|
(*i).second->m_client->getGroup(), (*i).second->m_client->getClientType(),
|
|
(*i).second->m_fiveMinuteCumeCounted, (*i).second->m_connectCounted,
|
|
(*i).second->m_ripClient, (*i).second->m_kicked);
|
|
client_data.push_back(client);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (streamStatTable_t::const_iterator i = g_streamStatTable.begin(); i != g_streamStatTable.end(); ++i)
|
|
{
|
|
const statTableEntry_t &ste = (*i).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
currentClientData_t* client = new currentClientData_t((*i).second->m_hostName, (*i).second->m_ipAddr,
|
|
(*i).second->m_client->getXFF(), (*i).second->m_client->getUserAgent(),
|
|
(*i).second->m_client->getReferer(), (*i).second->m_triggers,
|
|
(*i).second->m_client->getUnique(), (*i).second->m_client->getStartTime(),
|
|
(*i).second->m_client->getGroup(), (*i).second->m_client->getClientType(),
|
|
(*i).second->m_fiveMinuteCumeCounted, (*i).second->m_connectCounted,
|
|
(*i).second->m_ripClient, (*i).second->m_kicked);
|
|
client_data.push_back(client);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(client_data.begin(), client_data.end(), sortCurrentClientDataByTime);
|
|
}
|
|
|
|
void stats::getClientDataForKicking(const streamData::streamID_t id, kickClientList_t &kick_data) throw()
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
if (id > 0)
|
|
{
|
|
streamStatTable_t::const_iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
const statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
kickClientData_t* client = new kickClientData_t((*i).second->m_client->getUserAgent(),
|
|
(*i).second->m_client->getUnique(),
|
|
(*i).second->m_kicked);
|
|
kick_data.push_back(client);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (streamStatTable_t::const_iterator i = g_streamStatTable.begin(); i != g_streamStatTable.end(); ++i)
|
|
{
|
|
const statTableEntry_t &ste = (*i).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
kickClientData_t* client = new kickClientData_t((*i).second->m_client->getUserAgent(),
|
|
(*i).second->m_client->getUnique(),
|
|
(*i).second->m_kicked);
|
|
kick_data.push_back(client);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stats::catchPreAddClients(const streamData::streamID_t id)
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
// make sure the client still exists before calling it
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
// if we find an active listener which has m_group = -1 then
|
|
// we need to get the listener to do a listener_add request.
|
|
if (!(*i).second->m_kicked &&
|
|
(*i).second->m_client->m_removeClientFromStats &&
|
|
((*i).second->m_client->getGroup() == -1))
|
|
{
|
|
// we only want to do this once
|
|
// so we'll set it to group = 0
|
|
// and it'll not be done again.
|
|
(*i).second->m_client->setGroup(0);
|
|
|
|
DEBUG_LOG((*i).second->m_client->m_clientLogString + "Re-authenticating listener for adverts / metrics.");
|
|
|
|
// using this to force an attept to check but only for non
|
|
// 'local' listener connections which won't get a group id
|
|
if ((isRemoteAddress((*i).second->m_client->m_clientAddr) ||
|
|
isRemoteAddress((*i).second->m_client->m_XFF)))
|
|
{
|
|
(*i).second->m_client->authForStream((*i).second->m_client->m_streamData);
|
|
}
|
|
(*i).second->m_client->reportNewListener();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// set flag in client so it will bail on next round. This is safe since the client object
|
|
// cannot delete itself until calling stats::removeClient() which is protected by the g_statLock
|
|
void stats::kickClient(const streamData::streamID_t id, const size_t unique) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
// make sure the client still exists before calling it
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked && ((*i).first == unique))
|
|
{
|
|
(*i).second->m_kicked = true;
|
|
if ((*i).second->m_client)
|
|
{
|
|
(*i).second->m_client->kickNextRound();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stats::kickClient(const streamData::streamID_t id, const uniString::utf8& ipAddr) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
// make sure the client still exists before calling it
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked && ((*i).second->m_ipAddr == ipAddr))
|
|
{
|
|
(*i).second->m_kicked = true;
|
|
if ((*i).second->m_client)
|
|
{
|
|
(*i).second->m_client->kickNextRound();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool stats::kickAllClients(const streamData::streamID_t id, const bool allStreams) throw()
|
|
{
|
|
bool kicked = false;
|
|
if ((id > 0) && !allStreams)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
// make sure the client still exists before calling it
|
|
streamStatTable_t::iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
(*i).second->m_kicked = true;
|
|
if ((*i).second->m_client)
|
|
{
|
|
(*i).second->m_client->kickNextRound();
|
|
kicked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!id && allStreams)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
for (streamStatTable_t::iterator ti = g_streamStatTable.begin(); ti != g_streamStatTable.end(); ++ti)
|
|
{
|
|
statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
(*i).second->m_kicked = true;
|
|
if ((*i).second->m_client)
|
|
{
|
|
(*i).second->m_client->kickNextRound();
|
|
kicked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return kicked;
|
|
}
|
|
|
|
static bool sortClientDataByTime(const stats::clientData_t &a, const stats::clientData_t &b)
|
|
{
|
|
return (a.m_client->getStartTime() < b.m_client->getStartTime());
|
|
}
|
|
|
|
const bool stats::kickDuplicateClients(const streamData::streamID_t id) throw()
|
|
{
|
|
bool kicked = false;
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
|
|
// we first spin through all of the connected listeners and work out
|
|
// which listener addresses have more than one connection against it
|
|
streamStatTable_t::const_iterator ti = g_streamStatTable.find(id);
|
|
if (ti != g_streamStatTable.end())
|
|
{
|
|
map<utf8, size_t> ipTable;
|
|
|
|
const statTableEntry_t &ste = (*ti).second;
|
|
if (!ste.m_clientData.empty())
|
|
{
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked)
|
|
{
|
|
if (ipTable.find((*i).second->m_ipAddr) == ipTable.end())
|
|
{
|
|
ipTable[(*i).second->m_ipAddr] = 1;
|
|
}
|
|
else
|
|
{
|
|
++ipTable[(*i).second->m_ipAddr];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ipTable.empty())
|
|
{
|
|
for (map<utf8, size_t>::const_iterator ip = ipTable.begin(); ip != ipTable.end(); ++ip)
|
|
{
|
|
// we now only look at addresses with multiple clients
|
|
// being reported for the address which has been noted
|
|
if (ip->second > 1)
|
|
{
|
|
map<utf8, size_t> agentTable;
|
|
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked && (ip->first == (*i).second->m_ipAddr))
|
|
{
|
|
const utf8 userAgent = (*i).second->m_client->getUserAgent();
|
|
if (agentTable.find(userAgent) == agentTable.end())
|
|
{
|
|
agentTable[userAgent] = 1;
|
|
}
|
|
else
|
|
{
|
|
++agentTable[userAgent];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!agentTable.empty())
|
|
{
|
|
std::vector<clientData_t> data;
|
|
for (map<utf8, size_t>::const_iterator ai = agentTable.begin(); ai != agentTable.end(); ++ai)
|
|
{
|
|
// this should now just leave us with duplicate
|
|
// user-agents connected from the same address
|
|
if (ai->second > 1)
|
|
{
|
|
// now we need to process through and get the
|
|
// details needed so we can finally kick them
|
|
for (statTableEntry_t::clientEntry_t::const_iterator i = ste.m_clientData.begin(); i != ste.m_clientData.end(); ++i)
|
|
{
|
|
if (!(*i).second->m_kicked &&
|
|
(ip->first == (*i).second->m_ipAddr) &&
|
|
(ai->first == (*i).second->m_client->getUserAgent()))
|
|
{
|
|
data.push_back(*(*i).second);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now we've got data, we sort by data and then
|
|
// process through the final set of data & kick
|
|
if (!data.empty())
|
|
{
|
|
std::sort(data.begin(), data.end(), sortClientDataByTime);
|
|
// remove the newest and kick the remainder
|
|
data.pop_back();
|
|
|
|
for (vector<stats::clientData_t>::iterator i = data.begin(); i != data.end(); ++i)
|
|
{
|
|
if (!(*i).m_kicked)
|
|
{
|
|
(*i).m_kicked = true;
|
|
if ((*i).m_client)
|
|
{
|
|
(*i).m_client->kickNextRound();
|
|
kicked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return kicked;
|
|
}
|
|
|
|
const bool stats::kickRandomNonRipClient(const streamData::streamID_t id, const bool anyStream) throw()
|
|
{
|
|
if (id > 0)
|
|
{
|
|
stackLock sml(g_statLock);
|
|
return _kickRandomNonRipClient(id, anyStream);
|
|
}
|
|
return false;
|
|
}
|