Imported the QT4 IRC Client project, along with the mIRC script parser

that will eventually be integrated into it


git-svn-id: file:///srv/svn/ircclient/trunk@1 a9804ffe-773b-11dd-bd7c-89c3ef1d2733
This commit is contained in:
Correl Roush 2008-08-31 09:08:24 +00:00
commit 0381859219
24 changed files with 1315 additions and 0 deletions

51
guiapp/chatwindow.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef CHATWINDOW_H
#define CHATWINDOW_H
#include "ui_chatwindow.h"
#include <QScrollBar>
class ChatWindow : public QWidget, public Ui::ChatWindow {
Q_OBJECT
private:
QString filter;
QString defaultCmd;
public:
ChatWindow( QWidget *parent = 0 ) : QWidget( parent ) {
};
void setupUi( QWidget *ChatWindow = 0 ) {
Ui::ChatWindow::setupUi( ChatWindow );
connect( input, SIGNAL( returnPressed() ), this, SLOT( processCmd() ) );
}
public slots:
void setMessageFilter( QString filter ) { this->filter = filter; }
void setDefaultCmd( QString cmd ) { this->defaultCmd = cmd; }
void message( QString src, QString message ) {
display->append( "<" + src + "> " + message );
display->verticalScrollBar()->setValue( display->verticalScrollBar()->maximum() );
}
void echo( QString text ) {
display->append( text );
display->verticalScrollBar()->setValue( display->verticalScrollBar()->maximum() );
}
void processCmd() {
QString cmd = defaultCmd;
QStringList args = input->text().trimmed().split( " " );
if( args.count() < 1 ) { return; }
if( args[0].startsWith( '/' ) ) {
cmd = args.takeFirst();
cmd = cmd.right( cmd.length() - 1 );
}
emit doCmd( cmd + " " + args.join( " " ) );
input->clear();
}
signals:
void doCmd( QString cmd );
};
#endif

36
guiapp/chatwindow.ui Normal file
View file

@ -0,0 +1,36 @@
<ui version="4.0" >
<class>ChatWindow</class>
<widget class="QWidget" name="ChatWindow" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle" >
<string/>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="QTextBrowser" name="display" >
<property name="styleSheet" >
<string/>
</property>
<property name="html" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;/p>&lt;/body>&lt;/html></string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="input" />
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

16
guiapp/guiapp.pro Normal file
View file

@ -0,0 +1,16 @@
######################################################################
# Automatically generated by qmake (2.01a) Tue Aug 14 00:00:17 2007
######################################################################
TEMPLATE = app
TARGET = guiapp
DESTDIR = bin
DEPENDPATH += .
INCLUDEPATH += .
QT += network
# Input
HEADERS += chatwindow.h messagehandler.h ../src/ircclient.h ../src/dccserver.h
FORMS += chatwindow.ui
SOURCES += main.cpp messagehandler.cpp ../src/ircclient.cpp ../src/dccserver.cpp
TRANSLATIONS += guiapp_en.ts

34
guiapp/guiapp_en.ts Normal file
View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1" language="en">
<defaultcodec></defaultcodec>
<context>
<name>ChatWindow</name>
<message>
<location filename="chatwindow.ui" line="22"/>
<source>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Sans Serif&apos;; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Sans Serif&apos;; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
</context>
<context>
<name>IRCClient</name>
<message>
<location filename="../src/ircclient.cpp" line="35"/>
<source>Quit</source>
<translation>Quit</translation>
</message>
</context>
<context>
<name>MessageHandler</name>
<message>
<location filename="messagehandler.cpp" line="61"/>
<source>Changing servers</source>
<translation>Changing Servers</translation>
</message>
</context>
</TS>

18
guiapp/main.cpp Normal file
View file

@ -0,0 +1,18 @@
#include <QDialog>
#include "../src/ircclient.h"
#include "chatwindow.h"
#include "messagehandler.h"
int main( int argc, char *argv[] ) {
QApplication app( argc, argv );
IRCClient *irc = new IRCClient();
MessageHandler *mh = new MessageHandler( irc );
QObject::connect( &app, SIGNAL( aboutToQuit() ), irc, SLOT( quit() ) );
mh->show();
irc->connectAndRegister( argc > 1 ? argv[1] : "dev1", 6667, argc > 2 ? argv[2] : "Test", "correlr", "Correl Roush" );
return app.exec();
}

215
guiapp/messagehandler.cpp Normal file
View file

@ -0,0 +1,215 @@
#include "messagehandler.h"
#include <QFileInfo>
MessageHandler::MessageHandler( IRCClient *irc, QWidget *parent ) : QTabWidget( parent ) {
this->irc = irc;
connect( irc, SIGNAL( messageRcvd( QString, QString, QString, QStringList, QString ) ), this, SLOT( messageRcvd( QString, QString, QString, QStringList, QString ) ) );
connect( irc, SIGNAL( registered() ), this, SLOT( autoRun() ) );
connect( irc, SIGNAL( sentRAW( QString ) ), this, SLOT( sentRAW( QString ) ) );
connect( irc, SIGNAL( rcvdRAW( QString ) ), this, SLOT( rcvdRAW( QString ) ) );
this->setTabPosition( QTabWidget::South );
_addWindow( "status", "", true );
DCCPortMin = 1024;
DCCPortMax = 5000;
DCCSendAhead = true;
// Set up variables
variables["address"] = "$ip";
}
void MessageHandler::_addWindow( QString name, QString defaultCmd, bool focusOnOpen ) {
if( windows.keys().contains( name ) ) { return; }
int index = this->addTab( new ChatWindow(), name );
windows[name] = (ChatWindow*)this->widget( index );
windows[name]->setDefaultCmd( defaultCmd );
connect( windows[name], SIGNAL( doCmd( QString ) ), this, SLOT( processCmd( QString ) ) );
windows[name]->setupUi( windows[name] );
if( focusOnOpen ) {
this->setCurrentIndex( index );
windows[name]->input->setFocus();
}
}
QString MessageHandler::parseVar( QString text ) {
if( !text.startsWith( '$' ) ) { return text; }
QString var = text.right( text.length() - 1 ).toLower();
QString value;
if( var == "ip" ) { value = irc->getIPAddress(); }
if( var == "nick" || var == "nickname" ) { value = irc->getNickName(); }
if( value.isNull() && variables.keys().contains( var ) ) { value = variables[var].toString(); }
return ( value.isNull() || value == text ) ? text : parseVar( value ); // Allows for recursive vars, while avoiding an infinite loop
}
void MessageHandler::messageRcvd( QString type, QString src, QString dest, QStringList values, QString text ) {
QString window = "status";
QString source = "server";
QString message = text;
if( src.startsWith( ':' ) ) { src = src.right( src.length() - 1 ); }
// Handle special messages first
if( type == "JOIN" ) {
_addWindow( text, "msg " + text, true );
}
if( !dest.isEmpty() && dest.startsWith( '#' ) ) {
window = dest;
}
if( !src.isEmpty() && src.contains( '!' ) && src.contains( '@' ) ) {
QStringList split = src.split( '!' );
source = split[0];
if( dest == irc->getNickName() && type == "PRIVMSG" ) {
window = source;
}
}
if( type == "CTCP" ) {
values = text.split( " " );
QString ctcpCommand = values.takeFirst().toUpper();
if( ctcpCommand == "VERSION" ) {
irc->notice( source, tr( "Atma2 IRC Client %1" ).arg( 0.01 ) );
}
}
//emit dispatchMessage( window, src, message );
_addWindow( window, "msg " + window );
windows[window]->message( source, message );
}
void MessageHandler::messageSentDCC( QString nickName, QString text ) {
QString window = nickName.prepend( "=" );
_addWindow( window, "msg " + window );
windows[window]->message( irc->getNickName(), text );
}
void MessageHandler::messageRcvdDCC( QString nickName, QString text ) {
QString window = "=" + nickName;
_addWindow( window, "msg " + window );
windows[window]->message( nickName, text );
}
void MessageHandler::processCmd( QString cmdText ) {
qDebug() << cmdText;
QStringList args = cmdText.split( ' ' );
QStringList newargs;
QString cmd = args.takeFirst().toLower();
for( int i = 0; i < args.count(); i++ ) {
if( args[i].startsWith( '$' ) ) {
args[i] = parseVar( args[i] );
//QString var = args[i].right( args[i].length() - 1 );
//if( variables.contains( var ) ) { args[i] = variables[var].toString(); }
}
}
if( cmd == "server" || cmd == "connect" ) {
if( args.count() < 1 ) { return; }
irc->quit( tr( "Changing servers" ) );
QString host = args.takeFirst();
int port = 0;
if( host.contains( ':' ) ) {
QStringList splitHost = host.split( ':' );
host = splitHost.takeFirst();
port = splitHost.join( "" ).toInt();
}
port = port > 0 ? port : 6667;
QString nickName = args.count() > 0 ? args.takeFirst() : irc->getNickName();
QString userName = args.count() > 0 ? args.takeFirst() : irc->getUserName();
QString realName = args.count() > 0 ? args.join( " " ) : irc->getRealName();
irc->connectAndRegister(
host, // Host
port > 0 ? port : 6667, // Port
nickName,
userName,
realName );
} else if( cmd == "nick" ) {
if( args.count() < 1 ) { return; }
irc->nick( args[0] );
} else if( cmd == "msg" || cmd == "privmsg" ) {
if( args.count() < 2 ) { return; }
newargs += args.takeFirst();
if( newargs[0].startsWith( "=") ) {
// DCC Chat
((DCCChatServer*)DCCServers[DCCNicks[newargs[0].right( newargs[0].length() - 1 )]])->sendText( args.join( " " ) );
} else {
irc->msg( newargs[0], args.join( " " ) );
}
// Update existing chat windows with the sent message
QString window;
QStringList windows = newargs[0].split( "," );
foreach( window, windows ) {
if( !window.startsWith( "=" ) && this->windows.keys().contains( window ) ) { this->windows[window]->message( irc->getNickName(), args.join( " " ) ); }
}
} else if( cmd == "notice" ) {
if( args.count() < 2 ) { return; }
QString dest = args.takeFirst();
irc->notice( dest, args.join( " " ) );
} else if( cmd == "ctcp" ) {
if( args.count() < 2 ) { return; }
newargs += args.takeFirst();
irc->ctcp( newargs[0], args.join( " " ) );
} else if( cmd == "join" ) {
irc->join( args.join( "," ) );
} else if( cmd == "set" ) {
QString var = args.takeFirst();
variables[var] = args.join( " " );
} else if( cmd == "dcc" ) {
if( args.count() < 2 ) { return; }
QString type = args.takeFirst();
QString nick = args.takeFirst();
if( type == "chat" ) {
startDCC( nick );
} else if( type == "send" ) {
if( args.count() == 0 ) { return; }
startDCC( nick, args.join( " " ) );
}
} else if( cmd == "quit" || cmd == "q" ) {
if( args.count() > 0 ) { irc->quit( args.join( " " ) ); }
else { irc->quit(); }
}
}
void MessageHandler::autoRun() {
//irc->join( "#pinet" );
}
void MessageHandler::startDCC( QString nickName, QString fileName ) {
for( int i = DCCPortMin; i <= DCCPortMax; i++ ) {
if( !DCCServers.keys().contains( i ) ) {
QString message = "DCC %1";
if( fileName.isEmpty() ) {
// Chat session
DCCServers[i] = new DCCChatServer( nickName );
connect( ((DCCChatServer*)DCCServers[i]), SIGNAL( connectedChat( QString ) ), this, SLOT( connectedDCCChat( QString ) ) );
DCCNicks[nickName] = i;
message = message.arg( "CHAT chat %1 %2" );
} else {
// File Send
QFileInfo fileInfo = QFileInfo( fileName );
if( !fileInfo.exists() ) { return; }
DCCServers[i] = new DCCFileServer( fileName, DCCSendAhead );
message = message.arg( "SEND %1 %3 %2" ).arg( fileInfo.fileName() ).arg( fileInfo.size() ).arg( "%1 %2" );
}
bool success = DCCServers[i]->listen( QHostAddress::Any, i );
qDebug() << QString( "Listen on port %1: %2" ).arg( i ).arg( success );
message = message.arg( irc->localHost.toIPv4Address() ).arg( i );
qDebug() << message;
irc->ctcp( nickName, message );
break;
}
}
}
void MessageHandler::connectedDCCChat( QString nickName ) {
DCCChatServer *dcc = ((DCCChatServer*)DCCServers[DCCNicks[nickName]]);
connect( dcc, SIGNAL( rcvdText( QString, QString ) ), this, SLOT( messageRcvdDCC( QString, QString ) ) );
connect( dcc, SIGNAL( sentText( QString, QString ) ), this, SLOT( messageSentDCC( QString, QString ) ) );
QString window = nickName.prepend( "=" );
_addWindow( window, "msg " + window );
windows[window]->echo( "DCC Chat Connected" );
}
void MessageHandler::sentRAW( QString text ) { qDebug() << ">>> " << text.trimmed(); }
void MessageHandler::rcvdRAW( QString text ) { qDebug() << "<<< " << text.trimmed(); }

39
guiapp/messagehandler.h Normal file
View file

@ -0,0 +1,39 @@
#include <QTabWidget>
#include <QString>
#include <QStringList>
#include <QPointer>
#include "../src/ircclient.h"
#include "../src/dccserver.h"
#include "chatwindow.h"
class MessageHandler : public QTabWidget {
Q_OBJECT
private:
IRCClient *irc;
QHash< QString, ChatWindow* > windows;
QHash< QString, QVariant > variables;
int DCCPortMin;
int DCCPortMax;
bool DCCSendAhead;
QHash< int, DCCServer* > DCCServers;
QHash< QString, int > DCCNicks;
void _addWindow( QString name, QString defaultCmd = "", bool focusOnOpen = false );
QString parseVar( QString text );
public:
MessageHandler( IRCClient *irc, QWidget *parent = 0 );
public slots:
void messageRcvd( QString type, QString src, QString dest, QStringList values, QString text );
void messageSentDCC( QString nickName, QString text );
void messageRcvdDCC( QString nickName, QString text );
void processCmd( QString cmdText );
void autoRun();
void startDCC( QString nickName, QString fileName = "" );
void connectedDCCChat( QString nickName );
void rcvdRAW( QString text );
void sentRAW( QString text );
signals:
void dispatchMessage( QString window, QString src, QString message );
};

13
ircclient.pro Normal file
View file

@ -0,0 +1,13 @@
######################################################################
# Automatically generated by qmake (2.01a) Mon Aug 13 14:31:09 2007
######################################################################
TEMPLATE = lib
TARGET = ircclient
DESTDIR = bin
DEPENDPATH += . src
INCLUDEPATH += . src
QT += network
# Input
HEADERS += src/ircclient.h
SOURCES += src/ircclient.cpp

18
mirc/includes/mirc.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef MIRC_H
#define MIRC_H
#include <QObject>
#include <QString>
#include <QMap>
#include "script.h"
class MIRCScriptManager : public QObject {
Q_OBJECT
public:
//QMap<QString, QString> variables;
MIRCScriptManager(QObject *parent);
};
#endif

269
mirc/includes/parser.h Normal file
View file

@ -0,0 +1,269 @@
/*
Mercenary
mIRC Scripting Language Parser
*/
//#define BOOST_SPIRIT_DEBUG
#include <string>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/spirit/core.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/dynamic/if.hpp>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QMap>
#include <QStack>
using namespace std;
using namespace boost;
using namespace boost::spirit;
typedef QMap<QString, QString> mirc_variables;
struct mirc_alias {
mirc_alias(bool _global = true) {
global = _global;
}
bool global;
QString code;
};
typedef QMap<QString, mirc_alias> mirc_aliases;
enum mirc_engine_stage {
PARSE,
EXECUTE
};
class mirc_script_engine : public QObject {
Q_OBJECT
private:
mirc_aliases::iterator current_alias;
mirc_variables::iterator current_variable;
QStack<QStringList> stack;
public:
mirc_engine_stage stage;
mirc_alias script;
mirc_aliases aliases;
mirc_variables vars;
mirc_script_engine() : script() {
stage = PARSE;
current_alias = aliases.end();
current_variable = vars.end();
}
void handle_alias_definition(char const* str, char const* end) {
if (stage != PARSE) return;
string s(str, end);
aliases.insert(s.c_str(), mirc_alias(true));
current_alias = aliases.find(s.c_str());
}
void handle_alias_definition_local(char const* str, char const* end) {
if (stage != PARSE) return;
string s(str, end);
aliases.insert(s.c_str(), mirc_alias(false));
current_alias = aliases.find(s.c_str());
}
void close_alias(char const*, char const*) {
if (stage != PARSE) return;
if (!aliases.empty() && current_alias != aliases.end()) {
current_alias = aliases.end();
}
}
void store_code(char const* str, char const* end) {
if (stage != PARSE) return;
string s(str, end);
if (!aliases.empty() && current_alias != aliases.end()) {
current_alias->code.append(s.c_str()).append("\n");
} else {
script.code.append(s.c_str()).append("\n");
}
}
void call_alias(char const*, char const*) {
if (stage != EXECUTE) return;
}
void return_alias(char const*, char const*) {
}
void declare_variable(char const* str, char const* end) {
if (stage != EXECUTE) return;
string s(str, end);
vars.insert(s.c_str(), "");
current_variable = vars.find(s.c_str());
stack.push(QStringList());
}
void assign_variable(char const* str, char const* end) {
if (stage != EXECUTE) return;
if (current_variable != vars.end()) {
string s(str, end);
*current_variable = (!stack.isEmpty() ? stack.pop().join(" ") : "");
current_variable = vars.end();
}
}
void fetch_variable(char const*, char const*) {
if (stage != EXECUTE) return;
}
void append_expression(char const* str, char const *end) {
if (stage != EXECUTE) return;
string s(str, end);
QStringList list;
if (stack.isEmpty()) {
list << s.c_str();
} else {
list = stack.pop();
list << s.c_str();
}
stack.push(list);
}
};
struct mirc_script : public grammar<mirc_script> {
mirc_script_engine *actions;
mirc_script( mirc_script_engine *_actions ) {
actions = _actions;
}
template <typename ScannerT>
struct definition {
rule<ScannerT> script,
space,
identifier,
string,
expression,
expression_group,
parameters,
variable,
assignment,
alias_action,
alias_function,
alias_definition,
code_line,
code_block,
comment;
definition(mirc_script const &self) {
typedef function< void(const char*, const char*) > s_action;
s_action a_def ( bind( &mirc_script_engine::handle_alias_definition, self.actions, _1, _2 ) );
s_action l_def ( bind( &mirc_script_engine::handle_alias_definition_local, self.actions, _1, _2 ) );
s_action a_close ( bind( &mirc_script_engine::close_alias, self.actions, _1, _2 ) );
s_action v_def ( bind( &mirc_script_engine::declare_variable, self.actions, _1, _2 ) );
s_action v_assign ( bind( &mirc_script_engine::assign_variable, self.actions, _1, _2 ) );
s_action e_append ( bind( &mirc_script_engine::append_expression, self.actions, _1, _2 ) );
s_action s_code ( bind( &mirc_script_engine::store_code, self.actions, _1, _2 ) );
script
= *(
alias_definition[a_close]
| code_block
) >> end_p
;
space
= ( blank_p
//| (ch_p('\\') >> eol_p)
| str_p("$&") >> *blank_p >> eol_p
)
;
identifier
= alpha_p >> *alnum_p
;
string
= ( variable
| alias_function
| +(graph_p - ch_p(',') - ch_p('(') - ch_p(')'))
)[e_append]
;
expression
= string >> *(*space >> string)
;
expression_group
= expression | expression_group
;
parameters
= expression >> *(*space >> ch_p(',') >> *space >> expression)
;
variable
= ch_p('%') >> identifier
;
assignment
= !(str_p("var") >> *space)
>> variable[v_def]
>> *space >> ch_p('=') >> *space
>> expression[v_assign]
;
alias_action
= !ch_p('/') >> !ch_p('/')
>> identifier >> *space >> parameters
;
alias_function
= ch_p('$') >> identifier
>> !(
ch_p('(') >> *space
>> parameters >> *space
/*
>> (expression - ch_p(','))
>> *( *space
>> ch_p(',') >> *space
>> (expression - ch_p(','))
)
*/
>> ch_p(')')
)
;
alias_definition
= str_p("alias") >> *space
//>> !(str_p("-l") >> *space)
>> if_p(str_p("-l") >> *space)[identifier[l_def]].else_p[identifier[a_def]]
>> *space >> !eol_p
>> code_block
;
code_line
= *space
>> ( comment
| (
assignment /* Must come first to avoid "var" being caught as an action */
| alias_action
)[s_code]
)
>> !eol_p
;
code_block
= ( *space >> ch_p('{') >> *space >> !eol_p
>> (*code_line)
>> ch_p('}') >> *space >> !eol_p
)
| code_line
;
comment = comment_p(";");
BOOST_SPIRIT_DEBUG_NODE(script);
BOOST_SPIRIT_DEBUG_NODE(space);
BOOST_SPIRIT_DEBUG_NODE(identifier);
BOOST_SPIRIT_DEBUG_NODE(string);
BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(variable);
BOOST_SPIRIT_DEBUG_NODE(assignment);
BOOST_SPIRIT_DEBUG_NODE(alias_action);
BOOST_SPIRIT_DEBUG_NODE(alias_function);
BOOST_SPIRIT_DEBUG_NODE(alias_definition);
BOOST_SPIRIT_DEBUG_NODE(code_line);
BOOST_SPIRIT_DEBUG_NODE(code_block);
BOOST_SPIRIT_DEBUG_NODE(comment);
BOOST_SPIRIT_DEBUG_NODE(*this);
}
rule<ScannerT> const& start() const { return script; }
};
};

29
mirc/includes/script.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef MIRC_SCRIPT_H
#define MIRC_SCRIPT_H
#include <QObject>
#include <QString>
#include <QMap>
#include "mirc.h"
#include "parser.h"
class MIRCScript : public QObject {
Q_OBJECT
private:
QString script;
QMap<QString, mirc_alias> _aliases;
QMap<QString, QString> _variables;
bool loaded;
public:
MIRCScript();
bool load(QString filename);
bool parse(QString script);
bool run();
bool run(QString alias);
QString code();
QString code(QString alias);
QMap<QString, mirc_alias> aliases();
QMap<QString, QString> variables();
};
#endif

16
mirc/mirc.pro Normal file
View file

@ -0,0 +1,16 @@
######################################################################
# Automatically generated by qmake (2.01a) Sat Aug 30 23:01:47 2008
######################################################################
TEMPLATE = app
# CONFIG += plugin
TARGET = mirc
DESTDIR = bin
DEPENDPATH += . includes src
INCLUDEPATH += . includes
# Input
HEADERS += includes/mirc.h includes/parser.h includes/script.h
SOURCES += src/mirc.cpp \
src/parser.cpp \
src/script.cpp

4
mirc/src/mirc.cpp Normal file
View file

@ -0,0 +1,4 @@
#include "mirc.h"
MIRCScriptManager::MIRCScriptManager(QObject *parent = 0) {
}

77
mirc/src/parser.cpp Normal file
View file

@ -0,0 +1,77 @@
#include <QTextStream>
#include "script.h"
int main() {
QTextStream output(stdout);
QString foo;
foo.append("; Hey, here's some test code\n");
foo.append("set name Correl\n");
foo.append("%name = Correl $&\n\tRoush\n");
foo.append("echo Hello, %name!\n");
foo.append("alias dostuff {\n");
foo.append(" ; Not very useful, but good for testing the parser!\n");
foo.append(" var %b = 42\n");
foo.append(" %b = $calc(%b * 3)\n");
foo.append(" return %b;\n");
foo.append("}\n");
foo.append("alias -l dosomethingelse {\n");
foo.append(" ; Useless local alias!\n");
foo.append(" echo -s Busy doing nothing\n");
foo.append("}\n");
foo.append("alias -l dosomethingelse {\n");
foo.append(" ; Useless local alias!\n");
foo.append(" echo -s Busy doing nothing\n");
foo.append("}\n");
output << "Code:\n"
<< "================================================================================\n"
<< foo
<< "================================================================================\n"
<< "\n";
mirc_script_engine *interpreter = new mirc_script_engine();
mirc_script parser(interpreter);
parse_info<> info = parse((const char*)foo.toLatin1(), parser);
if (info.full)
{
output << "-------------------------\n";
output << "Parsing succeeded\n";
output << "-------------------------\n";
MIRCScript *ms = new MIRCScript();
output << "Creating new MIRCScript object\n";
//if (ms->parse(foo)) {
if (ms->load("test.mrc")) {
output << "MRC LOADED\n";
output << "Code:\n" << ms->code();
output << "Aliases:\n";
QMapIterator<QString, mirc_alias> alias(ms->aliases());
while(alias.hasNext()) {
alias.next();
output << (alias.value().global ? "global" : "local");
output << " " << alias.key() << "\n";
}
if (ms->run()) {
output << "MRC RAN SUCCESSFULLY\n";
QMapIterator<QString, QString> i(ms->variables());
output << "Variables:\n";
while(i.hasNext()) {
i.next();
output << i.key() << " = " << i.value() << "\n";
}
}
}
}
else
{
output << "-------------------------\n";
output << "Parsing failed\n";
output << "stopped at: \": " << info.stop << "\"\n";
output << "-------------------------\n";
}
return 0;
}

61
mirc/src/script.cpp Normal file
View file

@ -0,0 +1,61 @@
#include "script.h"
#include <QFile>
MIRCScript::MIRCScript() {
}
bool MIRCScript::load(QString filename) {
QFile file(filename);
if (file.exists() && file.open(QIODevice::ReadOnly)) {
QString code(file.readAll());
file.close();
return parse(code);
} else {
return false;
}
}
bool MIRCScript::parse(QString code) {
mirc_script_engine *interpreter = new mirc_script_engine();
mirc_script parser(interpreter);
parse_info<> info = boost::spirit::parse((const char*)code.toLatin1(), parser);
loaded = info.full;
if (loaded) {
script = interpreter->script.code;
_aliases = interpreter->aliases;
}
return loaded;
}
bool MIRCScript::run() {
if (!loaded) return false;
mirc_script_engine *interpreter = new mirc_script_engine();
interpreter->stage = EXECUTE;
mirc_script parser(interpreter);
parse_info<> info = boost::spirit::parse((const char*)script.toLatin1(), parser);
if (info.full) {
_variables = interpreter->vars;
}
return info.full;
}
bool MIRCScript::run(QString alias) {
if (!loaded) return false;
return false;
}
QString MIRCScript::code() {
return script;
}
QString MIRCScript::code(QString alias) {
return QString();
}
QMap<QString, mirc_alias> MIRCScript::aliases() {
return _aliases;
}
QMap<QString, QString> MIRCScript::variables() {
return _variables;
}

15
mirc/test.mrc Normal file
View file

@ -0,0 +1,15 @@
; Hey, here's some test code
set name Correl
%name = Correl $&
Roush
echo Hello, %name!
alias dostuff {
; Not very useful, but good for testing the parser!
var %b = 42
%b = $calc(%b * 3)
return %b;
}
alias -l dosomethingelse {
; Useless local alias!
echo -s Busy doing nothing
}

77
src/dccserver.cpp Normal file
View file

@ -0,0 +1,77 @@
#include "dccserver.h"
DCCServer::DCCServer( QObject *parent ) : QTcpServer( parent ) {
connect( this, SIGNAL( newConnection() ), this, SLOT( connectClient() ) );
}
void DCCServer::connectClient() {
conn = nextPendingConnection();
connect( conn, SIGNAL( readyRead() ), this, SLOT( readSocket() ) );
close(); // No more than one client
}
DCCChatServer::DCCChatServer( QString nickName, QObject *parent ) : DCCServer( parent ) {
this->nickName = nickName.trimmed();
connect( this, SIGNAL( newConnection() ), this, SLOT( connectChatClient() ) );
}
void DCCChatServer::sendText( QString text ) {
emit sentText( nickName, text );
text.append( "\r\n" );
conn->write( text.toLatin1() );
}
void DCCChatServer::connectChatClient() {
emit connectedChat( nickName );
}
void DCCChatServer::readSocket() {
if( !conn->canReadLine() ) return;
char buf[512];
while( conn->readLine( buf, sizeof( buf ) ) > 0 )
emit rcvdText( nickName, QString( buf ).trimmed() );
}
DCCFileServer::DCCFileServer( QString fileName, bool sendAhead, QObject *parent ) : DCCServer( parent ) {
chunkSize = 1024;
sent = 0;
verified = 0;
this->sendAhead = sendAhead;
file.setFileName( fileName );
file.open( QIODevice::ReadOnly );
connect( this, SIGNAL( newConnection() ), this, SLOT( connectFileClient() ) );
}
void DCCFileServer::connectFileClient() {
emit connectedFile( conn->localPort() );
// Send the initial chunk of data
sendNextChunk();
}
void DCCFileServer::readSocket() {
char buf[4];
while( conn->bytesAvailable() >= 4 ) {
conn->read( buf, 4 );
quint32 v = *reinterpret_cast<quint32*>( &buf[0] );
v = qFromBigEndian( v );
verified = v;
if( sendAhead || verified == sent ) { sendNextChunk(); }
if( !file.isOpen() ) { conn->close(); }
}
}
void DCCFileServer::sendNextChunk() {
char buf[chunkSize];
if( !file.isOpen() || !file.isReadable() || !conn->isOpen() ) { return; }
qint64 bytesRead = 0;
while( ( sendAhead || sent == verified ) && ( bytesRead = file.read( buf, chunkSize ) ) ) {
if( bytesRead > 0 ) {
conn->write( buf, bytesRead );
}
sent += bytesRead;
if( conn->bytesAvailable() >= 4 ) { readSocket(); }
}
if( bytesRead < chunkSize && sent == verified ) {
file.close();
}
}

60
src/dccserver.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef DCCSERVER_H
#define DCCSERVER_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QtEndian>
#include <QFile>
class DCCServer: public QTcpServer {
Q_OBJECT
protected:
QTcpSocket *conn;
public:
DCCServer( QObject *parent = 0 );
protected slots:
void connectClient();
};
class DCCChatServer : public DCCServer {
Q_OBJECT
private:
QString nickName;
public:
DCCChatServer( QString nickName, QObject *parent = 0 );
private slots:
void connectChatClient();
void readSocket();
public slots:
void sendText( QString text );
signals:
void connectedChat( QString nickName );
void sentText( QString nickName, QString text );
void rcvdText( QString nickName, QString text );
};
class DCCFileServer : public DCCServer {
Q_OBJECT
private:
int port;
QFile file;
int chunkSize;
quint64 sent;
quint64 verified;
bool sendAhead;
public:
DCCFileServer( QString fileName, bool sendAhead = false, QObject *parent = 0 );
private slots:
void connectFileClient();
void readSocket();
void sendNextChunk();
signals:
void connectedFile( int port );
void progress( int port, double percentage );
void complete( int port, bool success );
};
#endif

121
src/ircclient.cpp Normal file
View file

@ -0,0 +1,121 @@
#include "ircclient.h"
#include <QDebug>
IRCClient::IRCClient( QObject *parent ) : QObject( parent ) {
conn = new QTcpSocket();
QObject::connect( conn, SIGNAL( readyRead() ), this, SLOT( readSocket() ) );
QObject::connect( conn, SIGNAL( connected() ), this, SLOT( _connected() ) );
QObject::connect( conn, SIGNAL( disconnected() ), this, SLOT( _disconnected() ) );
QObject::connect( this, SIGNAL( rcvdRAW( QString ) ), this, SLOT( parseMessage( QString ) ) );
nickName = userName = realName = "";
}
void IRCClient::connectAndRegister( QString host, uint port, QString nickName, QString userName, QString realName ) {
conn->abort();
conn->connectToHost( host, port );
this->nickName = nickName;
this->userName = userName;
this->realName = realName;
}
void IRCClient::raw( QString text ) {
// Send raw text to the IRC Server
text.append( "\r\n" );
conn->write( text.toLatin1() );
emit sentRAW( text );
}
void IRCClient::msg( QString dest, QString text ) { raw( "PRIVMSG " + dest + " :" + text ); }
void IRCClient::ctcp( QString dest, QString text ) { raw( "PRIVMSG " + dest + " :\001" + text + "\001" ); }
void IRCClient::notice( QString dest, QString text ) { raw( "NOTICE " + dest + " :" + text ); }
void IRCClient::join( QString channel ) { raw( "JOIN " + channel ); }
void IRCClient::quit( QString reason ) { raw( "QUIT :" + reason ); conn->flush(); }
void IRCClient::quit() { quit( tr( "Quit" ) ); }
void IRCClient::nick( QString nick ) { raw( "NICK " + nick ); }
void IRCClient::_connected() {
localHost = conn->localAddress();
emit connected();
// Register
raw( "USER " + userName + " +iw localhost :" + realName );
nick( nickName );
}
void IRCClient::_disconnected() { emit disconnected(); }
void IRCClient::closeSocket() {
emit disconnected();
}
void IRCClient::updateHostInfo( QHostInfo host ) {
if (host.error() != QHostInfo::NoError) {
qDebug() << "Lookup failed: " + host.errorString();
return;
}
foreach (QHostAddress address, host.addresses())
localHost = address;
qDebug() << "Host updated: " + localHost.toString();
}
void IRCClient::readSocket() {
if( !conn->canReadLine() ) return;
char buf[512];
while( conn->readLine( buf, sizeof( buf ) ) > 0 )
emit rcvdRAW( buf );
}
void IRCClient::parseMessage( QString text ) {
text = text.trimmed();
int split = text.indexOf( " :" );
QString message = split >= 0 ? text.left( split ) : text;
QString messageText = split >= 0 ? text.right( text.length() - split - 2 ) : "";
QString from = "";
QString type = "";
QString dest = "";
QStringList values;
QStringList parsedMessage = message.split( ' ' );
if( parsedMessage.count() > 0 ) {
if( message.startsWith( ':' ) ) from = parsedMessage.takeFirst();
if( parsedMessage.count() > 0 ) type = parsedMessage.takeFirst();
if( parsedMessage.count() > 0 ) dest = parsedMessage.takeFirst();
values = parsedMessage;
} else {
type = message;
}
// The following are special cases that can be taken care of immediately
// Anything else should probably be dealt with by an actual client
if( type == "001" ) {
emit registered();
// Fetch the hostname from the welcome text if it's there and perform a nonblocking dns lookup
QString hostMask = messageText.right( messageText.length() - messageText.lastIndexOf( ' ' ) - 1 );
if( hostMask.contains( '@' ) ) {
QString hostName = hostMask.right( hostMask.length() - hostMask.indexOf( '@' ) - 1 );
qDebug() << "Looking up " + hostName;
QHostInfo::lookupHost( hostName, this, SLOT( updateHostInfo( QHostInfo ) ) );
} else {
raw( "WHOIS " + nickName );
}
} else if( type == "311" ) {
// RPL_WHOIS
if( values.count() < 3 ) return;
if( values[0] == nickName ) {
qDebug() << "Looking up " + values[2];
QHostInfo::lookupHost( values[2], this, SLOT( updateHostInfo( QHostInfo ) ) );
}
} else if( type == "NICK" && from.startsWith( ":" + nickName + "!" ) ) {
nickName = messageText;
} else if( type == "PING" ) {
raw( "PONG :" + messageText );
emit pingPong();
} else if( type == "PRIVMSG" ) {
if( messageText.startsWith( "\001" ) && messageText.endsWith( "\001" ) ) {
// CTCP Message
type = "CTCP";
messageText = messageText.mid( 1, messageText.length() - 2 );
}
}
//qDebug() << "t" << type << "s" << from << "d" << dest << "v" << values.join( "," ) << "m" << messageText;
emit messageRcvd( type, from, dest, values, messageText );
}

65
src/ircclient.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef IRCCLIENT_H
#define IRCCLIENT_H
#include <QObject>
#include <QString>
#include <QStringList>
#include <QTcpSocket>
#include <QHostAddress>
#include <QHostInfo>
class IRCClient : public QObject {
Q_OBJECT
private:
QString nickName, userName, realName;
public:
QTcpSocket *conn;
QHostAddress localHost;
IRCClient( QObject *parent = 0 );
//void connect( QString host, uint port, QString nickName );
void connectAndRegister( QString host, uint port, QString nickName, QString userName, QString realName );
void raw( QString text );
void msg( QString dest, QString message );
void ctcp( QString dest, QString message );
void notice( QString dest, QString message );
void join( QString channel );
void nick( QString nick );
QString getNickName() { return nickName; }
QString getUserName() { return userName; }
QString getRealName() { return realName; }
QString getIPAddress() { return localHost.toString(); }
signals:
// Basic
void connected();
void disconnected();
void registered();
// Debug
void rcvdRAW( QString text );
void sentRAW( QString text );
void debug( QString text );
// Events
void pingPong();
void messageRcvd( QString type, QString src, QString dest, QStringList values, QString text );
public slots:
void quit( QString reason );
void quit(); // No reason...
void updateHostInfo( QHostInfo host );
private slots:
void _connected();
void _disconnected();
void closeSocket();
void readSocket();
void parseMessage( QString text );
};
#endif

8
testapp/main.cpp Normal file
View file

@ -0,0 +1,8 @@
#include <iostream>
#include <QCoreApplication>
#include "testapp.h"
int main( int argc, char *argv[] ) {
TestApp app( argc, argv );
return app.exec();
}

38
testapp/testapp.cpp Normal file
View file

@ -0,0 +1,38 @@
#include "testapp.h"
TestApp::TestApp( int argc, char *argv[] ) : QCoreApplication( argc, argv ) {
irc = new IRCClient();
connect( irc->conn, SIGNAL( connected() ), this, SLOT( connected() ) );
connect( irc->conn, SIGNAL( disconnected() ), this, SLOT( disconnected() ) );
connect( irc, SIGNAL( registered() ), this, SLOT( registered() ) );
connect( irc, SIGNAL( sentRAW( QString ) ), this, SLOT( sent( QString ) ) );
connect( irc, SIGNAL( rcvdRAW( QString ) ), this, SLOT( rcvd( QString ) ) );
connect( irc, SIGNAL( debug( QString ) ), this, SLOT( debug( QString ) ) );
irc->connectAndRegister( argc > 1 ? argv[1] : "dev1", 6667, argc > 2 ? argv[2] : "Test", "correlr", "Correl Roush" );
}
void TestApp::connected() {
printf( "Connected!\n" );
}
void TestApp::registered() {
irc->join( "#pinet" );
}
void TestApp::disconnected() {
printf( "Disconnected.\n" );
}
void TestApp::sent( QString text ) {
printf( ">>> %s", (const char*)text.toLatin1() );
}
void TestApp::rcvd( QString text ) {
printf( "<<< %s", (const char*)text.toLatin1() );
}
void TestApp::debug( QString text ) {
printf( "DEBUG: %s\n", (const char*)text.toLatin1() );
}

21
testapp/testapp.h Normal file
View file

@ -0,0 +1,21 @@
#include <iostream>
#include <QCoreApplication>
#include "../src/ircclient.h"
class TestApp : public QCoreApplication {
Q_OBJECT
public:
TestApp( int argc, char *argv[] );
private:
IRCClient *irc;
private slots:
void connected();
void disconnected();
void registered();
void sent( QString text );
void rcvd( QString text );
void debug( QString text );
};

14
testapp/testapp.pro Normal file
View file

@ -0,0 +1,14 @@
######################################################################
# Automatically generated by qmake (2.01a) Mon Aug 13 15:01:40 2007
######################################################################
TEMPLATE = app
TARGET = testapp
DESTDIR = bin
DEPENDPATH += .
INCLUDEPATH += .
QT += network
# Input
HEADERS += testapp.h ../src/ircclient.h
SOURCES += main.cpp testapp.cpp ../src/ircclient.cpp