summaryrefslogtreecommitdiffstats
path: root/ksirc/chanparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksirc/chanparser.cpp')
-rw-r--r--ksirc/chanparser.cpp1047
1 files changed, 1047 insertions, 0 deletions
diff --git a/ksirc/chanparser.cpp b/ksirc/chanparser.cpp
new file mode 100644
index 00000000..8429a6f5
--- /dev/null
+++ b/ksirc/chanparser.cpp
@@ -0,0 +1,1047 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "alistbox.h"
+#include "chanparser.h"
+#include "ksopts.h"
+#include "control_message.h"
+#include "ssfeprompt.h"
+#include "toplevel.h"
+#include "ksircprocess.h"
+#include "ksview.h"
+
+#include <stdio.h>
+
+#include <qregexp.h>
+#include <qapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <qptrlist.h>
+
+// Static parser table is "initialized"
+QDict<parseFunc> ChannelParser::parserTable;
+
+
+ChannelParser::ChannelParser(KSircTopLevel *_top)
+{
+ top = _top;
+
+ /*
+ * Initial helper variables
+ */
+ prompt_active = false;
+ current_item = -1;
+ top_item = 0;
+
+ if(parserTable.isEmpty() == TRUE){
+ parserTable.setAutoDelete(TRUE);
+ parserTable.insert("`l`", new parseFunc(&ChannelParser::parseSSFEClear));
+ parserTable.insert("`s`", new parseFunc(&ChannelParser::parseSSFEStatus));
+ parserTable.insert("`i`", new parseFunc(&ChannelParser::parseSSFEInit));
+ parserTable.insert("`t`", new parseFunc(&ChannelParser::parseSSFEMsg));
+ parserTable.insert("`o`", new parseFunc(&ChannelParser::parseSSFEOut));
+ parserTable.insert("`p`", new parseFunc(&ChannelParser::parseSSFEPrompt));
+ parserTable.insert("`P`", new parseFunc(&ChannelParser::parseSSFEPrompt));
+ parserTable.insert("`R`", new parseFunc(&ChannelParser::parseSSFEReconnect));
+ // The rest are *** info message
+ parserTable.insert("***", new parseFunc(&ChannelParser::parseINFOInfo));
+ parserTable.insert("*E*", new parseFunc(&ChannelParser::parseINFOError));
+ parserTable.insert("*!*", new parseFunc(&ChannelParser::parseINFONicks)); // Normal
+ parserTable.insert("*C*", new parseFunc(&ChannelParser::parseINFONicks)); // 1st line
+ parserTable.insert("*c*", new parseFunc(&ChannelParser::parseINFONicks)); // Last line
+ parserTable.insert("*#*", new parseFunc(&ChannelParser::parseINFONicks)); // Non enhanced
+ parserTable.insert("*$*", new parseFunc(&ChannelParser::parseINFONicks)); // Enhanced turned off
+ parserTable.insert("*>*", new parseFunc(&ChannelParser::parseINFOJoin));
+ parserTable.insert("*<*", new parseFunc(&ChannelParser::parseINFOPart));
+ parserTable.insert("*N*", new parseFunc(&ChannelParser::parseINFOChangeNick));
+ parserTable.insert("*+*", new parseFunc(&ChannelParser::parseINFOMode));
+ parserTable.insert("*T*", new parseFunc(&ChannelParser::parseINFOTopic));
+ // End of info message
+ parserTable.insert("* ", new parseFunc(&ChannelParser::parseCTCPAction));
+ }
+
+}
+
+parseResult *ChannelParser::parse(QString string)
+{
+ // for older Qts
+
+ parseFunc *pf;
+ if(string.length() < 3){
+ return new parseError(string, QString("Dumb string, too short"));
+ }
+
+ /**
+ * Start pre-parsing the strings to make them fit the 3 character
+ * parser codes, etc
+ */
+
+ /*
+ * SSFE control messages are too long, so we convert the
+ * messges into a 3 character code, `#ssfe#\S becomes `\S`
+ */
+
+ if ((string[0] == '`') & (string.length() > 7))
+ {
+ QString prefix = "`"+string[7]+"`";
+ string = prefix + QString(" ") + string.mid(8).stripWhiteSpace();
+ }
+ else if((string[0] == '*') && (string[1] == ' ')) {
+ string.insert(1, ' ');
+ }
+ // Pre-parsing is now complete
+
+ pf = parserTable[string.mid(0, 3)];
+ if(pf != 0x0){
+ parseResult *result = (this->*(pf->parser))(string);
+
+ if (result)
+ return result;
+ }
+ else
+ {
+ // debug("No handler for: %s", string.data());
+ }
+
+ // Little bit of past parsing to catch one we've missed
+ if((string[0] == '*') && (string[2] == '*')) {
+ string.remove(0, 3);
+ return new parseSucc(string, ksopts->infoColor, "user|servinfo");
+ }
+ // If it's unkown we just fall out of the function
+ return 0;
+}
+
+parseResult * ChannelParser::parseSSFEClear(QString string)
+{
+// top->mainw->clear();
+ top->clearWindow();
+// top->mainw->repaint(TRUE);
+ string.truncate(0);
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEStatus(QString string)
+{
+ string.remove(0, 12); // strip off the first 12 characters "<junk> [sirc] "
+ if(string.length() == 0)
+ return new parseError("", i18n("Unable to parse status string"));
+
+ //kdDebug(5008) << "String: " << string << endl;
+ QRegExp rx("(\\S+).*\\(*([+-]*[+-\\w\\d]*)\\)*.*on (\\S+) \\((\\S+)\\)");
+ if(rx.search(string) == -1){
+ return new parseError("", i18n("Unable to parse status (no known format) string"));
+ }
+
+ QString nick = rx.cap(1);
+ QString modes = rx.cap(2);
+ QString chan = rx.cap(3);
+ QString chanmode = rx.cap(4);
+
+ /*
+ * fix up modes which may have gotten the (away) part
+ */
+ if(modes.contains("away")){
+ modes = "";
+ }
+
+ bool away = false;
+ if(string.contains("(away)"))
+ away = true;
+
+ if(away){
+ chan.prepend(i18n("Away-"));
+ }
+
+ nickListItem *nickItem = top->nicks->item( top->nicks->findNick( nick ) );
+ if ( nickItem ) {
+ if(nickItem->away() != away){
+ nickItem->setAway( away );
+ top->nicks->viewport()->repaint( top->nicks->itemRect( nickItem ), false );
+ }
+ nickItem->forceColour(&ksopts->ownNickColor);
+ }
+
+ top->ksircProcess()->setNick(nick);
+ if (chanmode.findRev("t") != -1)
+ top->channelButtons->setProtectMode(true);
+ else top->channelButtons->setProtectMode(false);
+ if (chanmode.findRev("m") != -1)
+ top->channelButtons->setModerateMode(true);
+ else top->channelButtons->setModerateMode(false);
+ if (chanmode.findRev("n") != -1)
+ top->channelButtons->setNooutsideMode(true);
+ else top->channelButtons->setNooutsideMode(false);
+ if (chanmode.findRev("i") != -1)
+ top->channelButtons->setMenuItemMode(0, true);
+ else top->channelButtons->setMenuItemMode(0, false);
+ if (chanmode.findRev("s") != -1)
+ top->channelButtons->setMenuItemMode(3, true);
+ else top->channelButtons->setMenuItemMode(3, false);
+
+ if (modes.findRev("i") != -1)
+ top->channelButtons->setMenuItemMode(4, true);
+ else top->channelButtons->setMenuItemMode(4, false);
+ if (modes.findRev("w") != -1)
+ top->channelButtons->setMenuItemMode(5, true);
+ else top->channelButtons->setMenuItemMode(5, false);
+ if (modes.findRev("s") != -1)
+ top->channelButtons->setMenuItemMode(6, true);
+ else top->channelButtons->setMenuItemMode(6, false);
+
+ QString status_line = QString("%1 (%2) %3 (%4) ").arg(chan).arg(chanmode).arg(nick).arg(modes);
+
+ /*
+ * Go srearching for key and limit messages
+ */
+ QRegExp rxKey("<key: (\\S+)>");
+ if(rxKey.search(string) >= 0){
+ top->channelButtons->setMenuItemMode(2, true);
+ status_line += QString("<key: %1>").arg(rxKey.cap(1));
+ }
+ else {
+ top->channelButtons->setMenuItemMode(2, false);
+ }
+
+ QRegExp rxLimit("<limit: (\\S+)>");
+ if(rxLimit.search(string) >= 0){
+ top->channelButtons->setMenuItemMode(1, true);
+ status_line += QString("<limit: %1>").arg(rxLimit.cap(1));
+ }
+ else {
+ top->channelButtons->setMenuItemMode(1, false);
+ }
+
+ if(ksopts->displayTopic){
+ if(top->topic().length() > 0)
+ status_line += "T: " + top->topic();
+ else
+ status_line += "T: " + i18n("<No Topic Set>");
+ }
+
+ if(top->caption != status_line){
+ if(nick[0] == '@' || (nick[0] == '*' && nick[1] == '@')) {
+ // If we're an op,,
+ // update the nicks popup menu
+ top->channelButtons->setButtonsEnabled(true); // set the buttons enabled if were an op
+ top->opami = TRUE;
+ } // opami = true sets us to an op
+ else {
+ top->channelButtons->setButtonsEnabled(false); // set the buttons enabled if were an op
+ top->opami = FALSE;
+ } // FALSE, were not an ops
+ top->UserUpdateMenu(); // update the menu
+ top->setCaption(status_line);
+ top->setIconText(status_line);
+ if(top->ticker) {
+ top->ticker->setCaption(status_line);
+ }
+ top->caption = status_line; // Make copy so we're not
+ // constantly changing the title bar
+ }
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEInit(QString)
+{
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEOut(QString)
+{
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEMsg(QString string)
+{
+
+ if(string.length() > 100)
+ return new parseError(QString::null, i18n("String length for nick is greater than 100 characters. This is unacceptably long."));
+
+ int l = string.length();
+ if (l <= 0)
+ return new parseError(string, i18n("String not long enough"));
+
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+
+parseResult * ChannelParser::parseSSFEPrompt(QString string)
+{
+
+ if(prompt_active == FALSE){
+ QString prompt, caption;
+ ssfePrompt *sp;
+
+ // Flush the screen.
+ // First remove the prompt message from the Buffer.
+ // (it's garunteed to be the first one)
+ top->LineBuffer.remove( *top->LineBuffer.begin() );
+ top->Buffer = FALSE;
+ top->sirc_receive(QString(""));
+
+ // "'[pP]' " gives 4 spaces
+ if(string.length() < 5)
+ caption = i18n("");
+ else
+ caption = string.mid(3);
+ prompt_active = TRUE;
+
+ // If we use this, then it blows up
+ // if we haven't popped up on the remote display yet.
+
+ KSirc::TextParagIterator it = top->mainw->firstParag();
+ QString last;
+ while(it.atEnd() == FALSE) {
+ last = it.plainText();
+ ++it;
+ }
+
+ if(last[0] == '['){ /* strip time stamp */
+ prompt = last.mid(last.find(' '));
+ }
+ else {
+ prompt = last;
+ }
+
+ sp = new ssfePrompt(prompt, 0);
+ sp->setCaption(caption);
+ if(string[1] == 'P')
+ sp->setPassword(TRUE);
+ sp->exec();
+ // cerr << "Entered: " << sp->text() << endl;
+ prompt = sp->text();
+ prompt += "\n";
+ emit top->outputUnicodeLine(prompt);
+ delete sp;
+ prompt_active = FALSE;
+ }
+
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEReconnect(QString)
+{
+
+ if(top->channelInfo().channel()[0] == '#' ||
+ top->channelInfo().channel()[0] == '&'){
+ QString str = "/join " + QString(top->channelInfo().channel()) + "\n";
+ emit top->outputUnicodeLine(str);
+ }
+
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseINFOInfo(QString string)
+{
+ string.remove(0, 3); // takes off the junk
+
+ return new parseSucc(string, ksopts->infoColor, "user|servinfo"); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseINFOError(QString string)
+{
+ string.remove(0, 3); // strip the junk
+
+ return new parseSucc(string,ksopts->errorColor, "user|error"); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseINFONicks(QString in_string)
+{
+ QString string = in_string;
+ QString channel_name;
+ bool clear_box = FALSE;
+
+ // Check to see if it's a continued line
+ if(string[1] == 'C'){
+ string[1] = '!';
+ clear_box = TRUE;
+ }
+ if(string[1] == '#'){
+ string[1] = '!';
+ clear_box = FALSE;
+ }
+ else if(string[1] == 'c'){
+ if(current_item > 0)
+ top->nicks->setCurrentItem(current_item);
+ top->nicks->setTopItem(top_item);
+ top->nicks->repaint(TRUE);
+ return new parseSucc(QString::null); // Parsing ok, don't print anything though
+ }
+ else if(string[1] == '$'){
+ top->nicks->clearAdvOps();
+ //kdDebug(5008) << "Turning off advanced ops" << endl;
+ return new parseSucc(QString::null); // Parsing ok, don't print anything though
+ }
+
+
+ // Get the channel name portion of the string
+ // Search for the first space, since : can be embeded into channel names.
+ //count = sscanf(string, "*!* Users on %100[^ ]", channelInfo().channel());
+ // *!* Users on #TEST: boren asj asj_
+ QRegExp rx("\\*\\S\\* Users on (\\S+): (.+)");
+ if(rx.search(string) == -1){
+ return new parseError(string, i18n("Could not find channel name"));
+ }
+ channel_name = rx.cap(1);
+
+
+ if (channel_name.lower() != top->channelInfo().channel().lower()){
+ string.remove(0,3);
+ return new parseSucc(string,ksopts->infoColor,"user|misc4");
+ }
+
+ if(clear_box == TRUE){
+ current_item = top->nicks->currentItem();
+ top_item = top->nicks->topItem();
+ top->nicks->clear();
+ }
+
+ //int start = string.find(": ", 0, FALSE); // Find start of nicks
+ //if (start < 0)
+ // return new parseError(string, i18n("Could not find start of nicks"));
+ //
+ //place_holder = new char[string.length()];
+ //strcpy(place_holder, string.ascii()+start+2);
+ //nick = strtok(place_holder, " ");
+ // while(nick != 0x0){ // While there's nick to go...
+ QStringList nicks = QStringList::split(QRegExp("\\s+"), rx.cap(2));
+
+ for ( QStringList::Iterator it = nicks.begin(); it != nicks.end(); ++it ) {
+ QString nick = *it;
+ nickListItem *irc = new nickListItem();
+
+ bool done = FALSE;
+ uint i;
+
+ for(i = 0; i < nick.length();i++){
+ switch(nick[0].unicode()){
+ case '@':
+ irc->setOp(TRUE);
+ nick.remove(0,1);
+ break;
+ case '+':
+ irc->setVoice(TRUE);
+ nick.remove(0,1);
+ break;
+ case '#':
+ irc->setAway(TRUE);
+ nick.remove(0,1);
+ break;
+ case '*':
+ irc->setIrcOp(TRUE);
+ nick.remove(0,1);
+ break;
+ default:
+ done = TRUE;
+ }
+ if(done == TRUE)
+ break;
+ }
+
+ if(nick == top->ksircProcess()->getNick()){
+ irc->forceColour(&ksopts->ownNickColor);
+ }
+ irc->setText(nick);
+ top->nicks->inSort(irc);
+ }
+
+ return new parseSucc(QString::null); // Parsing ok, don't print anything though
+}
+
+parseResult * ChannelParser::parseINFOJoin(QString string)
+{
+ string.remove(0, 4); // strip *>* to save a few compares
+ // You have joined channel #Linux
+ QRegExp rx("You have joined channel (\\S+)");
+ if(rx.search(string) != -1){
+ //QString channel = rx.cap(1).lower();
+ QString channel = rx.cap(1);
+ //kdDebug(5008) << "Channel: " << channel << endl;
+
+ if(top->channelInfo().channel() != channel) {
+ KSircChannel ci(top->channelInfo().server(), channel);
+ kdDebug(5008) << "Warning: we got a channel join yet me don't belong to it!!! Assuming no key!" << endl;
+ kdDebug(5008) << "String was: " << string << endl;
+ kdDebug(5008) << "We think the channel is: " << channel << " we are: " << top->channelInfo().channel()<< endl;
+ emit top->open_toplevel(ci);
+ }
+ return new parseJoinPart(" " + string, ksopts->channelColor, "user|join");
+ }
+
+ // reef-diddy (nenernener@xnet-3B34A9E2.snet.net) has joined channel #aquaria
+ rx.setPattern("(\\S+) .+ has joined channel (\\S+)");
+ if(rx.search(string) != -1){
+ QString nick = rx.cap(1);
+ QString channel = rx.cap(2).lower();
+ //kdDebug(5008) << "Channel: " << channel << " nick: " << nick << endl;
+ if(top->channelInfo().channel().lower() != channel){
+ return new parseWrongChannel(" " + string, ksopts->errorColor, "user|join");
+ }
+ // nicks->insertItem(s3, 0); // add the sucker
+ top->nicks->inSort(nick);
+ top->addCompleteNick(nick);
+ highlightNick(string, nick);
+ return new parseJoinPart(" " + string, ksopts->channelColor, "user|join");
+ }
+
+ return 0; // ??
+}
+
+parseResult * ChannelParser::parseINFOPart(QString string)
+{
+
+ bool foundNick = false;
+ QString pixname = "user|kick";
+ QString nick("");
+
+ string.remove(0, 4); // clear junk
+
+ // Multiple type of parts, a signoff or a /part
+ // Each get's get nick in a diffrent localtion
+ // Set we search and find the nick and the remove it from the nick list
+ // 1. /quit, signoff, nick after "^Singoff: "
+ // 2. /part, leave the channek, nick after "has left \w+$"
+ // 3. /kick, kicked off the channel, nick after "kicked off \w+$"
+ //
+ // Signoff: looser
+ QRegExp rx("Signoff: (\\S+)");
+ if(rx.search(string) != -1) {
+ nick = rx.cap(1);
+ foundNick = true;
+ pixname = "user|X";
+ highlightNick(string, nick);
+ }
+ /*
+ * Check for "You" before everyone else or else the next
+ * case will match it
+ * You have left channel <channel>
+ */
+ rx.setPattern("You have left channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ QString channel = rx.cap(1);
+ if(top->channelInfo().channel().lower() == channel.lower()) {
+ QApplication::postEvent(top, new QCloseEvent());
+ // WE'RE DEAD
+ return new parseSucc(QString::null);
+ }
+ pixname = "user|part";
+ }
+ /*
+ * Same as above, check your own state first
+ * You have been kicked off channel <channel>
+ */
+ rx.setPattern("You have been kicked off channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ QString channel = rx.cap(1);
+ if(top->channelInfo().channel().lower() != channel.lower())
+ return new parseWrongChannel(string, ksopts->errorColor, "user|kick");
+ if (ksopts->autoRejoin == TRUE)
+ {
+ QString str = QString("/join %1\n").arg(top->channelInfo().channel());
+ emit top->outputUnicodeLine(str);
+ /* if(top->ticker)
+ top->ticker->show();
+ else*/
+ top->show();
+ }
+ else
+ {
+ if(top->KickWinOpen != false)
+ return new parseError(" " + string, i18n("Kick window open"));
+ top->KickWinOpen = true;
+ int result = KMessageBox::questionYesNo(top, string, i18n("You Have Been Kicked"), i18n("Rejoin"), i18n("Leave"));
+ if (result == KMessageBox::Yes)
+ {
+ QString str = QString("/join %1\n").arg(top->channelInfo().channel());
+ emit top->outputUnicodeLine(str);
+ /* if(top->ticker)
+ * top->ticker->show();
+ * else*/
+ top->show();
+ return new parseJoinPart(" " + string, ksopts->channelColor, "user|kick");
+ }
+ else
+ {
+ // WE'RE DEAD
+ QApplication::postEvent(top, new QCloseEvent());
+ top->KickWinOpen = false;
+ }
+ }
+ pixname = "user|kick";
+ }
+ /*
+ * <nick> has left channel <channel>
+ */
+ rx.setPattern("(\\S+) has left channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ nick = rx.cap(1);
+ QString channel = rx.cap(2);
+// kdDebug(5008) << "Nick: " << nick << " Channel: " << channel << " top: " << top->channelInfo().channel() << endl;
+ if(top->channelInfo().channel().lower() == channel.lower()) {
+ foundNick = true;
+ }
+ else{
+ return new parseWrongChannel(QString::null);
+ }
+ pixname = "user|part";
+ highlightNick(string, nick);
+ }
+ /*
+ * "<nick> has been kicked off channel <channel>"
+ */
+ rx.setPattern("(\\S+) has been kicked off channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ nick = rx.cap(1);
+ QString channel = rx.cap(2);
+ if(top->channelInfo().channel().lower() == channel.lower()) {
+ foundNick = true;
+ } else {
+ return new parseWrongChannel(QString::null);
+ }
+ highlightNick(string, nick);
+ pixname = "user|kick";
+ }
+
+ if (foundNick) {
+
+ top->removeCompleteNick(nick);
+
+ int index = top->nicks->findNick(nick);
+ if(index >= 0){
+ top->nicks->removeItem(index);
+ return new parseJoinPart(" " + string, ksopts->channelColor, pixname);
+ }
+ else {
+ return new parseJoinPart(QString::null);
+ }
+ }
+ else {
+ return new parseError(" " + string, i18n("Failed to parse part/kick/leave/quit message"));
+ }
+
+ return 0;
+}
+
+parseResult * ChannelParser::parseINFOChangeNick(QString string)
+{
+ //char old_nick[101], new_nick[101];
+ QString old_nick, new_nick;
+
+ string.remove(0, 4); // Remove the leading *N* and space
+
+ /*
+ * *N* asj_ is now known as bleh
+ */
+ //kdDebug(5008) << "Nick change: " << string << endl;
+ QRegExp rx("(\\S+) is now known as (\\S+)");
+ if(rx.search(string) == -1){
+ if(string.contains("already taken")){
+ return new parseSucc(" " + string, ksopts->errorColor, "user|error");
+ }
+
+ return new parseError(i18n("Unable to parse: %1").arg(string), i18n("Unable to parse change nick code"));
+ }
+
+ old_nick = rx.cap(1);
+ new_nick = rx.cap(2);
+
+ // If we have a window open talking to the nick
+ // Change the nick to the new one.
+ if((top->channelInfo().channel()[0] != '#' || top->channelInfo().channel()[0] != '&') &&
+ (top->channelInfo().channel() == old_nick)){
+ if(!top->ksircProcess()->mrList()[new_nick.lower()]){
+ top->control_message(CHANGE_CHANNEL, new_nick.lower());
+ }
+ }
+
+ highlightNick(string, old_nick);
+ highlightNick(string, new_nick);
+
+ // search the list for the nick and remove it
+ // since the list is source we should do a binary search...
+ int found = top->nicks->findNick(old_nick);
+ if(found >= 0){ // If the nick's in the nick list, change it and display the change
+ // save current selection
+ int selection = top->nicks->currentItem();
+
+ // Get the old item, and create a new one
+ nickListItem *it = top->nicks->item(found);
+ nickListItem *irc = new nickListItem(*it);
+ irc->setText(new_nick);
+
+ top->nicks->removeItem(found); // remove old nick
+ top->nicks->inSort(irc);
+
+ top->changeCompleteNick(old_nick, new_nick);
+
+ top->nicks->setCurrentItem(selection);
+ top->nicks->repaint(TRUE);
+ // We're done, so let's finish up
+ return new parseSucc(" " + string, ksopts->channelColor, "user|join");
+ }
+ else {
+ if(top->channelInfo().channel() == new_nick ||
+ top->channelInfo().channel() == old_nick)
+ return new parseSucc(" " + string, ksopts->channelColor, "user|elipsis");
+ else
+ return new parseSucc(QString::null);
+
+ }
+
+ // warning("Toplevel-N: nick change search failed on %s", s3.data());
+ // return new parseSucc(QString::null);
+}
+
+class mode_info {
+public:
+ mode_info(bool op, QChar mode, QString arg);
+
+ bool op() const;
+ QChar mode() const;
+ QString arg() const;
+
+private:
+ const bool m_op;
+ const QChar m_mode;
+ const QString m_arg;
+};
+
+mode_info::mode_info(bool op, QChar mode, QString arg) :
+ m_op(op),
+ m_mode(mode),
+ m_arg(arg)
+{
+}
+
+bool mode_info::op() const {
+ return m_op;
+}
+
+QChar mode_info::mode() const {
+ return m_mode;
+}
+
+QString mode_info::arg() const {
+ return m_arg;
+}
+
+
+parseResult * ChannelParser::parseINFOMode(QString string)
+{
+ // Basic idea here is simple, go through the mode change and
+ // assign each mode a + or a - and an argument or "" if there is
+ // none. After that each mode change it looked at to see if
+ // we should handle it in any special way.
+
+ // Strip off leading sirc info
+
+ string.remove(0, 4);
+
+
+ /*
+ * 1k is pretty safe since KProcess returns 1 k blocks, and lines don't get split between reads. This is emprical
+ */
+ QString modes, args, channel;
+ int found = 0;
+
+ if(string.find("for user") >= 0)
+ return new parseSucc(" " + string, ksopts->infoColor, "user|mode");
+
+ /*
+ * We need to 2 scanf's, one for the case of arguments, and one for no args.
+ */
+ QRegExp rx("Mode change \"(\\S+) *([^\"]*)\" on channel (\\S+)");
+ if(rx.search(string) >= 0){
+ modes = rx.cap(1);
+ args = rx.cap(2);
+ channel = rx.cap(3);
+ found = 1;
+ }
+
+ rx.setPattern("Mode for channel (\\S+) is \"([^\" ]+)\"");
+ if(found == 0 &&
+ rx.search(string) >= 0){
+ channel = rx.cap(1);
+ modes = rx.cap(2);
+ found = 1;
+ }
+
+ rx.setPattern("Your user mode is");
+ if(found == 0 &&
+ rx.search(string) >= 0){
+ /*
+ * Don't parse user mode requests
+ */
+ return new parseSucc(" " + string, ksopts->infoColor, "user|mode");
+ }
+
+
+ if(found == 0)
+ return new parseError(" Failed to parse mode change: " + string, QString::null);
+
+ /*
+ * op specifie if it's a + or -. tru is + false is -
+ */
+ bool op = true;
+ /*
+ * arglist is the list of argument
+ * we use the itirator to tstep through the list
+ * as need be
+ */
+ QStringList arglist = QStringList::split(" ", args);
+ QStringList::Iterator ai = arglist.begin();
+
+ /*
+ * the ptr list structure contains the parsed contents
+ */
+ QPtrList<const mode_info> pmList;
+ pmList.setAutoDelete(true);
+
+ for(uint pos = 0; pos < modes.length(); pos++){
+ switch(modes.at(pos).unicode()){
+ case '+':
+ op = true;
+ break;
+ case '-':
+ op = false;
+ break;
+ case 'l': // Chan limits
+ /*
+ * -l doesn't take any arguments, so just add the mode and break
+ * +l otoh does, so read the argument
+ */
+ if(op == false){
+ pmList.append(new mode_info(op, 'l', QString::null));
+ break;
+ }
+ case 'o': // Op, arg is the nick
+ case 'v': // Voice, arg is the nick
+ case 'b': // Ban, arg is mask banned
+ case 'k': // kcik, arg is nick
+ if(ai == NULL)
+ return new parseError(i18n("Unable to parse mode change: %1").arg(string), QString::null);
+ pmList.append(new mode_info(op, modes.at(pos), *ai));
+ ai++;
+ break;
+ case 'i': // Invite only
+ case 'n': // No message to chan
+ case 'p': // Private
+ case 'm': // Moderated
+ case 's': // Secret
+ case 't': // Topic setable by ops
+ case 'R': // (Dalnet) only registered may join
+ case 'r': // (Dalnet) only registered may join or something
+ /*
+ * Mode changes which don't take args
+ */
+ pmList.append(new mode_info(op, modes.at(pos), QString::null));
+ break;
+ default:
+ kdDebug(5008) << "Unknown mode change: " << modes.mid(pos, 1) << " Assume no args" << endl;
+ pmList.append(new mode_info(op, modes.at(pos), QString::null));
+ }
+ }
+ // We have the modes set in mode and arg, now we go though
+ // looking at each mode seeing if we should handle it.
+ bool mode_o_plus = false;
+ bool mode_o_minus = false;
+ bool mode_b_plus = false;
+ bool mode_b_minus = false;
+
+ QPtrListIterator<const mode_info> it(pmList);
+ const mode_info *mi;
+ while ( (mi = it.current()) != 0 ) {
+ ++it;
+ /*
+ * Look at the second character, it's uniq, check for +,- latter
+ */
+ if(mi->mode().unicode() == 'o'){
+ mode_o_plus = mi->op();
+ mode_o_minus = !mi->op();
+
+ if(top->ksircProcess()->getNick() == mi->arg())
+ top->channelButtons->setButtonsEnabled(mi->op());
+
+ if(mi->arg().length() == 0){
+ qWarning("Invalid nick in +/- o mode change");
+ continue;
+ }
+
+ int offset = top->nicks->findNick(mi->arg());
+ if(offset >= 0){
+ nickListItem *irc = new nickListItem();
+ *irc = *top->nicks->item(offset);
+ top->nicks->removeItem(offset); // remove old nick
+ irc->setOp(mi->op());
+ // add new nick in sorted pass,with colour
+ top->nicks->inSort(irc);
+ top->nicks->repaint(TRUE);
+ }
+ else{
+ kdDebug(5008) << "Toplevel+o: nick search failed on " << mi->arg() << endl;
+ }
+ }
+ else if(mi->mode() == 't'){
+ if(mi->op())
+ top->channelButtons->setProtectMode(true); // set on
+ else
+ top->channelButtons->setProtectMode(false); // set off
+ }
+ else if(mi->mode() == 'm'){
+ if(mi->op())
+ top->channelButtons->setModerateMode(true); // set on
+ else
+ top->channelButtons->setModerateMode(false); // set off
+ }
+ else if(mi->mode() == 'n'){
+ if(mi->op())
+ top->channelButtons->setNooutsideMode(true); // set on
+ else
+ top->channelButtons->setNooutsideMode(false); // set off
+ }
+ else if(mi->mode() == 'v'){
+ bool voice;
+ if(mi->op())
+ voice = TRUE;
+ else
+ voice = FALSE;
+
+ if(mi->arg().length() == 0){
+ qWarning("Invalid nick in +-v mode change");
+ continue;
+ }
+
+ int offset = top->nicks->findNick(mi->arg());
+ if(offset >= 0){
+ nickListItem *irc = new nickListItem();
+ *irc = *top->nicks->item(offset);
+ top->nicks->removeItem(offset); // remove old nick
+ irc->setVoice(voice) ;
+ // add new nick in sorted pass,with colour
+ top->nicks->inSort(irc);
+ top->nicks->repaint();
+ }
+ }
+ else if(mi->mode() == 'b'){
+ if(mi->op())
+ mode_b_plus = true;
+ else
+ mode_b_minus = true;
+ }
+ else if(mi->mode() == 'k'){
+ if(mi->op()){
+ if(mi->arg().length() == 0){
+ qWarning("Invalid +k mode set, no argument!");
+ continue;
+ }
+ top->m_channelInfo.setKey(mi->arg());
+ }
+ else {
+ top->m_channelInfo.setKey(""); /* no key set anymore */
+ }
+ }
+ else{
+ QChar c(mi->mode());
+ QString m(&c, 1);
+ QString o;
+ if(mi->op())
+ o = "+";
+ else
+ o = "-";
+ kdDebug(5008) << "Did not handle: " << o << m << " arg: " << mi->arg() << endl;
+ }
+ }
+ /*
+ * We're all done, so output the message and be done with it
+ */
+ QString pixname = "user|mode";
+
+ if(mode_o_plus)
+ pixname = "user|oplus";
+ else if(mode_o_minus)
+ pixname = "user|ominus";
+ else if(mode_b_plus)
+ pixname ="user|bplus";
+ else if(mode_b_minus)
+ pixname = "user|bminus";
+
+ return new parseSucc(" " + string, ksopts->infoColor, pixname);
+}
+
+parseResult * ChannelParser::parseCTCPAction(QString string)
+{
+
+ string.remove(0, 2); // * <something> use fancy * pixmap. Remove 2, leave one for space after te *
+ // why? looks cool for dorks
+ return new parseSucc(string, ksopts->textColor, "user|action");
+}
+
+parseResult * ChannelParser::parseINFOTopic(QString string)
+{
+
+ int found = 0;
+
+ string.remove(0, 4); // Remove the leading *T* and space
+
+ //kdDebug(5008) << "Topic parser: " << string << endl;
+
+ // Topic for #boo: this is a nice test
+ QRegExp rx( "Topic for (\\S+): (.*)" );
+ if(rx.search( string ) != -1){
+ QString channel = rx.cap(1);
+ QString topic = rx.cap(2);
+ topic.replace( QRegExp( "~~" ), "~" );
+
+ /*
+ * check where it's going.
+ * topic's maybe for other channels since they have no channel routing
+ * information, so route it to the right place if need be.
+ * If we're not on the channnel just fall through and display it
+ * on our channel, maybe the user asked for a topic of a different channel
+ */
+
+ if(channel.lower() != top->channelInfo().channel().lower()){
+ if(top->ksircProcess()->mrList()[channel.lower()]){
+ KSircTopLevel *t = dynamic_cast<KSircTopLevel *>(top->ksircProcess()->mrList()[channel.lower()]);
+ if(t)
+ t->setTopic(topic);
+ }
+ }
+ else {
+ //kdDebug(5008) << "New topic: " << topic << endl;
+ top->setTopic( topic );
+ }
+ found = 1;
+ }
+
+ rx.setPattern("(\\S+) has changed the topic on channel (\\S+) to (.+)");
+ if(found == 0 && rx.search(string) != -1){
+ QString nick = rx.cap(1);
+ QString channel = rx.cap(2);
+ //kdDebug(5008) << "Channel: " << channel << endl;
+ if(top->channelInfo().channel().lower() == channel.lower()){
+ QString topic = rx.cap(3);
+ //kdDebug(5008) << "Topic: " << topic << endl;
+ topic.replace(QRegExp("~~"), "~");
+ /*
+ * topic is in double quotes, so remove them
+ */
+ top->setTopic( topic.mid(1, topic.length()-2) );
+ QString cmd = "/eval &dostatus();\n";
+ top->sirc_write(cmd);
+ }
+ highlightNick(string, nick);
+ }
+ return new parseSucc(" " + string, ksopts->infoColor, "user|topic");
+}
+
+
+void ChannelParser::highlightNick(QString &string, QString &nick)
+{
+ QRegExp rx(QString("(^|\\s+)%1(\\s+|$)").arg(QRegExp::escape(nick)));
+ string.replace(rx, "\\1~n" + nick + "~n\\2");
+}
+