//============================================================================= // // File : kvi_sp_numeric.cpp // Creation date : Thu Aug 3 2000 01:30:45 by Szymon Stefanek // // This file is part of the KVirc irc client distribution // Copyright (C) 1999-2007 Szymon Stefanek (pragma at kvirc dot net) // // 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 opinion) 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, write to the Free Software Foundation, // Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #define __KVIRC__ #include "kvi_sparser.h" #include "kvi_window.h" #include "kvi_query.h" #include "kvi_out.h" #include "kvi_locale.h" #include "kvi_ircsocket.h" #include "kvi_options.h" #include "kvi_channel.h" #include "kvi_topicw.h" #include "kvi_ircuserdb.h" #include "kvi_defaults.h" #include "kvi_mirccntrl.h" #include "kvi_frame.h" #include "kvi_parameterlist.h" #include "kvi_app.h" #include "kvi_notifylist.h" #include "kvi_numeric.h" #include "kvi_ircconnection.h" #include "kvi_ircconnectionstatedata.h" #include "kvi_ircconnectionuserinfo.h" #include "kvi_ircconnectionserverinfo.h" #include "kvi_ircconnectionasyncwhoisdata.h" #include "kvi_ircconnectiontarget.h" #include "kvi_time.h" #include "kvi_lagmeter.h" #include "kvi_qcstring.h" #include #include #include #include #include "kvi_kvs_eventtriggers.h" #include "kvi_kvs_script.h" #include "kvi_kvs_variantlist.h" // #define IS_CHANNEL_TYPE_FLAG(_str) ((*(_str) == '#') || (*(_str) == '&') || (*(_str) == '!')) #define IS_CHANNEL_TYPE_FLAG(_qchar) (msg->connection()->serverInfo()->supportedChannelTypes().find(_qchar) != -1) #define IS_USER_MODE_PREFIX(_qchar) (msg->connection()->serverInfo()->supportedModePrefixes().find(_qchar) != -1) // Numeric message handlers // FIXME: #warning "IN ALL OUTPUT ADD ESCAPE SEQUENCES!!!!" // FIXME: #warning "parseErrorUnknownModeChar() for modes e and I , parseErrorUnknownCommand for WATCH" void KviServerParser::parseNumeric001(KviIrcMessage *msg) { // 001: RPL_WELCOME // :prefix 001 target :Welcome to the Internet Relay Network // FIXME: #warning "SET THE USERMASK FROM SERVER" TQString szText = msg->connection()->decodeText(msg->safeTrailing()); TQRegExp rx( " ([^ ]+)!([^ ]+)@([^ ]+)$" ); if( rx.search(szText) != -1) { msg->connection()->userInfo()->setUnmaskedHostName(rx.cap(3)); msg->connection()->userInfo()->setNickName(rx.cap(1)); msg->connection()->userInfoReceived(rx.cap(2),rx.cap(3)); } if(msg->connection()->context()->state() != KviIrcContext::Connected) msg->connection()->loginComplete(msg->connection()->decodeText(msg->param(0))); if(!msg->haltOutput()) msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,szText); } void KviServerParser::parseNumeric002(KviIrcMessage *msg) { // 002: RPL_YOURHOST [I,E,U,D] // :prefix 002 target :Your host is , running version if(msg->connection()->context()->state() != KviIrcContext::Connected) msg->connection()->loginComplete(msg->connection()->decodeText(msg->param(0))); if(!msg->haltOutput())msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,msg->connection()->decodeText(msg->safeTrailing())); } void KviServerParser::parseNumeric003(KviIrcMessage *msg) { // 003: RPL_CREATED [I,E,U,D] // :prefix 003 target :This server was created if(msg->connection()->context()->state() != KviIrcContext::Connected) msg->connection()->loginComplete(msg->connection()->decodeText(msg->param(0))); if(!msg->haltOutput())msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,msg->connection()->decodeText(msg->safeTrailing())); } void KviServerParser::parseNumeric004(KviIrcMessage *msg) { // 004: RPL_MYINFO [I,E,U,D] // :prefix 004 target if(msg->connection()->context()->state() != KviIrcContext::Connected) msg->connection()->loginComplete(msg->connection()->decodeText(msg->param(0))); int uParams = msg->paramCount(); int uModeParam = 3; if(uParams < 2)uParams = 2; KviStr version = msg->safeParam(2); msg->connection()->serverInfo()->setServerVersion(msg->safeParam(2)); KviStr umodes; // skip version number (great, thanks WEBMASTER INCORPORATED -_-) do { umodes = msg->safeParam(uModeParam); } while (((umodes.contains('.')) || (umodes.contains('-'))) && uModeParam++ < uParams); KviStr chanmodes = msg->safeParam(uModeParam+1); if(uModeParam > 3) { version.append(' '); version.append(msg->safeParam(3)); } if((umodes.occurences('o') != 1) || (chanmodes.occurences('o') != 1) || (chanmodes.occurences('b') != 1) || (chanmodes.occurences('v') != 1) || (chanmodes.occurences('t') != 1) || (chanmodes.occurences('n') != 1) || (chanmodes.contains('.')) || (chanmodes.contains('-')) || (chanmodes.contains('('))) { if(!_OUTPUT_QUIET) { msg->console()->output(KVI_OUT_SYSTEMWARNING,__tr2qs( "One or more standard mode flags are missing in the server available modes.\n" \ "This is caused either by a non RFC1459-compliant IRC daemon or a broken server reply.\n" \ "Server umodes seem to be '%s' and channel modes seem to be '%s'.\n" \ "Ignoring this reply and assuming that the basic set of modes is available.\n" \ "If you have strange problems, try changing the server."),umodes.ptr(),chanmodes.ptr()); } umodes = "oiws"; // standard support chanmodes = "obtkmlvsn"; // standard support } if(KVI_OPTION_BOOL(KviOption_boolShowExtendedServerInfo) && (!msg->haltOutput())) { if(umodes.hasData())msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,__tr2qs("Available user modes:")); const char * aux = umodes.ptr(); TQString tmp; while(*aux) { tmp = msg->connection()->serverInfo()->getUserModeDescription(*aux); if(tmp.isEmpty()) { TQString tmp2 = __tr2qs(": Unknown user mode"); KviTQString::sprintf(tmp,"%c: %Q",*aux,&tmp2); } msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,tmp); aux++; } if(chanmodes.hasData())msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,__tr2qs("Available channel modes:")); aux = chanmodes.ptr(); while(*aux) { KviTQString::sprintf(tmp,"%c: %Q",*aux,&(msg->connection()->serverInfo()->getChannelModeDescription(*aux))); msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,tmp); aux++; } } TQString szServer = msg->connection()->decodeText(msg->safeParam(1)); msg->connection()->serverInfoReceived(szServer,umodes.ptr(),chanmodes.ptr()); // FIXME: #warning "TO ACTIVE ? OR TO CONSOLE ?" if(!_OUTPUT_MUTE) { if(!msg->haltOutput())msg->console()->output(KVI_OUT_SERVERINFO, __tr2qs("Server %Q version %S supporting user modes '%S' and channel modes '%S'"), &szServer,&version,&umodes,&chanmodes); } } void KviServerParser::parseNumeric005(KviIrcMessage *msg) { // 005: RPL_PROTOCTL [D] // :prefix 005 target .... :are available/supported on this server // 005: RPL_BOUNCE [?] // :prefix 005 target :Try server , port // 005: RPL_ISUPPORT if(msg->connection()->context()->state() != KviIrcContext::Connected) msg->connection()->loginComplete(msg->connection()->decodeText(msg->param(0))); bool bUhNames = false; bool bNamesx = false; unsigned int count = msg->paramCount(); if(count > 2) { count--; for(unsigned int i = 1;i < count;i++) { const char * p = msg->param(i); if(kvi_strEqualCIN("PREFIX=(",p,8)) { p+=8; const char * pModes = p; while(*p && (*p != ')'))p++; KviStr szModeFlags(pModes,p-pModes); if(*p)p++; KviStr szModePrefixes = p; if(szModePrefixes.hasData() && (szModePrefixes.len() == szModeFlags.len())) { msg->connection()->serverInfo()->setSupportedModePrefixes(szModePrefixes.ptr(),szModeFlags.ptr()); } } else if(kvi_strEqualCIN("CHANTYPES=",p,10)) { p+=10; KviStr tmp = p; if(tmp.hasData())msg->connection()->serverInfo()->setSupportedChannelTypes(tmp.ptr()); } else if(kvi_strEqualCI("WATCH",p) || kvi_strEqualCIN("WATCH=",p,6)) { msg->connection()->serverInfo()->setSupportsWatchList(true); if((!_OUTPUT_MUTE) && (!msg->haltOutput()) && KVI_OPTION_BOOL(KviOption_boolShowExtendedServerInfo)) { msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,__tr2qs("This server supports the WATCH notify list method, it will be used")); } } else if(kvi_strEqualCIN("TOPICLEN=",p,9)) { p += 9; TQString tmp = p; if(!tmp.isEmpty()) { bool ok; int len = tmp.toInt( &ok ); if(ok) msg->connection()->serverInfo()->setMaxTopicLen(len); } } else if(kvi_strEqualCIN("NETWORK=",p,8)) { p += 8; TQString tmp = p; if(!tmp.isEmpty())msg->console()->connection()->target()->setNetworkName(tmp); if((!_OUTPUT_MUTE) && (!msg->haltOutput()) && KVI_OPTION_BOOL(KviOption_boolShowExtendedServerInfo)) { msg->console()->output(KVI_OUT_SERVERINFO,__tr2qs("The current network is %Q"),&tmp); } } else if(kvi_strEqualCI("CODEPAGES",p)) { msg->connection()->serverInfo()->setSupportsCodePages(true); if((!_OUTPUT_MUTE) && (!msg->haltOutput()) && KVI_OPTION_BOOL(KviOption_boolShowExtendedServerInfo)) { msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,__tr2qs("This server supports the CODEPAGE command, it will be used")); } //msg->connection()->sendFmtData("CODEPAGE %s",msg->console()->textCodec()->name()); } else if(kvi_strEqualCIN("CHANMODES=",p,10)) { p+=10; TQString tmp = p; msg->connection()->serverInfo()->setSupportedChannelModes(tmp); }else if(kvi_strEqualCIN("MODES=",p,6)) { p+=6; TQString tmp = p; bool bok; int num=tmp.toUInt(&bok); if(bok) msg->connection()->serverInfo()->setMaxModeChanges(num); } else if(kvi_strEqualCIN("NAMESX",p,6)) { p+=6; bNamesx=true; } else if(kvi_strEqualCIN("UHNAMES",p,7)) { p+=7; bUhNames=true; }else if(kvi_strEqualCIN("CHARSET=",p,8)) { p+=8; TQString tmp = p; msg->connection()->serverInfo()->setSupportsCodePages(true); if((!_OUTPUT_MUTE) && (!msg->haltOutput()) && KVI_OPTION_BOOL(KviOption_boolShowExtendedServerInfo)) { msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,__tr2qs("This server supports the CODEPAGE command, it will be used")); } /*if( tmp.contains(msg->console()->textCodec()->name(),false) || tmp.contains("*",false) ) { msg->connection()->sendFmtData("CODEPAGE %s",msg->console()->textCodec()->name()); }*/ } } if((!_OUTPUT_MUTE) && (!msg->haltOutput())) { const char * aux = msg->allParams(); while(*aux == ' ')aux++; while(*aux && (*aux != ' '))aux++; while(*aux == ' ')aux++; if(*aux == ':')aux++; if(!msg->haltOutput())msg->console()->output(KVI_OUT_SERVERINFO,__tr2qs("This server supports: %s"),msg->connection()->decodeText(aux).utf8().data()); if(bNamesx || bUhNames) { msg->connection()->sendFmtData("PROTOCTL %s %s",bNamesx ? "NAMESX" : "", bUhNames ? "UHNAMES" : ""); } } } else { TQString inf = msg->connection()->decodeText(msg->safeTrailing()); if(!msg->haltOutput())msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,inf); } // } else { // // RPL_BOUNCE prolly // if(!msg->haltOutput())msg->console()->outputNoFmt(KVI_OUT_SERVERINFO,msg->safeTrailing()); // } } void KviServerParser::parseNumericMotd(KviIrcMessage *msg) { // 372: RPL_MOTD [I,E,U,D] // :prefix 372 target : - // 377: RPL_MOTD2 [?] // :prefix 377 target : - // 378: RPL_MOTD3 [Austnet] // :prefix 377 target : - // 375: RPL_MOTDSTART [I,E,U,D] // :prefix 375 target : - Message of the Day - // 372: RPL_ENDOFMOTD [I,E,U,D] // :prefix 376 target :End of /MOTD command. // FIXME: #warning "SKIP MOTD , MOTD IN A SEPARATE WINDOW , SILENT ENDOFMOTD , MOTD IN ACTIVE WINDOW" if(!msg->haltOutput())msg->console()->outputNoFmt(KVI_OUT_MOTD,msg->connection()->decodeText(msg->safeTrailing())); if(msg->numeric() == RPL_ENDOFMOTD) { msg->connection()->endOfMotdReceived(); } } void KviServerParser::parseNumericEndOfNames(KviIrcMessage *msg) { // 366: RPL_ENDOFNAMES [I,E,U,D] // :prefix 366 target :End of /NAMES list. TQString szChan = msg->connection()->decodeText(msg->safeParam(1)); KviChannel * chan = msg->connection()->findChannel(szChan); if(chan) { if(!chan->hasAllNames()) { chan->setHasAllNames(); return; } } if(!msg->haltOutput() && !_OUTPUT_MUTE) { // FIXME: #warning "KVI_OUT_NAMES missing" KviWindow * pOut = chan ? chan : KVI_OPTION_BOOL(KviOption_boolServerRepliesToActiveWindow) ? msg->console()->activeWindow() : (KviWindow *)(msg->console()); pOut->output(KVI_OUT_UNHANDLED,__tr2qs("End of NAMES for \r!c\r%Q\r"),&szChan); } } void KviServerParser::parseNumeric020(KviIrcMessage *msg) { // 020: RPL_CONNECTING //:irc.dotsrc.org 020 * :Please wait while we process your connection. TQString szServer; if(!msg->haltOutput()) { TQString szWText = msg->console()->decodeText(msg->safeTrailing()); msg->console()->output( KVI_OUT_CONNECTION,"%c\r!s\r%s\r%c: %Q",KVI_TEXT_BOLD, msg->safePrefix(),KVI_TEXT_BOLD,&szWText); } } void KviServerParser::parseNumericNames(KviIrcMessage *msg) { // 353: RPL_NAMREPLY [I,E,U,D] // :prefix 353 target [=|*|@] : // [=|*|@] is the type of the channel: // = --> public * --> private @ --> secret // ...but we ignore it //TQString szChan = msg->connection()->decodeText(msg->cSafeParam(2)->data()); // <-- KviTQCString::data() is implicitly unsafe: it CAN return 0 TQString szChan = msg->connection()->decodeText(msg->safeParam(2)); KviChannel * chan = msg->connection()->findChannel(szChan); // and run to the first nickname char * aux = msg->safeTrailingString().ptr(); while((*aux) && (*aux == ' '))aux++; // now check if we have that channel char * trailing = aux; bool bHalt = msg->haltOutput(); if(chan) { bHalt = bHalt || (!chan->hasAllNames()); // K...time to parse a lot of data chan->enableUserListUpdates(false); int iPrevFlags = chan->myFlags(); KviIrcConnectionServerInfo * pServerInfo = msg->connection()->serverInfo(); while(*aux) { int iFlags = 0; // @ = op (+o), + = voiced (+v), % = halfop (+h), - = userop (+u), ^ = protected (+a?), * = chan owner (+q), !, & = channel admin (+a?) // ^ +a is a weird mode: it also breaks nicknames on some networks! // not a valid first char(s) of nickname, must be a mode prefix bool bContinue = true; while(pServerInfo->isSupportedModePrefix((unsigned char)(*aux))) { // leading umode flag(s) iFlags |= pServerInfo->modeFlagFromPrefixChar(*aux); aux++; } char * begin = aux; while(*aux && (*aux != ' '))aux++; char save = *aux; *aux = 0; // NAMESX + UHNAMES support KviIrcMask mask(msg->connection()->decodeText(begin)); // and make it join if(!mask.nick().isEmpty())chan->join(mask.nick(), mask.hasUser() ? mask.user() : TQString(), mask.hasHost() ? mask.host() : TQString(), iFlags); *aux = ' '; *aux = save; // run to the next nick (or the end) while((*aux) && (*aux == ' '))aux++; } if(iPrevFlags != chan->myFlags())chan->updateCaption(); chan->enableUserListUpdates(true); // finished a block } // So it is a result of a /NAMES command or a local desync // We handle it in a cool way. if(!bHalt) { // FIXME: #warning "KVI_OUT_NAMES missing" KviWindow * pOut = chan ? chan : KVI_OPTION_BOOL(KviOption_boolServerRepliesToActiveWindow) ? msg->console()->activeWindow() : (KviWindow *)(msg->console()); TQString szTrailing = trailing ? msg->connection()->decodeText(trailing) : TQString(""); pOut->output(KVI_OUT_UNHANDLED,__tr2qs("Names for \r!c\r%Q\r: %Q"),&szChan,&szTrailing); } } void KviServerParser::parseNumericTopic(KviIrcMessage *msg) { // 332: RPL_TOPIC [I,E,U,D] // :prefix 332 target : TQString szChan = msg->connection()->decodeText(msg->safeParam(1)); KviChannel * chan = msg->connection()->findChannel(szChan); if(chan) { TQString szTopic = chan->decodeText(msg->safeTrailing()); chan->topicWidget()->setTopic(szTopic); chan->topicWidget()->setTopicSetBy(__tr2qs("(unknown)")); if(KVI_OPTION_BOOL(KviOption_boolEchoNumericTopic)) { if(!msg->haltOutput()) chan->output(KVI_OUT_TOPIC,__tr2qs("Channel topic is: %Q"),&szTopic); } } else { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolServerRepliesToActiveWindow) ? msg->console()->activeWindow() : (KviWindow *)(msg->console()); TQString szTopic = msg->connection()->decodeText(msg->safeTrailing()); pOut->output(KVI_OUT_TOPIC,__tr2qs("Topic for \r!c\r%Q\r is: %Q"), &szChan,&szTopic); } } void KviServerParser::parseNumericNoTopic(KviIrcMessage *msg) { // 331: RPL_NOTOPIC [I,E,U,D] // :prefix 331 target :No topic is set TQString szChan = msg->connection()->decodeText(msg->safeParam(1)); KviChannel * chan = msg->connection()->findChannel(szChan); if(chan) { chan->topicWidget()->setTopic(""); if(KVI_OPTION_BOOL(KviOption_boolEchoNumericTopic)) { if(!msg->haltOutput()) chan->outputNoFmt(KVI_OUT_TOPIC,__tr2qs("No channel topic is set")); } } else { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolServerRepliesToActiveWindow) ? msg->console()->activeWindow() : (KviWindow *)(msg->console()); pOut->output(KVI_OUT_TOPIC,__tr2qs("No topic is set for channel \r!c\r%Q\r"), &szChan); } } void KviServerParser::parseNumericTopicWhoTime(KviIrcMessage *msg) { // 333: RPL_TOPICWHOTIME [e,U,D] // :prefix 333 target