//==================================================================================== // // File : kvi_sp_literal.cpp // Creation date : Thu Aug 3 2000 01:29:12 by Szymon Stefanek // // This file is part of the KVirc irc client distribution // Copyright (C) 1999-2004 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_console.h" #include "kvi_out.h" #include "kvi_locale.h" #include "kvi_ircsocket.h" #include "kvi_options.h" #include "kvi_ircmask.h" #include "kvi_channel.h" #include "kvi_topicw.h" #include "kvi_frame.h" #include "kvi_mirccntrl.h" #include "kvi_query.h" #include "kvi_userlistview.h" #include "kvi_antispam.h" #include "kvi_nickserv.h" #include "kvi_parameterlist.h" #include "kvi_ircuserdb.h" #include "kvi_app.h" #include "kvi_regusersdb.h" #include "kvi_debug.h" #include "kvi_time.h" #include "kvi_useraction.h" #include "kvi_ircconnection.h" #include "kvi_ircconnectionuserinfo.h" #include "kvi_ircconnectiontarget.h" #include "kvi_ircconnectionserverinfo.h" #include "kvi_ircconnectionstatedata.h" #include "kvi_ircconnectionnetsplitdetectordata.h" #include "kvi_iconmanager.h" #include "kvi_lagmeter.h" #include "kvi_ircserver.h" #include "kvi_kvs_eventtriggers.h" #include "kvi_qcstring.h" #include "kvi_settings.h" #ifdef COMPILE_CRYPT_SUPPORT #include "kvi_crypt.h" #include "kvi_cryptcontroller.h" #endif #include "kvi_kvs_script.h" //#include "kvi_regusersdb.h" //#include "kvi_iconmanager.h" #include #ifdef COMPILE_USE_QT4 #include #else #include #endif extern KviNickServRuleSet * g_pNickServRuleSet; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // PING // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralPing(KviIrcMessage *msg) { // PING // PING : msg->connection()->sendFmtData("PONG %s",msg->console()->connection()->encodeText(msg->allParams()).data()); TQString szPrefix = msg->connection()->decodeText(msg->safePrefix()); TQString szAllParams = msg->connection()->decodeText(msg->allParams()); if(KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnPing,msg->console(),szPrefix,szAllParams)) msg->setHaltOutput(); if((!msg->haltOutput()) && KVI_OPTION_BOOL(KviOption_boolShowPingPong)) { msg->console()->output(KVI_OUT_SERVERPING, __tr2qs("Received ping from \r!s\r%Q\r (PING %Q), replied pong"), &szPrefix,&szAllParams); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PONG // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralPong(KviIrcMessage *msg) { TQString szPrefix = msg->connection()->decodeText(msg->safePrefix()); TQString szAllParams = msg->connection()->decodeText(msg->allParams()); if(KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnPong,msg->console(),szPrefix,szAllParams)) msg->setHaltOutput(); if(msg->console()->connection()->lagMeter()) { if(msg->console()->connection()->lagMeter()->lagCheckComplete("@ping@")) msg->setHaltOutput(); // was internally generated! } if((!msg->haltOutput()) && KVI_OPTION_BOOL(KviOption_boolShowPingPong)) { msg->console()->output(KVI_OUT_SERVERPING, __tr2qs("Received pong from \r!s\r%s\r (PONG %s)"),msg->safePrefix(),msg->allParams()); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ERROR // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralError(KviIrcMessage *msg) { // ERROR // ERROR : // ERROR :Closing Link: phoenix.pragmaware.net (Ping timeout) TQString szPrefix = msg->connection()->decodeText(msg->safePrefix()); TQString szParams = msg->connection()->decodeText(msg->allParams()); if(KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnError,msg->console(),szPrefix,szParams)) msg->setHaltOutput(); if(!msg->haltOutput()) { msg->console()->output(KVI_OUT_SERVERERROR, __tr2qs("Server ERROR: %Q"),&szParams); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // JOIN // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralJoin(KviIrcMessage *msg) { // JOIN // : JOIN : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); const char * encodedChan = msg->safeTrailing(); TQString channel = msg->connection()->decodeText(encodedChan); if(channel.isEmpty()) { // This is broken.... UNRECOGNIZED_MESSAGE(msg,__tr2qs("Missing channel parameter in join message")); return; } // check for extended join syntax. // it is used in splits only (AFAIK) // nick!user@host JOIN :#channel\x07[o|v] const TQChar * pExt = KviTQString::nullTerminatedArray(channel); char chExtMode = 0; while(pExt->unicode() && (pExt->unicode() != 0x07))pExt++; if(pExt->unicode()) { ++pExt; if(pExt->unicode()) { chExtMode = (char)pExt->unicode(); channel.remove(channel.length() - 2,2); // assuming that we're at the end (we should be) } // else { senseless 0x07 in channel name ? } // Now lookup the channel KviConsole * console = msg->console(); KviChannel * chan = msg->connection()->findChannel(channel); bool bIsMe = IS_ME(msg,szNick); if(!chan) { // This must be me...(or desync) if(bIsMe) { msg->connection()->userInfoReceived(szUser,szHost); chan = msg->connection()->createChannel(channel); // New channel (will resurrect an eventual dead one too!) } else { // Someone is joining an inexsisting channel!!! UNRECOGNIZED_MESSAGE(msg,__tr("Received a join message for an unknown channel, possible desync")); return; } int iFlags = 0; iFlags = msg->connection()->serverInfo()->modeFlagFromModeChar(chExtMode); KviUserListEntry * it = chan->join(szNick,szUser,szHost,iFlags); if(iFlags)chan->updateCaption(); // FIXME: #warning "Trigger also OnMeVoice and OnMeOp here ?" if(!(it->globalData()->avatar())) { KviAvatar * av = console->defaultAvatarFromOptions(); if(av) { it->globalData()->setAvatar(av); console->avatarChanged(av,szNick,szUser,szHost,TQString()); } } if(KVS_TRIGGER_EVENT_0_HALTED(KviEvent_OnMeJoin,chan)) msg->setHaltOutput(); // the channel requests must be sent AFTER we have created and accessed the chan // since it MAY happen that a sendFmtData() call fails by detecting a disconnect // and thus destroys the channel window! // If this problem persists in other parts of the KVIrc core then // we should disable disconnection detection during the parsing of a single // message in KviIrcSocket. See the comment in KviIrcSocket::processData() for more info. // FIXME: #warning "IF VERBOSE SAY THAT WE'RE REQUESTING MODES & BAN LIST" (Synching channel) if(!msg->connection()->sendFmtData("MODE %s",encodedChan))return; // disconnected if(msg->connection()->serverInfo()->supportsModesIe()) { if(!KVI_OPTION_BOOL(KviOption_boolDisableBanExceptionListRequestOnJoin)) { if(!msg->connection()->sendFmtData("MODE %s e",encodedChan))return; // disconnected chan->setSentBanExceptionListRequest(); } if(!KVI_OPTION_BOOL(KviOption_boolDisableInviteListRequestOnJoin)) { if(!msg->connection()->sendFmtData("MODE %s I",encodedChan))return; // disconnected chan->setSentInviteListRequest(); } } // MODE %s b MUST BE THE LAST AUTOMATIC CHANNEL QUERY // so we get RPL_ENDOFBANLIST as the last reply // and we know that the channel is in sync if(!KVI_OPTION_BOOL(KviOption_boolDisableWhoRequestOnJoin)) { msg->connection()->stateData()->setLastSentChannelWhoRequest(kvi_unixTime()); if(msg->connection()->lagMeter()) { KviStr tmp(KviStr::Format,"WHO %s",encodedChan); msg->connection()->lagMeter()->lagCheckRegister(tmp.ptr(),60); } if(!msg->connection()->sendFmtData("WHO %s",encodedChan))return; // disconnected chan->setSentWhoRequest(); } if(!KVI_OPTION_BOOL(KviOption_boolDisableBanListRequestOnJoin)) { if(!msg->connection()->sendFmtData("MODE %s b",encodedChan))return; // disconnected chan->setSentBanListRequest(); } } else { // This must be someone else...(or desync) int iFlags = 0; iFlags = msg->connection()->serverInfo()->modeFlagFromModeChar(chExtMode); KviUserListEntry * it = chan->join(szNick,szUser,szHost,iFlags); // FIXME: #warning "Trigger also OnVoice and OnOp here ?" // Note: checkDefaultAvatar() makes a KviRegisteredUser lookup // if later it is needed, make it return a pointer if(!(it->globalData()->avatar()))console->checkDefaultAvatar(it->globalData(),szNick,szUser,szHost); if(KVS_TRIGGER_EVENT_3_HALTED(KviEvent_OnJoin,chan,szNick,szUser,szHost)) msg->setHaltOutput(); // FIXME: #warning "WE COULD OPTIONALLY REQUEST A /WHO FOR THE USERS JOINING THAT WE DON'T KNOW THE HOST OF" } // Now say it to the world if(!msg->haltOutput()) { // FIXME: #warning "CHECK IF MESSAGES GO TO CONSOLE OR NOT" if(chExtMode != 0) { chan->output(KVI_OUT_JOIN, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has joined \r!c\r%Q\r [implicit +%c umode change]"), &szNick,&szUser,&szHost,&channel,chExtMode); } else { chan->output(KVI_OUT_JOIN, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has joined \r!c\r%Q\r"), &szNick,&szUser,&szHost,&channel); } } //if(!bisMe) deleted because we can open query with our nick TQString szChans; int iChans = msg->connection()->getCommonChannels(szNick,szChans); KviQuery * q = console->connection()->findQuery(szNick); if(q) { if(KVI_OPTION_BOOL(KviOption_boolEnableQueryTracing)) { q->output(KVI_OUT_QUERYTRACE, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has just joined \r!c\r%Q\r"),&szNick,&szUser, &szHost,&channel); q->notifyCommonChannels(szNick,szUser,szHost,iChans,szChans); } else { q->updateLabelText(); } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // PART // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralPart(KviIrcMessage *msg) { // PART // : PART : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); TQString szChan = msg->connection()->decodeText(msg->safeParam(0)); // Now lookup the channel KviConsole * console = msg->console(); KviChannel * chan = msg->connection()->findChannel(szChan); if(!chan) { //chan = msg->context()->findDeadChannel(msg->safeParam(0)); UNRECOGNIZED_MESSAGE(msg,__tr("Received a part message for an unknown channel, possible desync")); return; } // always decode with the textEncoding of the channel TQString partMsg = msg->paramCount() > 1 ? chan->decodeText(msg->safeTrailing()) : TQString(); if(IS_ME(msg,szNick)) { if(KVS_TRIGGER_EVENT_1_HALTED(KviEvent_OnMePart,chan,partMsg)) msg->setHaltOutput(); KviWindow * pOut = console; // It's me! if(chan->closeOnPart() && !KVI_OPTION_BOOL(KviOption_boolKeepChannelOpenOnPart)) { chan->frame()->closeWindow(chan); // <-- deleted path } else { chan->part(szNick); // this will trigger the action too chan->setDeadChan(); pOut = chan; } if(!msg->haltOutput()) { if(KVI_OPTION_BOOL(KviOption_boolShowOwnParts)) { if(partMsg.isEmpty()) pOut->output(KVI_OUT_PART,__tr2qs("You have left channel \r!c\r%Q\r"),&szChan); else pOut->output(KVI_OUT_PART,__tr2qs("You have left channel \r!c\r%Q\r: %Q"),&szChan,&partMsg); } } } else { // Someone else if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnPart,chan,szNick,szUser,szHost,partMsg)) msg->setHaltOutput(); chan->part(szNick); if(!msg->haltOutput()) { if(!partMsg.isEmpty()) chan->output(KVI_OUT_PART, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has left \r!c\r%Q\r: %Q"),&szNick,&szUser, &szHost,&szChan,&partMsg); else chan->output(KVI_OUT_PART, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has left \r!c\r%Q\r"),&szNick,&szUser, &szHost,&szChan); } if(KVI_OPTION_BOOL(KviOption_boolEnableQueryTracing)) { TQString szChans; int iChans = console->connection()->getCommonChannels(szNick,szChans); KviQuery * q = console->connection()->findQuery(szNick); if(q) { if(!partMsg.isEmpty()) q->output(KVI_OUT_QUERYTRACE, __tr2qs("\r!nc\r%Q\r [%Q@\r!h\r%Q\r] has just left \r!c\r%Q\r: %Q"), &szNick,&szUser,&szHost,&szChan,&partMsg); else q->output(KVI_OUT_QUERYTRACE, __tr2qs("\r!nc\r%Q\r [%Q@\r!h\r%Q\r] has just left \r!c\r%Q\r"), &szNick,&szUser,&szHost,&szChan); q->notifyCommonChannels(szNick,szUser,szHost,iChans,szChans); } } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // QUIT // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralQuit(KviIrcMessage *msg) { // QUIT // : QUIT : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); KviConsole * console = msg->console(); // NETSPLIT DETECTION STUFF // this doesn't need to be decoded for the moment const char * aux = msg->safeTrailing(); bool bWasSplit = false; //determine if signoff string matches "%.% %.%", and only one space (from eggdrop code) char *p = (char *)strchr(aux, ' '); if (p && (p == (char *)strrchr(aux,' '))) { char *daSpace = p; // one space detected. go ahead char *z1, *z2; *p = 0; z1 = (char *)strchr(p + 1, '.'); z2 = (char *)strchr(aux, '.'); if (z1 && z2 && (*(z1 + 1) > 47) && (z1 - 1 != p) && (z2 + 1 != p) && (z2 != aux) && console->connection()) { // server split, or else it looked like it anyway KviIrcConnectionNetsplitDetectorData * ndd = msg->connection()->netsplitDetectorData(); *p=' '; bWasSplit = true; time_t curTime = kvi_unixTime(); int diff = ((unsigned int)curTime) - ((unsigned int)ndd->lastNetsplitOnQuitTime()); bool bDuplicate = false; TQString szReason = aux; if(diff < 6) { if(KviTQString::equalCI(ndd->lastNetsplitOnQuitReason(),szReason)) { bDuplicate = true; } } ndd->setLastNetsplitOnQuitTime(curTime); ndd->setLastNetsplitOnQuitReason(szReason); if(!bDuplicate) { KviStr sz1(aux,daSpace - aux); KviStr sz2(daSpace + 1); TQString szD1 = msg->connection()->decodeText(sz1.ptr()); TQString szD2 = msg->connection()->decodeText(sz2.ptr()); if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnNetsplit,console,szD1,szD2)) { if(!msg->haltOutput()) console->output(KVI_OUT_SPLIT,__tr2qs("Netsplit detected: %s"),aux); } } } else *p = ' '; } // FIXME: #warning "Add a netsplit parameter ?" if(KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnQuit)) { // compute the channel list TQString chanlist; TQString szReason = msg->connection()->decodeText(msg->safeTrailing()); for(KviChannel *daChan=console->channelList()->first();daChan;daChan=console->channelList()->next()) { if(daChan->isOn(szNick)) { if(chanlist.isEmpty())chanlist = daChan->windowName(); else { chanlist.append(','); chanlist.append(daChan->windowName()); } } } KviKvsVariantList vList; vList.append(szNick); vList.append(szUser); vList.append(szHost); vList.append(szReason); vList.append(chanlist); if(KviKvsEventManager::instance()->trigger(KviEvent_OnQuit,console,&vList)) msg->setHaltOutput(); } for(KviChannel *c=console->channelList()->first();c;c=console->channelList()->next()) { if(c->part(szNick)) { if(!msg->haltOutput()) { TQString quitMsg = c->decodeText(msg->safeTrailing()); if(bWasSplit) { quitMsg.prepend("NETSPLIT "); } if(!msg->haltOutput())c->output(KVI_OUT_QUIT, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has quit IRC: %Q"), &szNick,&szUser,&szHost,&quitMsg); } } } if(!msg->haltOutput()) { KviQuery * q = msg->connection()->findQuery(szNick); if(q) { TQString quitMsg = q->decodeText(msg->safeTrailing()); if(bWasSplit) { quitMsg.prepend("NETSPLIT "); } q->output(KVI_OUT_QUIT,__tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has quit IRC: %Q"), &szNick,&szUser,&szHost,&quitMsg); } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // KICK // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralKick(KviIrcMessage *msg) { // KICK // : KICK : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); TQString szChan = msg->connection()->decodeText(msg->safeParam(0)); TQString victim = msg->connection()->decodeText(msg->safeParam(1)); KviConsole * console = msg->console(); KviChannel * chan = msg->connection()->findChannel(szChan); if(!chan){ // Ooops , desync with the server. UNRECOGNIZED_MESSAGE(msg,__tr("Received a kick message for an unknown channel, possible desync")); return; } TQString szKickMsg = chan->decodeText(msg->safeTrailing()); if(IS_ME(msg,victim)) { // ops...I have been kicked if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnMeKick,chan, szNick,szUser,szHost,szKickMsg)) msg->setHaltOutput(); if(!KVI_OPTION_STRING(KviOption_stringOnMeKickedSound).isEmpty()) KviKvsScript::run("snd.play $0",0,new KviKvsVariantList(new KviKvsVariant(KVI_OPTION_STRING(KviOption_stringOnMeKickedSound)))); TQString szPass = chan->channelKey(); if(KVI_OPTION_BOOL(KviOption_boolKeepChannelOpenOnKick)) { chan->userAction(szNick,szUser,szHost,KVI_USERACTION_KICK); chan->part(victim); chan->setDeadChan(); if(!msg->haltOutput()) { // FIXME: #warning "OPTION FOR THIS TO GO TO THE CONSOLE!" chan->output(KVI_OUT_MEKICK, __tr2qs("You have been kicked from \r!c\r%Q\r by \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"), &szChan,&szNick,&szUser,&szHost,&szKickMsg); } } else { chan->frame()->closeWindow(chan); // <-- deleted path if(!msg->haltOutput()) { // FIXME: #warning "This could go also to the active window!" console->output(KVI_OUT_MEKICK, __tr2qs("You have been kicked from \r!c\r%Q\r by \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"), &szChan,&szNick,&szUser,&szHost,&szKickMsg); } } if(KVI_OPTION_BOOL(KviOption_boolRejoinChannelOnKick)) { if(_OUTPUT_VERBOSE) console->output(KVI_OUT_SYSTEMMESSAGE,__tr2qs("Attempting to rejoin \r!c\r%Q\r..."),&szChan); KviTQCString szC = msg->connection()->encodeText(szChan); if(!szPass.isEmpty()) { KviTQCString szP = msg->connection()->encodeText(szChan); msg->connection()->sendFmtData("JOIN %s %s",szC.data(),szP.data()); } else msg->connection()->sendFmtData("JOIN %s",szC.data()); } } else { // ok...someone else has been kicked if(KVS_TRIGGER_EVENT_5_HALTED(KviEvent_OnKick,chan, szNick,szUser,szHost,victim,szKickMsg)) msg->setHaltOutput(); KviIrcUserEntry * e = msg->connection()->userDataBase()->find(victim); TQString szVHost; TQString szVUser; if(e) { szVHost = e->host(); szVUser = e->user(); } else { szVHost = "*"; szVUser = "*"; } chan->userAction(szNick,szUser,szHost,KVI_USERACTION_KICK); chan->part(victim); if(!msg->haltOutput()) { // FIXME: #warning "OPTION FOR THIS TO GO TO THE CONSOLE!" chan->output(KVI_OUT_KICK, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has been kicked from \r!c\r%Q\r by \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"), &victim,&szVUser,&szVHost,&szChan,&szNick,&szUser,&szHost,&szKickMsg); } if(KVI_OPTION_BOOL(KviOption_boolEnableQueryTracing)) { KviQuery * q = console->connection()->findQuery(victim); if(q) { TQString szChans; int iChans = console->connection()->getCommonChannels(victim,szChans); q->output(KVI_OUT_QUERYTRACE, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has just been kicked from \r!c\r%Q\r by \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"), &victim,&szVUser,&szVHost,&szChan, &szNick,&szUser,&szHost,&szKickMsg); q->notifyCommonChannels(victim,szVUser,szVHost,iChans,szChans); } } } } #ifdef COMPILE_CRYPT_SUPPORT #define DECRYPT_IF_NEEDED(_target,_txt,_type,_type2,_buffer,_retptr,_retmsgtype) \ if(KviCryptSessionInfo * cinf = _target->cryptSessionInfo()){ \ if(cinf->bDoDecrypt){ \ switch(cinf->pEngine->decrypt(_txt,_buffer)) \ { \ case KviCryptEngine::DecryptOkWasEncrypted: \ _retptr = _buffer.ptr(); \ _retmsgtype = _type2; \ break; \ case KviCryptEngine::DecryptOkWasPlainText: \ case KviCryptEngine::DecryptOkWasEncoded: \ _retptr = _buffer.ptr(); \ _retmsgtype = _type; \ break; \ default: /* also case KviCryptEngine::DecryptError: */ \ { \ TQString szEngineError = cinf->pEngine->lastError(); \ _target->output(KVI_OUT_SYSTEMERROR, \ __tr2qs("The following message appears to be encrypted, but the crypto engine failed to decode it: %Q"), \ &szEngineError); \ _retptr = _txt + 1; _retmsgtype=_type; \ } \ break; \ } \ } else _retptr = _txt, _retmsgtype=_type; \ } else _retptr = _txt, _retmsgtype=_type; #else //!COMPILE_CRYPT_SUPPORT #define DECRYPT_IF_NEEDED(_target,_txt,_type,_type2,_buffer,_retptr,_retmsgtype) \ _retptr = _txt; _retmsgtype = _type; #endif //!COMPILE_CRYPT_SUPPORT void KviServerParser::parseLiteralPrivmsg(KviIrcMessage *msg) { // PRIVMSG // :source PRIVMSG : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); TQString szTarget = msg->connection()->decodeText(msg->safeParam(0)); TQString szMsg = msg->connection()->decodeText(msg->safeTrailing()); KviConsole * console = msg->console(); KviRegisteredUser * u = msg->connection()->userDataBase()->registeredUser(szNick,szUser,szHost); //Highlight it? // FIXME: #warning "DEDICATED CTCP WINDOW ?" KviStr * pTrailing = msg->trailingString(); if(pTrailing) { if(*(pTrailing->ptr()) == 0x01) { if(pTrailing->len() > 1) { if(pTrailing->lastCharIs(0x01))pTrailing->cutRight(1); pTrailing->cutLeft(1); KviCtcpMessage ctcp; ctcp.msg = msg; ctcp.pData = pTrailing->ptr(); KviIrcMask talker(szNick,szUser,szHost); // FIXME! ctcp.pSource = &talker; ctcp.szTarget = msg->connection()->decodeText(msg->safeParam(0)); ctcp.bIgnored = false; ctcp.bIsFlood = false; ctcp.bUnknown = false; parseCtcpRequest(&ctcp); return; } } } // Normal PRIVMSG if(IS_ME(msg,szTarget)) { //Ignore it? if (u) { if (u->isIgnoreEnabledFor(KviRegisteredUser::Query)) { if(KVS_TRIGGER_EVENT_5_HALTED(KviEvent_OnIgnoredMessage,msg->console(),szNick,szUser,szHost,szTarget,szMsg)) return; if (KVI_OPTION_BOOL(KviOption_boolVerboseIgnore)) { console->output(KVI_OUT_IGNORE,__tr2qs("Ignoring query-PRIVMSG from \r!nc\r%Q\r [%Q@\r!h\r%Q\r]: %Q"),&szNick,&szUser,&szHost,&szMsg); } return; } } // FIXME: #warning "PROCESS MULTIMEDIA FILE REQUESTS" // if(g_pOptions->m_bListenToMultimediaFileRequests) // { // if(*aux == '!') // { // const char *tmp = aux; // tmp++; // if(kvi_strEqualCI(tmp,m_pFrm->m_global.szCurrentNick.ptr())) // { // // A multimedia file request ? // tmp += m_pFrm->m_global.szCurrentNick.len(); // if(((*tmp) == ' ') || ((*tmp) == '\t')) // { // while(((*tmp) == ' ') || ((*tmp) == '\t'))tmp++; // if(*tmp) // { // KviStr file = tmp; // KviStr filePath; // m_pFrm->findMultimediaFileOffert(filePath,file); // if(filePath.hasData()) // { // m_pFrm->activeWindow()->output(KVI_OUT_INTERNAL,__tr("%s requests previously offered file %s: sending (%s)"),talker.nick(),file.ptr(),filePath.ptr()); // KviStr cmd(KviStr::Format,"DCC SEND %s %s",talker.nick(),filePath.ptr()); // m_pFrm->m_pUserParser->parseUserCommand(cmd,m_pConsole); // return; // } else { // m_pFrm->activeWindow()->output(KVI_OUT_INTERNAL,__tr("%s requests file %s: no such file was offered , ignoring"),talker.nick(),file.ptr()); // return; // } // } // } // } // } // } // A query request // do we have a matching window ? KviQuery * query = msg->connection()->findQuery(szNick); if(!query) { // New query requested. Check if we really should create it or not // first of all the anti spam , if desired. // the antispam blocks anything else // Eventually we could trigger a special event to notify the user of the // spam message... if(KVI_OPTION_BOOL(KviOption_boolUseAntiSpamOnPrivmsg)) { KviStr * theMsg = msg->trailingString(); if(theMsg) { KviStr spamWord; if(kvi_mayBeSpam(theMsg,spamWord)) { // FIXME: OnSpam ? if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolSilentAntiSpam))) { TQString szMsg = msg->connection()->decodeText(msg->safeTrailing()); console->output(KVI_OUT_SPAM, __tr2qs("Spam privmsg from \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q (matching spamword \"%s\")"), &szNick,&szUser,&szHost,&szMsg,spamWord.ptr()); } return; } } } // this is not a spam, or at least it hasn't been recognized as spam // user option ? (this should again override any script) // if the scripters want really to force the query creation they can do // it manually or they can set the option to true at KVIrc startup if(KVI_OPTION_BOOL(KviOption_boolCreateQueryOnPrivmsg)) { TQString szMsg = msg->connection()->decodeText(msg->safeTrailing()); // We still want to create it // Give the scripter a chance to filter it out again if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryWindowRequest, console,szNick,szUser,szHost,szMsg)) { // check if the scripter hasn't created it query = msg->connection()->findQuery(szNick); } else { // no query yet, create it! // this will trigger OnQueryWindowCreated query = console->connection()->createQuery(szNick); // and this will trigger OnQueryTargetAdded query->setTarget(szNick,szUser,szHost); } } } // ok, now we either have a query or not if(query) { // ok, we have the query. Trigger the user action anyway query->userAction(szNick,szUser,szHost,KVI_USERACTION_PRIVMSG); // decrypt the message if needed KviStr szBuffer; const char * txtptr; int msgtype; DECRYPT_IF_NEEDED(query,msg->safeTrailing(),KVI_OUT_QUERYPRIVMSG,KVI_OUT_QUERYPRIVMSGCRYPTED,szBuffer,txtptr,msgtype) // trigger the script event and eventually kill the output TQString szMsgText = query->decodeText(txtptr); if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryMessage,query,szNick,szUser,szHost,szMsgText)) msg->setHaltOutput(); if(!KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound).isEmpty() && query!=g_pActiveWindow) { // KviKvsScript does NOT take parameters ownership KviKvsVariantList soundParams(new KviKvsVariant(KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound))); //KviKvsScript::run("snd.play $0",0,&soundParams); <-- we also should provide a window for the script: it's always a good idea KviKvsScript::run("snd.play $0",query,&soundParams); } // spit out the message text if(!msg->haltOutput()) { int iFlags = 0; if(!query->hasAttention()) { if(KVI_OPTION_BOOL(KviOption_boolFlashQueryWindowOnNewMessages)) { // avoid double window flashing iFlags |= KviConsole::NoWindowFlashing; query->demandAttention(); } if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewQueryMessages)) { // don't send the message to the notifier twice iFlags |= KviConsole::NoNotifier; #ifdef COMPILE_USE_QT4 TQString szMsg = TQt::escape(szMsgText); #else TQString szMsg = TQStyleSheet::escape(szMsgText); #endif //tqDebug("kvi_sp_literal.cpp:908 debug: %s",szMsg.data()); g_pApp->notifierMessage(query,KVI_SMALLICON_QUERYPRIVMSG,szMsg,1800); } } console->outputPrivmsg(query,msgtype,szNick,szUser,szHost,szMsgText,iFlags); } } else { // no query creation: no decryption possible // trigger the query message event in the console TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryMessage,console,szNick,szUser,szHost,szMsgText)) msg->setHaltOutput(); // we don't have a query here! //if(!KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound).isEmpty() && query!=g_pActiveWindow) if(!KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound).isEmpty() && console!=g_pActiveWindow) { // same as above KviKvsVariantList soundParams(new KviKvsVariant(KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound))); KviKvsScript::run("snd.play $0",console,&soundParams); } // spit the message text out if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolExternalMessagesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); if(KviIrcConnection * pConnection = console->connection()) { KviWindow * aWin = console->activeWindow(); if((aWin->type() == KVI_WINDOW_TYPE_CHANNEL) && ((KviChannel *)aWin)->isOn(szNick)) pOut = aWin; else { for(KviChannel * c = pConnection->channelList()->first();c;c = pConnection->channelList()->next()) if(c->isOn(szNick)) { pOut = (KviWindow *) c; break; } } } pOut->output(KVI_OUT_QUERYPRIVMSG,"[PRIVMSG \r!nc\r%Q\r]: %Q",&szNick,&szMsgText); } } } else { // Channel PRIVMSG KviChannel * chan = msg->connection()->findChannel(szTarget); TQString szOriginalTarget = szTarget; TQString szPrefixes; //Ignore it? if(u) { if(u->isIgnoreEnabledFor(KviRegisteredUser::Channel)) { if(KVS_TRIGGER_EVENT_5_HALTED(KviEvent_OnIgnoredMessage,msg->console(),szNick,szUser,szHost,szTarget,szMsg)) return; if (KVI_OPTION_BOOL(KviOption_boolVerboseIgnore)) { console->output(KVI_OUT_IGNORE,__tr2qs("Ignoring channel-PRIVMSG from \r!nc\r%Q\r [%Q@\r!h\r%Q\r]: %Q"),&szNick,&szUser,&szHost,&szMsg); } return; } } if(!chan) { // check if the channel has some leading mode prefixes while((szTarget.length() > 0) && console->connection()->serverInfo()->isSupportedModePrefix(szTarget[0].unicode())) { szPrefixes += szTarget[0]; szTarget.remove(0,1); } chan = msg->connection()->findChannel(szTarget); } if(!chan) { if(!msg->haltOutput()) { TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolOperatorMessagesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); TQString broad; KviTQString::sprintf(broad,"[>> %Q] %Q",&szOriginalTarget,&szMsgText); console->outputPrivmsg(pOut,KVI_OUT_BROADCASTPRIVMSG,szNick,szUser,szHost,broad,0); } } else { chan->userAction(szNick,szUser,szHost,KVI_USERACTION_PRIVMSG); KviStr szBuffer; const char * txtptr; int msgtype; DECRYPT_IF_NEEDED(chan,msg->safeTrailing(),KVI_OUT_CHANPRIVMSG,KVI_OUT_CHANPRIVMSGCRYPTED,szBuffer,txtptr,msgtype) TQString szMsgText = chan->decodeText(txtptr); if(KVS_TRIGGER_EVENT_5_HALTED(KviEvent_OnChannelMessage,chan,szNick,szUser,szHost,szMsgText,szPrefixes)) msg->setHaltOutput(); if(!msg->haltOutput()) { if(szPrefixes.length() > 0) { TQString szBroad; KviTQString::sprintf(szBroad,"[>> %Q\r!c\r%Q\r] %Q",&szPrefixes,&szTarget,&szMsgText); console->outputPrivmsg(chan,msgtype,szNick,szUser,szHost,szBroad,0); } else { console->outputPrivmsg(chan,msgtype,szNick,szUser,szHost,szMsgText,0); } } } } } void KviServerParser::parseLiteralNotice(KviIrcMessage *msg) { // NOTICE // :source NOTICE : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); KviConsole * console = msg->console(); if(szHost == "*") { if(szUser == "*") { if(szNick.find('.') != -1) { // server notice // FIXME: "Dedicated window for server notices ?" TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); if(KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnServerNotice,console,szNick,szMsgText)) msg->setHaltOutput(); if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolServerNoticesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); pOut->output(KVI_OUT_SERVERNOTICE,"[\r!s\r%Q\r]: %Q",&szNick,&szMsgText); } return; } } } // FIXME: "DEDICATED CTCP WINDOW ?" KviStr * pTrailing = msg->trailingString(); if(pTrailing) { if(*(pTrailing->ptr()) == 0x01){ if(pTrailing->len() > 1) { if(pTrailing->lastCharIs(0x01))pTrailing->cutRight(1); pTrailing->cutLeft(1); KviCtcpMessage ctcp; ctcp.msg = msg; ctcp.pData = pTrailing->ptr(); KviIrcMask talker(szNick,szUser,szHost); // FIXME ctcp.pSource = &talker; ctcp.szTarget = msg->connection()->decodeText(msg->safeParam(0)); ctcp.bIgnored = false; ctcp.bIsFlood = false; ctcp.bUnknown = false; parseCtcpReply(&ctcp); return; } } } TQString szTarget = msg->connection()->decodeText(msg->safeParam(0)); KviRegisteredUser * u = msg->connection()->userDataBase()->registeredUser(szNick,szUser,szHost); //Ignore it? if(u) { if(u->isIgnoreEnabledFor(KviRegisteredUser::Notice)) { if(KVI_OPTION_BOOL(KviOption_boolVerboseIgnore)) { TQString szMsg = msg->connection()->decodeText(msg->safeTrailing()); console->output(KVI_OUT_IGNORE,__tr2qs("Ignoring Notice from \r!nc\r%Q\r [%Q@\r!h\r%Q\r]: %Q"),&szNick,&szUser,&szHost,&szMsg); } return; } } // Normal NOTICE if(IS_ME(msg,szTarget)) { // FIXME: "The NickServ and ChanServ handling should be optional!" if(KviTQString::equalCI(szNick,"NickServ")) { TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnNickServNotice,console,szNick,szUser,szHost,szMsgText)) msg->setHaltOutput(); // nickname service... does it ask for identification ? if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolServicesNoticesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); pOut->output(KVI_OUT_NICKSERV,"\r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q",&szNick,&szUser,&szHost,&szMsgText); } bool bAuthDone = false; KviNickServRuleSet * r = msg->connection()->target()->network()->nickServRuleSet(); if(r) { if(r->isEnabled() && !r->isEmpty()) { KviIrcMask talker(szNick,szUser,szHost); KviNickServRule * rule = r->matchRule(msg->connection()->currentNickName(),&talker,szMsgText); if(rule) { bAuthDone = true; console->outputNoFmt(KVI_OUT_SYSTEMMESSAGE,__tr2qs("NickServ requests authentication, executing scheduled command")); if(!KviKvsScript::run(rule->identifyCommand(),console)) { console->outputNoFmt(KVI_OUT_SYSTEMERROR,__tr2qs("The scheduled NickServ identification command appears to be broken, please change the setting")); } } } } if(!bAuthDone) { if(g_pNickServRuleSet->isEnabled() && !g_pNickServRuleSet->isEmpty()) { KviIrcMask talker(szNick,szUser,szHost); KviNickServRule * rule = g_pNickServRuleSet->matchRule(msg->connection()->currentNickName(),&talker,szMsgText,msg->connection()->currentServerName()); if(rule) { console->outputNoFmt(KVI_OUT_SYSTEMMESSAGE,__tr2qs("NickServ requests authentication, executing scheduled command")); if(!KviKvsScript::run(rule->identifyCommand(),console)) { console->outputNoFmt(KVI_OUT_SYSTEMERROR,__tr2qs("The scheduled NickServ identification command appears to be broken, please change the setting")); } } } } return; } if(KviTQString::equalCI(szNick,"ChanServ")) { TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnChanServNotice,console,szNick,szUser,szHost,szMsgText)) msg->setHaltOutput(); if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolServicesNoticesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); pOut->output(KVI_OUT_CHANSERV,"\r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q",&szNick,&szUser,&szHost,&szMsgText); } return; } // FIXME: PROCESS MULTIMEDIA FILE REQUESTS // A query request // do we have a matching window ? KviQuery * query = msg->connection()->findQuery(szNick); if(!query) { // New query requested. Check if we really should create it or not // first of all the anti spam , if desired. // the antispam blocks anything else // Eventually we could trigger a special event to notify the user of the // spam message... if(KVI_OPTION_BOOL(KviOption_boolUseAntiSpamOnNotice)) { KviStr * theMsg = msg->trailingString(); // FIXME if(theMsg) { KviStr spamWord; if(kvi_mayBeSpam(theMsg,spamWord)) { // FIXME: OnSpam ? if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolSilentAntiSpam))) { TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); TQString szSpamWord = spamWord.ptr(); console->output(KVI_OUT_SPAM,__tr2qs("Spam notice from \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q (matching spamword \"%Q\")"), &szNick,&szUser,&szHost,&szMsgText,&szSpamWord); } return; } } } // this is not a spam, or at least it hasn't been recognized as spam // user option ? (this should again override any script) // if the scripters want really to force the query creation they can do // it manually or they can set the option to true at KVIrc startup if(KVI_OPTION_BOOL(KviOption_boolCreateQueryOnNotice)) { TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); // We still want to create it // Give the scripter a chance to filter it out again if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryWindowRequest,console,szNick,szUser,szHost,szMsgText)) { // check if the scripter hasn't created it query = msg->connection()->findQuery(szNick); } else { // no query yet, create it! // this will trigger OnQueryWindowCreated query = console->connection()->createQuery(szNick); // and this will trigger OnQueryTargetAdded query->setTarget(szNick,szUser,szHost); } } } // ok, now we either have a query or not if(query) { // ok, we have the query. Trigger the user action anyway query->userAction(szNick,szUser,szHost,KVI_USERACTION_NOTICE); // decrypt it if needed KviStr szBuffer; const char * txtptr; int msgtype; DECRYPT_IF_NEEDED(query,msg->safeTrailing(),KVI_OUT_QUERYNOTICE,KVI_OUT_QUERYNOTICECRYPTED,szBuffer,txtptr,msgtype) TQString szMsgText = query->decodeText(txtptr); // trigger the script event and eventually kill the output if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryNotice,query,szNick,szUser,szHost,szMsgText)) msg->setHaltOutput(); // spit out the message text if(!msg->haltOutput()) { int iFlags = 0; if(!query->hasAttention()) { if(KVI_OPTION_BOOL(KviOption_boolFlashQueryWindowOnNewMessages)) { // avoid double window flashing iFlags |= KviConsole::NoWindowFlashing; query->demandAttention(); } if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewQueryMessages)) { // don't send the message twice to the notifier iFlags |= KviConsole::NoNotifier; #ifdef COMPILE_USE_QT4 TQString szMsg = TQt::escape(szMsgText); #else TQString szMsg = TQStyleSheet::escape(szMsgText); #endif //tqDebug("kvi_sp_literal.cpp:908 debug: %s",szMsg.data()); g_pApp->notifierMessage(query,KVI_SMALLICON_QUERYNOTICE,szMsg,1800); } } console->outputPrivmsg(query,msgtype,szNick,szUser,szHost,szMsgText,iFlags); } } else { TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); // no query creation: no decryption possible // trigger the query message event in the console if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryNotice,console,szNick,szUser,szHost,szMsgText)) msg->setHaltOutput(); // spit the message text out if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolExternalMessagesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); if(KviIrcConnection * pConnection = console->connection()) { KviWindow * aWin = console->activeWindow(); if((aWin->type() == KVI_WINDOW_TYPE_CHANNEL) && ((KviChannel *)aWin)->isOn(szNick)) pOut = aWin; else { for(KviChannel * c = pConnection->channelList()->first();c;c = pConnection->channelList()->next()) if(c->isOn(szNick)) { pOut = (KviWindow *) c; break; } } } pOut->output(KVI_OUT_QUERYNOTICE,"*\r!n\r%Q\r* %Q",&szNick,&szMsgText); } } return; } // Channel NOTICE KviChannel * chan = msg->connection()->findChannel(szTarget); TQString szOriginalTarget = szTarget; TQString szPrefixes; if(!chan) { // check if the channel has some leading mode prefixes while((szTarget.length() > 0) && console->connection()->serverInfo()->isSupportedModePrefix(szTarget[0].unicode())) { szPrefixes += szTarget[0]; szTarget.remove(0,1); } chan = msg->connection()->findChannel(szTarget); } if(!chan) { if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolOperatorMessagesToActiveWindow) ? console->activeWindow() : (KviWindow *)(console); TQString szBroad; TQString szMsgText = msg->connection()->decodeText(msg->safeTrailing()); KviTQString::sprintf(szBroad,"[>> %Q] %Q",&szOriginalTarget,&szMsgText); console->outputPrivmsg(pOut,KVI_OUT_BROADCASTNOTICE,szNick,szUser,szHost,szBroad,0); return; } } else { chan->userAction(szNick,szUser,szHost,KVI_USERACTION_NOTICE); KviStr szBuffer; const char * txtptr; int msgtype; DECRYPT_IF_NEEDED(chan,msg->safeTrailing(),KVI_OUT_CHANNELNOTICE,KVI_OUT_CHANNELNOTICECRYPTED,szBuffer,txtptr,msgtype) TQString szMsgText = chan->decodeText(txtptr); if(KVS_TRIGGER_EVENT_3_HALTED(KviEvent_OnChannelNotice,chan,szNick,szMsgText,szOriginalTarget))msg->setHaltOutput(); if(!msg->haltOutput()) { if(szPrefixes.length() > 0) { TQString szBroad; KviTQString::sprintf(szBroad,"[>> %Q\r!c\r%Q\r] %Q",&szPrefixes,&szTarget,&szMsgText); console->outputPrivmsg(chan,msgtype,szNick,szUser,szHost,szBroad,0); } else { console->outputPrivmsg(chan,msgtype,szNick,szUser,szHost,szMsgText,0); } } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // TOPIC // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void KviServerParser::parseLiteralTopic(KviIrcMessage *msg) { // TOPIC // : TOPIC : TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); TQString szTarget = msg->connection()->decodeText(msg->safeParam(0)); // Now lookup the channel KviChannel * chan = msg->connection()->findChannel(szTarget); if(!chan) { UNRECOGNIZED_MESSAGE(msg,__tr2qs("Received a topic message for an unknown channel, possible desync")); return; } TQString szTopic = chan->decodeText(msg->safeTrailing()); if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnTopic,chan,szNick,szUser,szHost,szTopic)) msg->setHaltOutput(); chan->topicWidget()->setTopic(szTopic); chan->topicWidget()->setTopicSetBy(szNick); TQString tmp = TQDateTime::currentDateTime().toString(); chan->topicWidget()->setTopicSetAt(tmp); chan->userAction(szNick,szUser,szHost,KVI_USERACTION_TOPIC); if(!msg->haltOutput()) { chan->output(KVI_OUT_TOPIC, __tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] has changed topic to \"%Q%c\""), &szNick,&szUser,&szHost,&szTopic,KVI_TEXT_RESET); } } void KviServerParser::parseLiteralNick(KviIrcMessage *msg) { // NICK // :source NICK TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); KviConsole * console = msg->console(); TQString szNewNick = msg->connection()->decodeText(msg->safeTrailing()); bool bIsMe = IS_ME(msg,szNick); if(bIsMe) { // We have changed our nick msg->connection()->nickChange(szNewNick); if(KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnMeNickChange,console,szNick,szNewNick)) msg->setHaltOutput(); } else { if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnNickChange,console,szNick,szUser,szHost,szNewNick)) msg->setHaltOutput(); } for(KviChannel * c = console->channelList()->first();c;c = console->channelList()->next()) { if(c->nickChange(szNick,szNewNick)) { if(!msg->haltOutput()) c->output(KVI_OUT_NICK,__tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] is now known as \r!n\r%Q\r"), &szNick,&szUser,&szHost,&szNewNick); // FIXME if(bIsMe)output(YOU ARE now known as.. ?) } if(bIsMe)c->updateCaption(); } // FIXME: #warning "NEW NICK MIGHT BE REGISTERED AND HAVE AN AVATAR!" if(bIsMe) { // just update all the captions : we have changed OUR nick for(KviQuery * q = console->queryList()->first();q;q = console->queryList()->next()) { if(!msg->haltOutput()) q->output(KVI_OUT_NICK,__tr2qs("You have changed your nickname to %Q"),&szNewNick); q->updateCaption(); } } KviQuery * q = console->connection()->findQuery(szNick); // It CAN happen that szNewNick first queries us without being // on any channel then he QUITS , he reconnects , he joins // a channel with szNick , queries us and changes nick to szNewNick : gotcha! // should merge the queries! KviQuery * old = console->connection()->findQuery(szNewNick); if(old && (old != q)) { if(KVI_OPTION_BOOL(KviOption_boolEnableQueryTracing) && (!_OUTPUT_QUIET)) { old->output(KVI_OUT_QUERYTRACE, __tr2qs("The target of this query was lost and has been found when \r!n\r%Q\r [%Q@\r!h\r%Q\r] changed his nickname to \r!n\r%Q\r"), &szNick,&szUser,&szHost,&szNewNick); } if(q) { bool bTQWasActive = (q == g_pActiveWindow); if(!_OUTPUT_MUTE) { old->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The recent nickname change from \r!n\r%Q\r to \r!n\r%Q\r caused a query collision: merging output"), &szNick,&szNewNick); } old->mergeQuery(q); q->frame()->closeWindow(q); // deleted path if(!msg->haltOutput()) old->output(KVI_OUT_NICK,__tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] is now known as \r!n\r%Q\r"), &szNick,&szUser,&szHost,&szNewNick); if(!_OUTPUT_MUTE) old->output(KVI_OUT_SYSTEMWARNING,__tr2qs("End of merged output")); old->userAction(szNewNick,szUser,szHost,KVI_USERACTION_NICK); if(bTQWasActive)old->delayedAutoRaise(); } if(KVI_OPTION_BOOL(KviOption_boolEnableQueryTracing)) { TQString szChans; int iChans = console->connection()->getCommonChannels(szNewNick,szChans); old->notifyCommonChannels(szNewNick,szUser,szHost,iChans,szChans); } } else { if(q) { // the target SHOULD have changed his nick here if(!q->nickChange(szNick,szNewNick)) tqDebug("Internal error: query %s failed to change nick from %s to s",szNick.utf8().data(),szNick.utf8().data(),szNewNick.utf8().data()); if(!msg->haltOutput()) q->output(KVI_OUT_NICK,__tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] is now known as \r!n\r%Q\r"), &szNick,&szUser,&szHost,&szNewNick); q->userAction(szNewNick,szUser,szHost,KVI_USERACTION_NICK); } } // FIXME: #warning "UPDATE ALL THE OTHER CONNECTION RELATED WINDOW CAPTIONS WHEN bIsMe!!" } void KviServerParser::parseLiteralInvite(KviIrcMessage *msg) { // INVITE // :source INVITE TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); TQString szTarget = msg->connection()->decodeText(msg->safeParam(0)); TQString szChannel = msg->connection()->decodeText(msg->safeParam(1)); KviConsole * console = msg->console(); KviRegisteredUser * u = msg->connection()->userDataBase()->registeredUser(szNick,szUser,szHost); //Ignore it? if(u) { if(u->isIgnoreEnabledFor(KviRegisteredUser::Invite)) { if(KVI_OPTION_BOOL(KviOption_boolVerboseIgnore)) { console->output(KVI_OUT_IGNORE,__tr2qs("Ignoring invite from \r!nc\r%Q\r [%Q@\r!h\r%Q\r]"),&szNick,&szUser,&szHost); } return; } } if(IS_ME(msg,szTarget)) { if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnInvite,msg->console(),szNick,szUser,szHost,szChannel)) msg->setHaltOutput(); if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolInvitesToActiveWindow) ? msg->console()->activeWindow() : (KviWindow *)(msg->console()); TQString szAction = KVI_OPTION_BOOL(KviOption_boolAutoJoinOnInvite) ? __tr2qs("autojoining") : __tr2qs("double-click the channel name to join"); pOut->output(KVI_OUT_INVITE,__tr2qs("\r!n\r%Q\r [%Q@\r!h\r%Q\r] invites you to channel \r!c\r%Q\r (%Q)"), &szNick,&szUser,&szHost,&szChannel,&szAction); } if(KVI_OPTION_BOOL(KviOption_boolAutoJoinOnInvite)) msg->connection()->sendFmtData("JOIN %s",msg->safeParam(1)); } else { UNRECOGNIZED_MESSAGE(msg,__tr("Received an invite message directed to another nick, possible desync")); } } void KviServerParser::parseLiteralWallops(KviIrcMessage *msg) { // WALLOPS // :source WALLOPS :msg TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); TQString szMsg = msg->connection()->decodeText(msg->safeTrailing()); if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnWallops,msg->console(),szNick,szUser,szHost,szMsg)) msg->setHaltOutput(); if(!msg->haltOutput()) { KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolOperatorMessagesToActiveWindow) ? msg->console()->activeWindow() : (KviWindow *)(msg->console()); pOut->output(KVI_OUT_WALLOPS,__tr2qs("WALLOPS from \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"), &szNick,&szUser,&szHost,&szMsg); } } void KviServerParser::parseUserMode(KviIrcMessage *msg,const char * modeflptr) { // changed my user mode bool bSet = true; while(*modeflptr) { switch(*modeflptr) { case '+': bSet = true; break; case '-': bSet = false; break; default: if(msg->connection()->changeUserMode(*modeflptr,bSet)) { if(msg->connection()->serverInfo()->registerModeChar()==*modeflptr) { KviKvsVariantList vList; KviKvsEventManager::instance()->trigger(KviEvent_OnNickServAuth,msg->console(),&vList); } // There was a mode change if(KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnUserMode)) { TQString szModeFlag(bSet ? TQChar('+') : TQChar('-')); szModeFlag += TQChar(*modeflptr); KviKvsVariantList vList(new KviKvsVariant(szModeFlag)); if(KviKvsEventManager::instance()->trigger(KviEvent_OnUserMode,msg->console(),&vList)) msg->setHaltOutput(); } } break; } ++modeflptr; } } void KviServerParser::parseLiteralMode(KviIrcMessage *msg) { // NICK // :source MODE target // :source MODE +|-modeflag // :source MODE +-modeflags [parameters] TQString szNick,szUser,szHost; msg->decodeAndSplitPrefix(szNick,szUser,szHost); // if(!source.hasHost()) // { // // This is a server or a channel service // KviStr snick = source.nick(); // if(snick.contains('.'))source.setHost(source.nick()); // this is a server // } TQString szTarget = msg->connection()->decodeText(msg->safeParam(0)); KviStr modefl(msg->safeParam(1)); if(IS_ME(msg,szTarget)) { parseUserMode(msg,modefl.ptr()); if(!msg->haltOutput()) msg->console()->output(KVI_OUT_MODE,__tr2qs("You have set user mode %s"),modefl.ptr()); } else { // a channel mode KviChannel * chan = msg->connection()->findChannel(szTarget); if(!chan){ // Ooops , desync with the server. UNRECOGNIZED_MESSAGE(msg,__tr("Received a mode change for an unknown channel, possible desync")); return; } chan->userAction(szNick,szUser,szHost,KVI_USERACTION_CHANMODE); parseChannelMode(szNick,szUser,szHost,chan,modefl,msg,2); } } void KviServerParser::parseChannelMode(const TQString &szNick,const TQString &szUser,const TQString &szHost,KviChannel * chan,KviStr &modefl,KviIrcMessage *msg,int curParam) { // FIXME: freenode has two ugly incompatible extensions: // mode e: that is NOT viewable (???) // mode q that stands for "quiet-ban" // mode #chan +q mask // adds mask to the banlist with the prefix % // and doesn't allow the users matching the mask to talk to the channel bool bSet = true; const char * aux = modefl.ptr(); TQString aParam; TQString nickBuffer; TQString hostBuffer; if(szHost != "*") { KviTQString::sprintf(nickBuffer,"\r!n\r%Q\r",&szNick); KviTQString::sprintf(hostBuffer,"\r!h\r%Q\r",&szHost); } else { if(nickBuffer.find('.') != -1) { // This looks a lot like a server! KviTQString::sprintf(nickBuffer,"\r!s\r%Q\r",&szNick); } else { // Probably a service....whois should work KviTQString::sprintf(nickBuffer,"\r!n\r%Q\r",&szNick); } hostBuffer = szHost; } KviIrcMask * auxMask; int curParamSave = curParam; bool bIsMe; //FIXME: Use PREFIX in 005 numeric instead of bServerSupportsModeIe - get rid of it altogether //bool bModeIe = console->connection()->serverInfo()->supportsModesIe(); while(*aux) { switch(*aux) { case '+': bSet = true; break; case '-': bSet = false; break; case 'k': if(bSet)aParam = msg->safeParam(curParam++); else aParam = ""; chan->setChannelKey(aParam); if(bSet) { if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnKeySet,chan,szNick,szUser,szHost,aParam)) msg->setHaltOutput(); } else { if(KVS_TRIGGER_EVENT_3_HALTED(KviEvent_OnKeyUnset,chan,szNick,szUser,szHost)) msg->setHaltOutput(); } if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges))) { if(bSet)chan->output(KVI_OUT_KEY, __tr2qs("%Q [%Q@%Q] has set channel key to \"\r!m-k\r%Q\r\""), &nickBuffer,&szUser,&hostBuffer,&aParam); else chan->output(KVI_OUT_KEY, __tr2qs("%Q [%Q@%Q] has unset the channel key"), &nickBuffer,&szUser,&hostBuffer); } break; case 'l': if(bSet)aParam = msg->safeParam(curParam++); else aParam = ""; chan->setChannelLimit(aParam); if(bSet) { if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnLimitSet,chan,szNick,szUser,szHost,aParam)) msg->setHaltOutput(); } else { if(KVS_TRIGGER_EVENT_3_HALTED(KviEvent_OnLimitUnset,chan,szNick,szUser,szHost)) msg->setHaltOutput(); } if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges))) { if(bSet)chan->output(KVI_OUT_LIMIT, __tr2qs("%Q [%Q@%Q] has set channel \r!m-l\rlimit to %Q\r"), &nickBuffer,&szUser,&hostBuffer,&aParam); else chan->output(KVI_OUT_LIMIT, __tr2qs("%Q [%Q@%Q] has unset the channel limit"), &nickBuffer,&szUser,&hostBuffer); } break; #define CHANUSER_MODE(__modechar,__chanfunc,__evmeset,__evmeunset,__evset,__evunset,__icomeset,__icomeunset,__icoset,__icounset) \ case __modechar: \ if(msg->connection()->serverInfo()->isSupportedModeFlag(__modechar)) \ { \ aParam = msg->connection()->decodeText(msg->safeParam(curParam++)); \ chan->__chanfunc(aParam,bSet); \ bIsMe = IS_ME(msg,aParam); \ if(bIsMe) \ { \ if(KVS_TRIGGER_EVENT_3_HALTED(bSet ? __evmeset : __evmeunset,chan,szNick,szUser,szHost))msg->setHaltOutput(); \ chan->updateCaption(); \ } else { \ if(KVS_TRIGGER_EVENT_4_HALTED(bSet ? __evset : __evunset,chan,szNick,szUser,szHost,aParam))msg->setHaltOutput(); \ } \ if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges))) \ { \ chan->output(bSet ? (bIsMe ? __icomeset : __icoset) : (bIsMe ? __icomeunset : __icounset), \ __tr2qs("%Q [%Q@%Q] has set mode %c%c \r!n\r%Q\r"), \ &nickBuffer,&szUser,&hostBuffer,bSet ? '+' : '-',__modechar,&aParam); \ } \ } else {\ chan->setChannelMode(__modechar,bSet);\ if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges)))\ {\ chan->output(KVI_OUT_CHANMODE,\ __tr2qs("%Q [%Q@%Q] has set channel \r!m%c%c\rmode %c%c\r"),\ &nickBuffer,&szUser,&hostBuffer,\ bSet ? '-' : '+',__modechar,bSet ? '+' : '-',__modechar);\ }\ }\ break; CHANUSER_MODE('q',setChanOwner,KviEvent_OnMeChanOwner,KviEvent_OnMeDeChanOwner,KviEvent_OnChanOwner,KviEvent_OnDeChanOwner,KVI_OUT_MECHANOWNER,KVI_OUT_MEDECHANOWNER,KVI_OUT_CHANOWNER,KVI_OUT_DECHANOWNER) CHANUSER_MODE('a',setChanAdmin,KviEvent_OnMeChanAdmin,KviEvent_OnMeDeChanAdmin,KviEvent_OnChanAdmin,KviEvent_OnDeChanAdmin,KVI_OUT_MECHANADMIN,KVI_OUT_MEDECHANADMIN,KVI_OUT_CHANADMIN,KVI_OUT_DECHANADMIN) CHANUSER_MODE('o',op,KviEvent_OnMeOp,KviEvent_OnMeDeOp,KviEvent_OnOp,KviEvent_OnDeOp,KVI_OUT_MEOP,KVI_OUT_MEDEOP,KVI_OUT_OP,KVI_OUT_DEOP) CHANUSER_MODE('h',halfop,KviEvent_OnMeHalfOp,KviEvent_OnMeDeHalfOp,KviEvent_OnHalfOp,KviEvent_OnDeHalfOp,KVI_OUT_MEHALFOP,KVI_OUT_MEDEHALFOP,KVI_OUT_HALFOP,KVI_OUT_HALFDEOP) CHANUSER_MODE('v',voice,KviEvent_OnMeVoice,KviEvent_OnMeDeVoice,KviEvent_OnVoice,KviEvent_OnDeVoice,KVI_OUT_MEVOICE,KVI_OUT_MEDEVOICE,KVI_OUT_VOICE,KVI_OUT_DEVOICE) CHANUSER_MODE('u',userop,KviEvent_OnMeUserOp,KviEvent_OnMeDeUserOp,KviEvent_OnUserOp,KviEvent_OnDeUserOp,KVI_OUT_MEUSEROP,KVI_OUT_MEDEUSEROP,KVI_OUT_USEROP,KVI_OUT_USERDEOP) #define CHANNEL_MODE(__modefl,__evmeset,__evmeunset,__evset,__evunset,__icomeset,__icomeunset,__icoset,__icounset) \ case __modefl: \ aParam = msg->connection()->decodeText(msg->safeParam(curParam++)); \ chan->setMask(*aux,aParam,bSet,msg->connection()->decodeText(msg->safePrefix()),TQDateTime::currentDateTime().toTime_t()); \ auxMask = new KviIrcMask(aParam); \ bIsMe = auxMask->matchesFixed( \ msg->connection()->userInfo()->nickName(), \ msg->connection()->userInfo()->userName(), \ msg->connection()->userInfo()->hostName()); \ delete auxMask; \ if(bIsMe) \ { \ if(KVS_TRIGGER_EVENT_4_HALTED(bSet ? __evmeset : __evmeunset,chan,szNick,szUser,szHost,aParam))msg->setHaltOutput(); \ } else { \ if(KVS_TRIGGER_EVENT_4_HALTED(bSet ? __evset : __evunset,chan,szNick,szUser,szHost,aParam))msg->setHaltOutput(); \ } \ if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges))) \ { \ chan->output(bSet ? (bIsMe ? __icomeset : __icoset) : (bIsMe ? __icomeunset : __icounset), \ __tr2qs("%Q [%Q@%Q] has set mode %c%c \r!m%c%c\r%Q\r"), \ &nickBuffer,&szUser,&hostBuffer, \ bSet ? '+' : '-',__modefl,bSet ? '-' : '+',__modefl,&aParam); \ } \ break; CHANNEL_MODE('b',KviEvent_OnMeBan,KviEvent_OnMeUnban,KviEvent_OnBan,KviEvent_OnUnban,KVI_OUT_MEBAN,KVI_OUT_MEUNBAN,KVI_OUT_BAN,KVI_OUT_UNBAN) CHANNEL_MODE('I',KviEvent_OnMeInviteException,KviEvent_OnMeInviteExceptionRemove,KviEvent_OnInviteException,KviEvent_OnInviteExceptionRemove,KVI_OUT_MEINVITEEXCEPT,KVI_OUT_MEINVITEUNEXCEPT,KVI_OUT_INVITEEXCEPT,KVI_OUT_INVITEUNEXCEPT) CHANNEL_MODE('e',KviEvent_OnMeBanException,KviEvent_OnMeBanExceptionRemove,KviEvent_OnBanException,KviEvent_OnBanExceptionRemove,KVI_OUT_MEBANEXCEPT,KVI_OUT_MEBANUNEXCEPT,KVI_OUT_BANEXCEPT,KVI_OUT_BANUNEXCEPT) default: chan->setChannelMode(*aux,bSet); if(!(msg->haltOutput() || KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges))) { chan->output(KVI_OUT_CHANMODE, __tr2qs("%Q [%Q@%Q] has set channel \r!m%c%c\rmode %c%c\r"), &nickBuffer,&szUser,&hostBuffer, bSet ? '-' : '+',*aux,bSet ? '+' : '-',*aux); } break; } ++aux; } TQString param; TQString params; param = msg->connection()->decodeText(msg->safeParam(curParamSave++)); while(!param.isEmpty()) { if(!params.isEmpty())params.append(' '); params.append(param); param = msg->connection()->decodeText(msg->safeParam(curParamSave++)); } if(KVS_TRIGGER_EVENT_5_HALTED(KviEvent_OnChannelModeChange,chan,szNick,szUser,szHost,modefl.ptr(),params)) msg->setHaltOutput(); if(KVI_OPTION_BOOL(KviOption_boolShowCompactModeChanges) && (!msg->haltOutput()) && (!kvi_strEqualCS(modefl.ptr(),"+"))) { if(!params.isEmpty()) { chan->output(KVI_OUT_CHANMODE,__tr2qs("%Q [%Q@%Q] has set mode %s %Q"), &nickBuffer,&szUser,&hostBuffer,modefl.ptr(),¶ms); } else { chan->output(KVI_OUT_CHANMODE,__tr2qs("%Q [%Q@%Q] has set channel mode %s"), &nickBuffer,&szUser,&hostBuffer,modefl.ptr()); } } }