2004-12-22 18:25:29 +00:00
|
|
|
// IRCClient.cpp
|
|
|
|
// This file is (C) 2004 Royce Mitchell III
|
|
|
|
// and released under the BSD & LGPL licenses
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning ( disable : 4786 )
|
|
|
|
#endif//_MSC_VER
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "IRCClient.h"
|
|
|
|
#include "md5.h"
|
|
|
|
#include "cram_md5.h"
|
|
|
|
#include "trim.h"
|
|
|
|
#include "chomp.h"
|
|
|
|
#include "SplitJoin.h"
|
|
|
|
#include "base64.h"
|
2005-06-03 17:28:53 +00:00
|
|
|
#include "config.h"
|
2004-12-22 18:25:29 +00:00
|
|
|
|
|
|
|
using std::string;
|
|
|
|
using std::stringstream;
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
bool IRCClient::_debug = true;
|
|
|
|
|
2005-06-03 17:28:53 +00:00
|
|
|
// see rfc1459 for IRC-Protocoll Reference
|
|
|
|
|
2004-12-22 18:25:29 +00:00
|
|
|
IRCClient::IRCClient()
|
|
|
|
: _timeout(10*60*1000), _inRun(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IRCClient::Connect ( const string& server, short port )
|
|
|
|
{
|
|
|
|
string buf;
|
|
|
|
Close();
|
|
|
|
Attach ( suTcpSocket() );
|
|
|
|
if ( !suConnect ( *this, server.c_str(), port ) )
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::User ( const string& user, const string& mode,
|
|
|
|
const string& network, const string& realname )
|
|
|
|
{
|
|
|
|
string buf;
|
|
|
|
buf = "USER " + user + " \"" + mode + "\" \"" + network + "\" :" + realname + "\n";
|
|
|
|
return Send ( buf );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Nick ( const string& nick )
|
|
|
|
{
|
|
|
|
_nick = nick;
|
2005-06-03 17:28:53 +00:00
|
|
|
Send ( "NICK " + _nick + "\n" );
|
|
|
|
PrivMsg ("NickServ", "IDENTIFY " + (string)PASS);
|
|
|
|
return true;
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Mode ( const string& mode )
|
|
|
|
{
|
|
|
|
return Send ( "MODE " + _nick + " " + mode + "\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Names ( const string& channel )
|
|
|
|
{
|
|
|
|
return Send ( "NAMES " + channel + "\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Mode ( const string& channel, const string& mode, const string& target )
|
|
|
|
{
|
|
|
|
return Send ( "MODE " + channel + " " + mode + " " + target + "\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Join ( const string& channel )
|
|
|
|
{
|
2005-06-03 17:28:53 +00:00
|
|
|
return Send("JOIN " + channel + "\n");
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::PrivMsg ( const string& to, const string& text )
|
|
|
|
{
|
2005-04-20 18:16:32 +00:00
|
|
|
return Send ( "PRIVMSG " + to + " :" + text + '\n' );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Action ( const string& to, const string& text )
|
|
|
|
{
|
|
|
|
return Send ( "PRIVMSG " + to + " :" + (char)1 + "ACTION " + text + (char)1 + '\n' );
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Part ( const string& channel, const string& text )
|
|
|
|
{
|
|
|
|
return Send ( "PART " + channel + " :" + text + "\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
IRCClient::Quit ( const string& text )
|
|
|
|
{
|
|
|
|
return Send( "QUIT :" + text + "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IRCClient::_Recv ( string& buf )
|
|
|
|
{
|
|
|
|
bool b = (recvUntil ( buf, '\n', _timeout ) > 0);
|
|
|
|
if ( b && _debug )
|
|
|
|
{
|
|
|
|
printf ( ">> %s", buf.c_str() );
|
|
|
|
if ( buf[buf.length()-1] != '\n' )
|
|
|
|
printf ( "\n" );
|
|
|
|
}
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IRCClient::Send ( const string& buf )
|
|
|
|
{
|
|
|
|
if ( _debug )
|
|
|
|
{
|
|
|
|
printf ( "<< %s", buf.c_str() );
|
|
|
|
if ( buf[buf.length()-1] != '\n' )
|
|
|
|
printf ( "\n" );
|
|
|
|
}
|
|
|
|
return ( buf.length() == (size_t)send ( *this, buf.c_str(), buf.length(), 0 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IRCClient::OnPing( const string& text )
|
|
|
|
{
|
|
|
|
return Send( "PONG " + text + "\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int THREADAPI IRCClient::Callback ( IRCClient* irc )
|
|
|
|
{
|
|
|
|
return irc->Run ( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
int IRCClient::Run ( bool launch_thread )
|
|
|
|
{
|
|
|
|
if ( (SOCKET)*this == INVALID_SOCKET )
|
|
|
|
return 0;
|
|
|
|
if ( _inRun ) return 1;
|
|
|
|
if ( launch_thread )
|
|
|
|
{
|
|
|
|
ThreadPool::Instance().Launch ( (ThreadPoolFunc*)IRCClient::Callback, this );
|
|
|
|
return 1;
|
2005-06-03 17:28:53 +00:00
|
|
|
}
|
2004-12-22 18:25:29 +00:00
|
|
|
_inRun = true;
|
|
|
|
if ( _debug ) printf ( "IRCClient::Run() - waiting for responses\n" );
|
|
|
|
string buf;
|
|
|
|
while ( _Recv(buf) )
|
|
|
|
{
|
|
|
|
if ( !strnicmp ( buf.c_str(), "NOTICE ", 7 ) )
|
|
|
|
{
|
|
|
|
//printf ( "recv'd NOTICE msg...\n" );
|
|
|
|
// TODO...
|
|
|
|
//OnAuth (
|
|
|
|
}
|
|
|
|
else if ( !strnicmp ( buf.c_str(), "PING ", 5 ) )
|
|
|
|
{
|
|
|
|
const char* p = &buf[5]; // point to first char after "PING "
|
|
|
|
while ( *p == ':' ) // then read past the colons
|
|
|
|
p++;
|
|
|
|
const char* p2 = strpbrk ( p, "\r\n" ); // find the end of line
|
|
|
|
string text ( p, p2-p ); // and set the text
|
|
|
|
OnPing( text );
|
|
|
|
}
|
|
|
|
else if ( buf[0] == ':' )
|
|
|
|
{
|
|
|
|
const char* p = &buf[1]; // skip first colon...
|
|
|
|
const char* p2 = strpbrk ( p, " !" );
|
|
|
|
if ( !p2 )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 0: ", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
string src ( p, p2-p );
|
|
|
|
if ( !src.length() )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 0.5: %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p = p2 + 1;
|
|
|
|
if ( *p2 == '!' )
|
|
|
|
{
|
|
|
|
p2 = strchr ( p, ' ' );
|
|
|
|
if ( !p2 )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 1: %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//string srchost ( p, p2-p );
|
|
|
|
p = p2 + 1;
|
|
|
|
}
|
|
|
|
p2 = strchr ( p, ' ' );
|
|
|
|
if ( !p2 )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 2: %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
string cmd ( p, p2-p );
|
|
|
|
p = p2 + 1;
|
|
|
|
p2 = strpbrk ( p, " :" );
|
|
|
|
if ( !p2 )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 3: %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
string tgt ( p, p2-p );
|
|
|
|
p = p2 + 1;
|
|
|
|
p += strspn ( p, " " );
|
|
|
|
if ( *p == '=' )
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
p += strspn ( p, " " );
|
|
|
|
}
|
|
|
|
if ( *p == ':' )
|
|
|
|
p++;
|
|
|
|
p2 = strpbrk ( p, "\r\n" );
|
|
|
|
if ( !p2 )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 4: %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
string text ( p, p2-p );
|
|
|
|
strlwr ( &cmd[0] );
|
|
|
|
if ( cmd == "privmsg" )
|
|
|
|
{
|
|
|
|
if ( !tgt.length() )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 5 (PRIVMSG w/o target): %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
2005-04-20 18:16:32 +00:00
|
|
|
if ( *p == 1 )
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
p2 = strchr ( p, ' ' );
|
|
|
|
if ( !p2 ) p2 = p + strlen(p);
|
|
|
|
cmd = string ( p, p2-p );
|
|
|
|
strlwr ( &cmd[0] );
|
|
|
|
p = p2 + 1;
|
|
|
|
p2 = strchr ( p, 1 );
|
|
|
|
if ( !p2 )
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 6 (no terminating \x01 for initial \x01 found: %s", buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
text = string ( p, p2-p );
|
|
|
|
if ( cmd == "action" )
|
|
|
|
{
|
|
|
|
if ( tgt[0] == '#' )
|
|
|
|
OnChannelAction ( tgt, src, text );
|
|
|
|
else
|
|
|
|
OnPrivAction ( src, text );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf ( "!!!:OnRecv failure 7 (unrecognized \x01 command '%s': %s", cmd.c_str(), buf.c_str() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2004-12-22 18:25:29 +00:00
|
|
|
else
|
2005-04-20 18:16:32 +00:00
|
|
|
{
|
|
|
|
if ( tgt[0] == '#' )
|
|
|
|
OnChannelMsg ( tgt, src, text );
|
|
|
|
else
|
|
|
|
OnPrivMsg ( src, text );
|
|
|
|
}
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
|
|
|
else if ( cmd == "mode" )
|
|
|
|
{
|
|
|
|
// two diff. kinds of mode notifications...
|
|
|
|
//printf ( "[MODE] src='%s' cmd='%s' tgt='%s' text='%s'", src.c_str(), cmd.c_str(), tgt.c_str(), text.c_str() );
|
|
|
|
//OnMode (
|
|
|
|
// self mode change:
|
2005-04-20 18:16:32 +00:00
|
|
|
// [MODE] src=Nick cmd=mode tgt=Nick text=+i
|
2004-12-22 18:25:29 +00:00
|
|
|
// channel mode change:
|
2005-04-20 18:16:32 +00:00
|
|
|
// [MODE] src=Nick cmd=mode tgt=#Channel text=+o Nick
|
2004-12-22 18:25:29 +00:00
|
|
|
if ( tgt[0] == '#' )
|
|
|
|
{
|
|
|
|
p = text.c_str();
|
|
|
|
p2 = strchr ( p, ' ' );
|
2005-04-20 18:16:32 +00:00
|
|
|
if ( p2 && *p2 )
|
2004-12-22 18:25:29 +00:00
|
|
|
{
|
2005-04-20 18:16:32 +00:00
|
|
|
string mode ( p, p2-p );
|
2004-12-22 18:25:29 +00:00
|
|
|
p = p2 + 1;
|
|
|
|
p += strspn ( p, " " );
|
2005-04-20 18:16:32 +00:00
|
|
|
OnUserModeInChannel ( src, tgt, mode, trim(p) );
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
2005-04-20 18:16:32 +00:00
|
|
|
else
|
|
|
|
OnChannelMode ( tgt, text );
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
OnMode ( tgt, text );
|
|
|
|
}
|
|
|
|
else if ( cmd == "join" )
|
|
|
|
{
|
2005-06-03 17:28:53 +00:00
|
|
|
mychannel = text;
|
2004-12-22 18:25:29 +00:00
|
|
|
OnJoin ( src, text );
|
|
|
|
}
|
2005-04-20 18:16:32 +00:00
|
|
|
else if ( cmd == "part" )
|
|
|
|
{
|
|
|
|
OnPart ( src, text );
|
|
|
|
}
|
|
|
|
else if ( cmd == "nick" )
|
|
|
|
{
|
|
|
|
OnNick ( src, text );
|
|
|
|
}
|
2005-06-03 17:28:53 +00:00
|
|
|
else if ( cmd == "kick" )
|
|
|
|
{
|
|
|
|
OnKick ();
|
|
|
|
}
|
2004-12-22 18:25:29 +00:00
|
|
|
else if ( isdigit(cmd[0]) )
|
|
|
|
{
|
|
|
|
int i = atoi(cmd.c_str());
|
|
|
|
switch ( i )
|
|
|
|
{
|
2005-06-03 17:28:53 +00:00
|
|
|
|
2004-12-22 18:25:29 +00:00
|
|
|
case 1: // "Welcome!" - i.e. it's okay to issue commands now...
|
|
|
|
OnConnected();
|
|
|
|
break;
|
2005-06-03 17:28:53 +00:00
|
|
|
|
2004-12-22 18:25:29 +00:00
|
|
|
case 353: // user list for channel....
|
|
|
|
{
|
|
|
|
p = text.c_str();
|
|
|
|
p2 = strpbrk ( p, " :" );
|
|
|
|
if ( !p2 ) continue;
|
|
|
|
string channel ( p, p2-p );
|
|
|
|
p = strchr ( p2, ':' );
|
|
|
|
if ( !p ) continue;
|
|
|
|
p++;
|
|
|
|
vector<string> users;
|
|
|
|
while ( *p )
|
|
|
|
{
|
|
|
|
p2 = strchr ( p, ' ' );
|
|
|
|
if ( !p2 )
|
|
|
|
p2 = p + strlen(p);
|
|
|
|
users.push_back ( string ( p, p2-p ) );
|
|
|
|
p = p2+1;
|
|
|
|
p += strspn ( p, " " );
|
|
|
|
}
|
|
|
|
OnChannelUsers ( channel, users );
|
|
|
|
}
|
|
|
|
break;
|
2005-06-03 17:28:53 +00:00
|
|
|
|
2004-12-22 18:25:29 +00:00
|
|
|
case 366: // END of user list for channel
|
|
|
|
{
|
|
|
|
p = text.c_str();
|
|
|
|
p2 = strpbrk ( p, " :" );
|
|
|
|
if ( !p2 ) continue;
|
|
|
|
string channel ( p, p2-p );
|
|
|
|
OnEndChannelUsers ( channel );
|
|
|
|
}
|
|
|
|
break;
|
2005-06-03 17:28:53 +00:00
|
|
|
|
|
|
|
case 474: // You are banned
|
|
|
|
{
|
|
|
|
p = text.c_str();
|
|
|
|
p2 = strpbrk ( p, " :" );
|
|
|
|
if ( !p2 ) continue;
|
|
|
|
string channel ( p, p2-p );
|
|
|
|
OnBanned ( channel );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 433: // Nick in Use
|
|
|
|
{
|
|
|
|
string nick = _nick;
|
|
|
|
Nick (nick + "_");
|
|
|
|
|
|
|
|
PrivMsg ("NickServ", "GHOST " + nick + " " + PASS);
|
|
|
|
|
|
|
|
// HACK HACK HACK
|
|
|
|
Mode ( "+i" );
|
|
|
|
Join ( CHANNEL ); // this is because IRC client does not review if his commands were sucessfull
|
|
|
|
|
|
|
|
Sleep ( 1000 );
|
|
|
|
Nick ( nick );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: //MOTD
|
|
|
|
case 376: //MOTD
|
|
|
|
case 372:
|
|
|
|
break;
|
|
|
|
|
2004-12-22 18:25:29 +00:00
|
|
|
default:
|
|
|
|
if ( _debug ) printf ( "unknown command %i: %s", i, buf.c_str() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-04-20 18:16:32 +00:00
|
|
|
if ( strstr ( buf.c_str(), "ACTION" ) )
|
|
|
|
{
|
|
|
|
printf ( "ACTION: " );
|
|
|
|
for ( int i = 0; i < buf.size(); i++ )
|
|
|
|
printf ( "%c(%xh)", buf[i], (unsigned)(unsigned char)buf[i] );
|
|
|
|
}
|
|
|
|
else if ( _debug ) printf ( "unrecognized ':' response: %s", buf.c_str() );
|
2004-12-22 18:25:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( _debug ) printf ( "unrecognized irc msg: %s", buf.c_str() );
|
|
|
|
}
|
|
|
|
//OnRecv ( buf );
|
|
|
|
}
|
|
|
|
if ( _debug ) printf ( "IRCClient::Run() - exiting\n" );
|
|
|
|
_inRun = false;
|
|
|
|
return 0;
|
|
|
|
}
|