/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "IRCProtocol.h"
#include
#define CIRCDDB_VERSION "1.2.4"
IRCProtocol::IRCProtocol ( IRCApplication * app,
const wxString& callsign, const wxString& password, const wxString& channel,
const wxString& versionInfo )
{
this -> password = password;
this -> channel = channel;
this -> app = app;
this->versionInfo = wxT("CIRCDDB:");
this->versionInfo.Append(wxT(CIRCDDB_VERSION));
if (versionInfo.Len() > 0) {
this->versionInfo.Append(wxT(" "));
this->versionInfo.Append(versionInfo);
}
int hyphenPos = callsign.find(wxT('-'));
if (hyphenPos == wxNOT_FOUND) {
wxString n;
n = callsign + wxT("-1");
nicks.Add(n);
n = callsign + wxT("-2");
nicks.Add(n);
n = callsign + wxT("-3");
nicks.Add(n);
n = callsign + wxT("-4");
nicks.Add(n);
} else {
nicks.Add(callsign);
}
name = callsign;
pingTimer = 60; // 30 seconds
state = 0;
timer = 0;
chooseNewNick();
}
IRCProtocol::~IRCProtocol()
{
}
void IRCProtocol::chooseNewNick()
{
int r = rand() % nicks.GetCount();
currentNick = nicks[r];
}
void IRCProtocol::setNetworkReady( bool b )
{
if (b == true) {
if (state != 0) {
wxLogError(wxT("IRCProtocol::setNetworkReady: unexpected state"));
}
state = 1;
chooseNewNick();
} else {
state = 0;
}
}
bool IRCProtocol::processQueues ( IRCMessageQueue * recvQ, IRCMessageQueue * sendQ )
{
if (timer > 0) {
timer --;
}
while (recvQ->messageAvailable()) {
IRCMessage * m = recvQ -> getMessage();
#if defined(DEBUG_IRC)
wxString d = wxT("R [") + m->prefix + wxT("] [") + m->command + wxT("]");
for (int i=0; i < m->numParams; i++) {
d.Append(wxT(" [") + m->params[i] + wxT("]") );
}
d.Replace(wxT("%"), wxT("%%"), true);
d.Replace(wxT("\\"), wxT("\\\\"), true);
wxLogVerbose(d);
#endif
if (m->command.IsSameAs(wxT("004"))) {
if (state == 4) {
if (m->params.GetCount() > 1) {
wxRegEx serverNamePattern(wxT("^grp[1-9]s[1-9].ircDDB$"));
if (serverNamePattern.Matches( m->params[1] )) {
app->setBestServer(wxT("s-") + m->params[1].Mid(0,6));
}
}
state = 5; // next: JOIN
app->setCurrentNick(currentNick);
}
} else if (m->command.IsSameAs(wxT("PING"))) {
IRCMessage * m2 = new IRCMessage();
m2->command = wxT("PONG");
if (m->params.GetCount() > 0) {
m2->numParams = 1;
m2->params.Add( m->params[0] );
}
sendQ -> putMessage(m2);
} else if (m->command.IsSameAs(wxT("JOIN"))) {
if ((m->numParams >= 1) && m->params[0].IsSameAs(channel)) {
if (m->getPrefixNick().IsSameAs(currentNick) && (state == 6)) {
if (debugChannel.Len() > 0) {
state = 7; // next: join debug_channel
} else {
state = 10; // next: WHO *
}
} else if (app != NULL) {
app->userJoin( m->getPrefixNick(), m->getPrefixName(), m->getPrefixHost());
}
}
if ((m->numParams >= 1) && m->params[0].IsSameAs(debugChannel)) {
if (m->getPrefixNick().IsSameAs(currentNick) && (state == 8)) {
state = 10; // next: WHO *
}
}
} else if (m->command.IsSameAs(wxT("PONG"))) {
if (state == 12) {
timer = pingTimer;
state = 11;
}
} else if (m->command.IsSameAs(wxT("PART"))) {
if ((m->numParams >= 1) && m->params[0].IsSameAs(channel)) {
if (app != NULL) {
app->userLeave( m->getPrefixNick() );
}
}
} else if (m->command.IsSameAs(wxT("KICK"))) {
if ((m->numParams >= 2) && m->params[0].IsSameAs(channel)) {
if (m->params[1].IsSameAs(currentNick)) {
// i was kicked!!
delete m;
return false;
} else if (app != NULL) {
app->userLeave( m->params[1] );
}
}
} else if (m->command.IsSameAs(wxT("QUIT"))) {
if (app != NULL) {
app->userLeave( m->getPrefixNick() );
}
} else if (m->command.IsSameAs(wxT("MODE"))) {
if ((m->numParams >= 3) && m->params[0].IsSameAs(channel)) {
if (app != NULL) {
size_t i;
wxString mode = m->params[1];
for (i = 1; (i < mode.Len()) && ((size_t) m->numParams >= (i+2)); i++) {
if ( mode[i] == wxT('o') ) {
if ( mode[0] == wxT('+') ) {
app->userChanOp(m->params[i+1], true);
} else if ( mode[0] == wxT('-') ) {
app->userChanOp(m->params[i+1], false);
}
}
} // for
}
}
} else if (m->command.IsSameAs(wxT("PRIVMSG"))) {
if ((m->numParams == 2) && (app != NULL)) {
if (m->params[0].IsSameAs(channel)) {
app->msgChannel(m);
} else if (m->params[0].IsSameAs(currentNick)) {
app->msgQuery(m);
}
}
} else if (m->command.IsSameAs(wxT("352"))) { // WHO list
if ((m->numParams >= 7) && m->params[0].IsSameAs(currentNick)
&& m->params[1].IsSameAs(channel)) {
if (app != NULL) {
app->userJoin( m->params[5], m->params[2], m->params[3]);
app->userChanOp ( m->params[5], m->params[6].IsSameAs(wxT("H@")));
}
}
} else if (m->command.IsSameAs(wxT("433"))) { // nick collision
if (state == 2) {
state = 3; // nick collision, choose new nick
timer = 10; // wait 5 seconds..
}
} else if (m->command.IsSameAs(wxT("332")) ||
m->command.IsSameAs(wxT("TOPIC"))) { // topic
if ((m->numParams == 2) && (app != NULL) &&
m->params[0].IsSameAs(channel) ) {
app->setTopic(m->params[1]);
}
}
delete m;
}
IRCMessage * m;
switch (state) {
case 1:
m = new IRCMessage();
m->command = wxT("PASS");
m->numParams = 1;
m->params.Add(password);
sendQ->putMessage(m);
m = new IRCMessage();
m->command = wxT("NICK");
m->numParams = 1;
m->params.Add(currentNick);
sendQ->putMessage(m);
timer = 10; // wait for possible nick collision message
state = 2;
break;
case 2:
if (timer == 0) {
m = new IRCMessage();
m->command = wxT("USER");
m->numParams = 4;
m->params.Add(name);
m->params.Add(wxT("0"));
m->params.Add(wxT("*"));
m->params.Add(versionInfo);
sendQ->putMessage(m);
timer = 30;
state = 4; // wait for login message
}
break;
case 3:
if (timer == 0) {
chooseNewNick();
m = new IRCMessage();
m->command = wxT("NICK");
m->numParams = 1;
m->params.Add(currentNick);
sendQ->putMessage(m);
timer = 10; // wait for possible nick collision message
state = 2;
}
break;
case 4:
if (timer == 0) {
// no login message received -> disconnect
return false;
}
break;
case 5:
m = new IRCMessage();
m->command = wxT("JOIN");
m->numParams = 1;
m->params.Add(channel);
sendQ->putMessage(m);
timer = 30;
state = 6; // wait for join message
break;
case 6:
if (timer == 0) {
// no join message received -> disconnect
return false;
}
break;
case 7:
if (debugChannel.Len() == 0) {
return false; // this state cannot be processed if there is no debug_channel
}
m = new IRCMessage();
m->command = wxT("JOIN");
m->numParams = 1;
m->params.Add(debugChannel);
sendQ->putMessage(m);
timer = 30;
state = 8; // wait for join message
break;
case 8:
if (timer == 0) {
// no join message received -> disconnect
return false;
}
break;
case 10:
m = new IRCMessage();
m->command = wxT("WHO");
m->numParams = 2;
m->params.Add(channel);
m->params.Add(wxT("*"));
sendQ->putMessage(m);
timer = pingTimer;
state = 11; // wait for timer and then send ping
if (app != NULL) {
app->setSendQ(sendQ); // this switches the application on
}
break;
case 11:
if (timer == 0) {
m = new IRCMessage();
m->command = wxT("PING");
m->numParams = 1;
m->params.Add(currentNick);
sendQ->putMessage(m);
timer = pingTimer;
state = 12; // wait for pong
}
break;
case 12:
if (timer == 0) {
// no pong message received -> disconnect
return false;
}
break;
}
return true;
}