gnuworld/mod.snoop/snoop.cc

818 lines
18 KiB
C++

/**
* snoop.cc
*/
#include <string>
#include <sstream>
#include <iostream>
#include <cctype>
#include "server.h"
#include "client.h"
#include "snoop.h"
#include "Channel.h"
#include "iClient.h"
#include "Network.h"
#include "EConfig.h"
#include "StringTokenizer.h"
#include "misc.h"
namespace gnuworld
{
using std::endl ;
using std::string ;
using std::stringstream ;
extern "C"
{
xClient* _gnuwinit( const string& args )
{
return new snoop( args ) ;
}
}
snoop::snoop( const string& confFileName )
: xClient( confFileName )
{
EConfig conf( confFileName ) ;
cmdchar = conf.Require( "cmdchar" )->second ;
adminChanName = conf.Require( "adminchan" )->second ;
relayChanName = conf.Require( "relaychan" )->second ;
defaultQuitMessage = conf.Require( "defaultquitmessage" )->second ;
maxnicklen = ::atoi( conf.Require( "maxnicklen" )->second.c_str() ) ;
EConfig::const_iterator ptr = conf.Find( "permit_user" ) ;
while( ptr != conf.end() && ptr->first == "permit_user" )
{
allowAccess.push_back( ptr->second ) ;
++ptr ;
}
ptr = conf.Find( "joinchan" ) ;
while( ptr != conf.end() && ptr->first == "joinchan" )
{
joinchans.push_back( ptr->second ) ;
++ptr ;
}
ptr = conf.Find( "address" ) ;
while( ptr != conf.end() && ptr->first == "address" )
{
addresses.push_back( ptr->second ) ;
++ptr ;
}
}
snoop::~snoop()
{}
void snoop::BurstChannels()
{
xClient::BurstChannels() ;
// It's ok if admin and relay chans are the same,
// xServer::JoinChannel() will not join more than once
Join( adminChanName ) ;
Join( relayChanName ) ;
//MyUplink->RegisterTimer( ::time( 0 ) + 1, this ) ;
for (std::vector<string>::const_iterator addr = addresses.begin(); addr < addresses.end(); addr++)
{
StringTokenizer straddr(*addr);
string nick = straddr[0];
string user = straddr[1];
string host = straddr[2];
string real = straddr.assemble(3);
string fulladdr = nick + '!' + user + '@' +host;
elog << "snoop::handleSpawnClient> "
<< fulladdr
<< endl ;
if ((validNickname(nick)) && ( Network->findNick( nick ) == 0 ))
{
char newCharYY[ 6 ] ;
newCharYY[ 2 ] = 0 ;
inttobase64( newCharYY, MyUplink->getIntYY(), 2 ) ;
iClient* newClient = new (std::nothrow) iClient(
MyUplink->getIntYY(), // intYY
newCharYY, // charYYXXX
nick,
user,
"AAAAAA", // host base 64
host,
"realInsecureHost.com", // realInsecureHost
"+i", // mode
string(), // account
0, // account_ts
real,
31337 // connect time
) ;
assert( newClient != 0 ) ;
if( !getUplink()->AttachClient( newClient, this ) )
{
elog << "snoop::handleSpawnClient> Failed to add new client: "
<< *newClient
<< endl ;
//Notice( srcClient, "Failed to create new fake client" ) ;
delete newClient ; newClient = 0 ;
continue;
}
// Each relay (spawn) client must join the relay chan
getUplink()->JoinChannel( newClient, relayChanName );
clones.push_back( newClient ) ;
}
joinAll();
}
}
void snoop::OnChannelMessage( iClient* srcClient,
Channel* theChan,
const string& Message )
{
if( strcasecmp( theChan->getName(), adminChanName ) )
{
// Not the admin chan, ignore
return ;
}
bool userHasAccess = hasAccess( srcClient->getAccount() ) ;
if ((!srcClient->isOper()) && (!userHasAccess))
{
return ;
}
StringTokenizer st( Message ) ;
if( st.empty() || st[ 0 ] != cmdchar )
{
return ;
}
elog << "snoop::OnChannelMessage> Received chan message: "
<< st.assemble( 0 )
<< endl ;
if( st.size() < 2 )
{
return ;
}
string command( st[ 1 ] ) ;
string_lower( command ) ;
if( command == "spawnclient" )
{
handleSpawnClient( srcClient, theChan, st ) ;
}
else if( command == "join" )
{
handleSpawnJoin( srcClient, theChan, st ) ;
}
else if( command == "joinall" )
{
if (st.size() < 3)
joinAll();
else
joinAll( srcClient, theChan, st ) ;
}
else if( command == "part" )
{
handleSpawnPart( srcClient, theChan, st ) ;
}
else if( command == "partall" )
{
partAll( srcClient, theChan, st ) ;
}
else if( command == "quit" )
{
handleSpawnQuit( srcClient, st ) ;
}
else if( command == "quitall" )
{
quitAll(srcClient, st ) ;
}
else if( (command == "say") || (command == "do") )
{
say(srcClient,st);
}
else if( (command == "sayall") || (command == "doall") )
{
sayAll(srcClient,st);
}
else if( command == "reload" )
{
getUplink()->UnloadClient( this,
"Something has changed in the matrix..." ) ;
getUplink()->LoadClient( "libsnoop.la",
getConfigFileName() ) ;
}
else if( command == "shutdown" )
{
getUplink()->Shutdown() ;
}
}
void snoop::handleSpawnClient( iClient* srcClient,
Channel* theChan,
const StringTokenizer& st )
{
// snoop spawnclient nick!user@host realname
if( st.size() < 4 )
{
usage( srcClient, "spawnclient" ) ;
return ;
}
const string realname( st.assemble( 3 ) ) ;
//elog << "snoop::OnChannelMessage> nickTokens, st[ 2 ]: "
// << st[ 2 ]
// << endl ;
// st[ 2 ] is nick!user@host:realname
StringTokenizer nickTokens( st[ 2 ], '!' ) ;
if( nickTokens.size() != 2 )
{
usage( srcClient, "spawnclient" ) ;
return ;
}
StringTokenizer userTokens( nickTokens[ 1 ], '@' ) ;
if( userTokens.size() != 2 )
{
usage( srcClient, "spawnclient" ) ;
return ;
}
const string nickname( nickTokens[ 0 ] ) ;
const string username( userTokens[ 0 ] ) ;
const string hostname( userTokens[ 1 ] ) ;
if( nickname.empty() || username.empty() || hostname.empty() )
{
Notice( srcClient, "Please specify non-empty nick/user/host names" ) ;
return ;
}
// Verify that the nickname is valid
if( !validNickname( nickname ) )
{
Notice( srcClient, "Invalid nickname" ) ;
return ;
}
if( Network->findNick( nickname ) != 0 )
{
Notice( srcClient, "SPAWNCLIENT: Nickname %s already exists",
nickname.c_str() ) ;//elog << "cloner::OnPrivateMessage> " << Message << endl ;
return ;
}
if( string::npos == hostname.find( '.' ) )
{
Notice( srcClient, "SPAWNCLIENT: Hostname must appear valid" ) ;
return ;
}
char newCharYY[ 6 ] ;
newCharYY[ 2 ] = 0 ;
inttobase64( newCharYY, MyUplink->getIntYY(), 2 ) ;
iClient* newClient = new (std::nothrow) iClient(
MyUplink->getIntYY(), // intYY
newCharYY, // charYYXXX
nickname,
username,
"AAAAAA", // host base 64
hostname,
"realInsecureHost.com", // realInsecureHost
"+i", // mode
string(), // account
0, // account_ts
realname,
31337 // connect time
) ;
assert( newClient != 0 ) ;
if( !getUplink()->AttachClient( newClient, this ) )
{
elog << "snoop::handleSpawnClient> Failed to add new client: "
<< *newClient
<< endl ;
Notice( srcClient, "Failed to create new fake client" ) ;
delete newClient ; newClient = 0 ;
}
// Each relay (spawn) client must join the relay chan
if( !getUplink()->JoinChannel( newClient, relayChanName ) )
{
Notice( srcClient, "Unable to make \'%s\' join channel "
"\'%s\', killing off client",
st[ 2 ].c_str(),
st[ 3 ].c_str() ) ;
getUplink()->DetachClient( newClient ) ;
delete newClient ; newClient = 0 ;
return ;
}
clones.push_back( newClient ) ;
Message( theChan, "Successfully created nick %s and joined "
"relay channel %s",
nickname.c_str(),
relayChanName.c_str() ) ;
}
void snoop::handleSpawnJoin( iClient* srcClient,
Channel* theChan,
const StringTokenizer& st )
{
// snoopy spawnjoin <nick> <chan>
if( st.size() != 4 )
{
usage( srcClient, "join" ) ;
return ;
}
// Find the client
iClient* fakeClient = Network->findNick( st[ 2 ] ) ;
if( 0 == fakeClient )
{
Notice( srcClient, "Nick \'%s\' does not exist",
st[ 2 ].c_str() ) ;
return ;
}
// Verify that it is a fake client, and owned by this module
xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ;
if( ownerClient != this )
{
Notice( srcClient, "I don't own that client!" ) ;
return ;
}
// fakeClient is valid, and owned by this module
if( !getUplink()->JoinChannel( fakeClient, st[ 3 ] ) )
{
Notice( srcClient, "Unable to make \'%s\' join channel "
"%s",
st[ 2 ].c_str(),
st[ 3 ].c_str() ) ;
}
else
{
Message( theChan, "%s successfully joined %s",
st[ 2 ].c_str(),
st[ 3 ].c_str() ) ;
}
}
void snoop::joinAll()
{
if (joinchans.empty()) return;
for (std::vector<string>::const_iterator jchan = joinchans.begin(); jchan < joinchans.end(); jchan++)
for( std::list< iClient* >::const_iterator ptr = clones.begin(),
endPtr = clones.end() ; ptr != endPtr ; ++ptr )
{
//if( !getUplink()->JoinChannel( *ptr, relayChanName ) ) return;
getUplink()->JoinChannel( *ptr, *jchan );
}
}
void snoop::joinAll(iClient* srcClient, Channel* theChan, const StringTokenizer& st)
{
// snoopy joinall <chan>
if( st.size() != 3 )
{
usage( srcClient, "joinall" ) ;
return ;
}
string chanName = st[2]; //st.assemble(2); //( st[ 0 ] ) ;
if( chanName[ 0 ] != '#' )
{
chanName.insert( chanName.begin(), '#' ) ;
}
for( std::list< iClient* >::const_iterator ptr = clones.begin(),
endPtr = clones.end() ; ptr != endPtr ; ++ptr )
{
if( !getUplink()->JoinChannel( *ptr, st.assemble(2) ) )
{
Message(theChan, "%s unable to join channel %s",(*ptr)->getNickName().c_str(),chanName.c_str());
//Notice( srcClient, "Unable to make \'%s\' join channel "
// "\'%s\', killing off client",
// chanName.c_str(),
// (*ptr)->getNickName().c_str() ) ;
//getUplink()->DetachClient( *ptr ) ;
//delete (*ptr); (*ptr) = 0;
//delete newClient ; newClient = 0 ;
//return ;
}
}
}
void snoop::handleSpawnPart( iClient* srcClient,
Channel* theChan,
const StringTokenizer& st )
{
// snoopy part <nick> <chan> [message]
if( st.size() < 4 )
{
usage( srcClient, "part" ) ;
return ;
}
// Find the client
iClient* fakeClient = Network->findNick( st[ 2 ] ) ;
if( 0 == fakeClient )
{
Notice( srcClient, "Nick \'%s\' does not exist",
st[ 2 ].c_str() ) ;
return ;
}
// Verify that it is a fake client, and owned by this module
xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ;
if( ownerClient != this )
{
Notice( srcClient, "I don't own that client!" ) ;
return ;
}
// fakeClient is valid, and owned by this module
getUplink()->PartChannel( fakeClient, st.assemble(3) ) ; //st[3]
Message( theChan, "%s successfully parted %s",
st[ 2 ].c_str(),
st[ 3 ].c_str() ) ;
}
void snoop::partAll(iClient* srcClient, Channel* theChan, const StringTokenizer& st)
{
// snoopy partall <chan> [message]
if( st.size() < 3 )
{
usage( srcClient, "partall" ) ;
return ;
}
for( std::list< iClient* >::const_iterator ptr = clones.begin(),
endPtr = clones.end() ; ptr != endPtr ; ++ptr )
{
// Find the client
iClient* fakeClient = Network->findFakeClient(*ptr); //->findNick( st[ 2 ] ) ;
if( 0 == fakeClient )
{
Notice( srcClient, "Nick \'%s\' does not exist",
st[ 2 ].c_str() ) ;
return ;
}
// Verify that it is a fake client, and owned by this module
xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ;
if( ownerClient != this )
{
Notice( srcClient, "I don't own that client!" ) ;
return ;
}
// fakeClient is valid, and owned by this module
getUplink()->PartChannel( fakeClient, st.assemble(2) ) ; //st[2]
//Message( theChan, "%s successfully parted %s", st[ 2 ].c_str(), st[ 3 ].c_str() ) ;
}
Message(theChan, "Successfully parted all channels");
}
void snoop::usage( iClient* srcClient,
const string& command )
{
if( command == "spawnclient" )
{
Notice( srcClient, "Usage: spawnclient <nick!user@host <realname>" ) ;
}
else if( command == "join" )
{
Notice( srcClient, "Usage: join <nick> <chan> [key]" ) ;
}
else if( command == "joinall" )
{
Notice( srcClient, "Usage: joinall <chan> [key]" ) ;
}
else if( command == "part" )
{
Notice( srcClient, "Usage: part <nick> <chan> [message]" ) ;
}
else if( command == "partall" )
{
Notice( srcClient, "Usage: partall <chan> [message]" ) ;
}
else if( command == "quit" )
{
Notice( srcClient, "Usage: quit <nick> [quit message]" ) ;
}
else if( command == "quitall" )
{
Notice( srcClient, "Usage: quitall [quit message]" ) ;
}
else if( command == "say" )
{
Notice( srcClient, "Usage: say <nick> <chan|nick> <message>" ) ;
}
else if( command == "sayall" )
{
Notice( srcClient, "Usage: sayall <chan|nick> <message>" ) ;
}
else if( command == "do" )
{
Notice( srcClient, "Usage: do <nick> <chan> <message>" ) ;
}
else if( command == "doall" )
{
Notice( srcClient, "Usage: doall <chan> <message>" ) ;
}
}
/* //From now on, snooping is not our primary task
void snoop::OnFakeChannelMessage( iClient* srcClient,
iClient* fakeClient,
Channel* theChan,
const string& Message )
{
// A fake client received a channel message
// Ignore if it's the admin or relay channel
if( !strcasecmp( theChan->getName(), adminChanName ) ||
!strcasecmp( theChan->getName(), relayChanName ) )
{
return ;
}
// For now simply relay to the relay chan without checking for
// state, etc
Channel* relayChan = Network->findChannel( relayChanName ) ;
if( 0 == relayChan )
{
elog << "snoop::OnFakeChannelMessage> Fake client: "
<< *fakeClient
<< " unable to find relay chan: "
<< relayChanName
<< endl ;
return ;
}
stringstream s ;
s << "[RELAY "
<< theChan->getName()
<< "] "
<< srcClient->getNickName()
<< "!"
<< srcClient->getUserName()
<< "@"
<< srcClient->getInsecureHost()
<< ": "
<< Message ;
FakeMessage( relayChan,
fakeClient,
s.str().c_str() ) ;
}
*/
void snoop::handleSpawnQuit( iClient* srcClient,
const StringTokenizer& st )
{
// snoopy quit <nick> [message]
if( st.size() < 3 )
{
usage( srcClient, "quit" ) ;
return ;
}
string quitMessage( defaultQuitMessage ) ;
if( st.size() >= 4 )
{
quitMessage = st.assemble( 3 ) ;
}
// Find the client
iClient* fakeClient = Network->findFakeNick( st[ 2 ] ) ;
if( 0 == fakeClient )
{
Notice( srcClient, "Nick \'%s\' does not exist",
st[ 2 ].c_str() ) ;
return ;
}
// Verify that it is a fake client, and owned by this module
xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ;
if( ownerClient != this )
{
Notice( srcClient, "I don't own that client!" ) ;
return ;
}
getUplink()->DetachClient( fakeClient, quitMessage ) ;
Notice( srcClient, "%s detached",
st[ 2 ].c_str() ) ;
// This module allocated the iClient, and it must therefore deallocate
// it.
delete fakeClient ; fakeClient = 0 ;
}
void snoop::quitAll(iClient* srcClient, const StringTokenizer& st)
{
// snoopy quitall [message]
if( st.size() < 3 )
{
usage( srcClient, "quitall" ) ;
return ;
}
string quitMessage( defaultQuitMessage ) ;
if( st.size() >= 3 )
{
quitMessage = st.assemble( 2 ) ;
}
//std::vector<iClient*> toDie;
for( std::list< iClient* >::const_iterator ptr = clones.begin(),
endPtr = clones.end() ; ptr != endPtr ; ++ptr )
{
// Find the client
iClient* fakeClient = Network->findFakeClient(*ptr); //->findFakeNick( st[ 2 ] ) ;
if( 0 == fakeClient )
{
Notice( srcClient, "Nick \'%s\' does not exist",
st[ 2 ].c_str() ) ;
return ;
}
// Verify that it is a fake client, and owned by this module
xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ;
if( ownerClient != this )
{
Notice( srcClient, "I don't own that client!" ) ;
return ;
}
getUplink()->DetachClient( fakeClient, quitMessage ) ;
//Notice( srcClient, "%s detached", st[ 2 ].c_str() ) ;
// This module allocated the iClient, and it must therefore deallocate
// it.
//toDie.push_back(*ptr);
delete fakeClient ; fakeClient = 0 ;
}
clones.clear();
}
void snoop::say(iClient* srcClient, const StringTokenizer& st)
{
// snoopy say <fakenick> <channel|nick> [message]
if( st.size() < 5 )
{
if (st[1] == "say") usage( srcClient, "say" ) ;
if (st[1] == "do" ) usage( srcClient, "do" ) ;
return ;
}
string chanOrNickName( st[ 3 ] ) ;
string privMsg( st.assemble(4).c_str() ) ;
if( chanOrNickName[ 0 ] != '#' )
{ // Assume nickname
iClient* Target = Network->findNick( st[ 3 ] ) ;
if( NULL == Target )
{
Notice( srcClient, "Unable to find nick: %s"
, st[ 3 ].c_str() ) ;
return ;
}
chanOrNickName = Target->getCharYYXXX();
}
iClient* fakeClient = Network->findFakeNick( st[ 2 ] ) ;
if( 0 == fakeClient )
{
Notice( srcClient, "Nick \'%s\' does not exist",
st[ 2 ].c_str() ) ;
return ;
}
// Verify that it is a fake client, and owned by this module
xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ;
if( ownerClient != this )
{
Notice( srcClient, "I don't own that client!" ) ;
return ;
}
stringstream s ;
s << fakeClient->getCharYYXXX()
<< " P "
<< chanOrNickName
<< " :"
<< privMsg ;
MyUplink->Write( s ) ;
}
void snoop::sayAll(iClient* srcClient, const StringTokenizer& st)
{
// snoopy say <chan|nick> [message]
if( st.size() < 4 )
{
if (st[1] == "sayall") usage( srcClient, "sayall" ) ;
if (st[1] == "doall" ) usage( srcClient, "doall" ) ;
return ;
}
string chanOrNickName( st[ 2 ] ) ;
string privMsg( st.assemble(3).c_str() ) ;
if( chanOrNickName[ 0 ] != '#' )
{ // Assume nickname
iClient* Target = Network->findNick( st[ 2 ] ) ;
if( NULL == Target )
{
Notice( srcClient, "Unable to find nick: %s"
, st[ 2 ].c_str() ) ;
return ;
}
chanOrNickName = Target->getCharYYXXX();
}
for( std::list< iClient* >::const_iterator ptr = clones.begin(),
endPtr = clones.end() ; ptr != endPtr ; ++ptr )
{
stringstream s ;
s << (*ptr)->getCharYYXXX()
<< " P "
<< chanOrNickName
<< " :"
<< privMsg ;
MyUplink->Write( s ) ;
}
}
bool snoop::validNickname( const string& nickname ) const
{
if( nickname.empty() || nickname.size() > maxnicklen )
{
return false ;
}
/*
* From ircu:
* Nickname characters are in range 'A'..'}', '_', '-', '0'..'9'
* anything outside the above set will terminate nickname.
* In addition, the first character cannot be '-' or a Digit.
*/
if( isdigit( nickname[ 0 ] ) )
{
return false ;
}
for( string::const_iterator sItr = nickname.begin() ;
sItr != nickname.end() ; ++sItr )
{
if( *sItr >= 'A' && *sItr <= '}' )
{
// ok
continue ;
}
if( '_' == *sItr || '-' == *sItr )
{
// ok
continue ;
}
if( *sItr >= '0' && *sItr <= '9' )
{
// ok
continue ;
}
// bad char
return false ;
}
return true ;
}
bool snoop::hasAccess( const string& accountName ) const
{
for( std::list< string >::const_iterator itr = allowAccess.begin() ;
itr != allowAccess.end() ; ++itr )
{
if( !strcasecmp( accountName, *itr ) )
{
return true ;
}
}
return false ;
}
} // namespace gnuworld