/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
*/

/*
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
  Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
  Copyright (C) 2005 Peter Simonsson <psn@linux.se>
  Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
  Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
*/

#include "outputfilter.h"
#include "konversationapplication.h"
#include "konversationmainwindow.h"
#include "awaymanager.h"
#include "ignore.h"
#include "server.h"
#include "irccharsets.h"
#include "linkaddressbook/addressbook.h"
#include "query.h"

#include <tqstringlist.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqregexp.h>
#include <tqmap.h>
#include <tqvaluelist.h>
#include <tqtextcodec.h>

#include <tdelocale.h>
#include <kdebug.h>
#include <tdeio/passdlg.h>
#include <tdeconfig.h>
#include <tdeversion.h>
#include <kshell.h>
#include <tdesocketaddress.h>
#include <kresolver.h>
#include <kreverseresolver.h>
#include <tdemessagebox.h>


namespace Konversation
{
    OutputFilter::OutputFilter(Server* server)
        : TQObject(server)
    {
        m_server = server;
    }

    OutputFilter::~OutputFilter()
    {
    }

    // replace all aliases in the string and return true if anything got replaced at all
    bool OutputFilter::replaceAliases(TQString& line)
    {
        TQStringList aliasList=Preferences::aliasList();
        TQString cc(Preferences::commandChar());
        // check if the line starts with a defined alias
        for(unsigned int index=0;index<aliasList.count();index++)
        {
            // cut alias pattern from definition
            TQString aliasPattern(aliasList[index].section(' ',0,0));
            // cut first word from command line, so we do not wrongly find an alias
            // that starts with the same letters, like /m would override /me
            TQString lineStart=line.section(' ',0,0);

            // pattern found?
            // TODO: cc may be a regexp character here ... we should escape it then
            if (lineStart==cc+aliasPattern)
            {
                TQString aliasReplace;

                // cut alias replacement from definition
                if ( aliasList[index].contains("%p") )
                    aliasReplace = aliasList[index].section(' ',1);
                else
                    aliasReplace = aliasList[index].section(' ',1 )+' '+line.section(' ',1 );

                // protect "%%"
                aliasReplace.replace("%%","%\x01");
                // replace %p placeholder with rest of line
                aliasReplace.replace("%p",line.section(' ',1));
                // restore "%<1>" as "%%"
                aliasReplace.replace("%\x01","%%");
                // modify line
                line=aliasReplace;
                // return "replaced"
                return true;
            }
        }                                         // for

        return false;
    }

    TQStringList OutputFilter::splitForEncoding(const TQString& inputLine, uint max)
    {
        uint sublen = 0; //The encoded length since the last split
        int charLength = 0; //the length of this char
        int lastBreakPoint = 0;

        //FIXME should we run this through the encoder first, checking with "canEncode"?
        TQString text = inputLine; // the text we'll send, currently in Unicode
        TQStringList finals; // The strings we're going to output

        TQString channelCodecName=Preferences::channelEncoding(m_server->getDisplayName(), destination);
        //Get the codec we're supposed to use. This must not fail. (not verified)
        TQTextCodec* codec;

        // I copied this bit straight out of Server::send
        if (channelCodecName.isEmpty())
        {
            codec = m_server->getIdentity()->getCodec();
        }
        else
        {
            codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName);
        }

        Q_ASSERT(codec);
        int index = 0;

        while(text.length() > max)
        {
            // The most important bit - turn the current char into a TQCString so we can measure it
            TQCString ch = codec->fromUnicode(TQString(text[index]));
            charLength = ch.length();

            // If adding this char puts us over the limit:
            if (charLength + sublen > max)
            {
                if(lastBreakPoint != 0)
                {
                    finals.append(text.left(lastBreakPoint + 1));
                    text = text.mid(lastBreakPoint + 1);
                }
                else
                {
                    finals.append(text.left(index));
                    text = text.mid(index);
                }

                lastBreakPoint = 0;
                sublen = 0;
                index = 0;
            }
            else if (text[index].isSpace() || text[index].isPunct())
            {
                lastBreakPoint = index;
            }

            ++index;
            sublen += charLength;
        }

        if (!text.isEmpty())
        {
            finals.append(text);
        }

        return finals;
    }

    OutputFilterResult OutputFilter::parse(const TQString& myNick,const TQString& originalLine,const TQString& name)
    {
        setCommandChar();

        OutputFilterResult result;
        destination=name;

        TQString inputLine(originalLine);

        if(inputLine.isEmpty() || inputLine == "\n")
            return result;

        //Protect against nickserv auth being sent as a message on the off chance
        // someone didn't notice leading spaces
        {
            TQString testNickServ( inputLine.stripWhiteSpace() );
            if(testNickServ.startsWith(commandChar+"nickserv", false)
              || testNickServ.startsWith(commandChar+"ns", false))
            {
                    inputLine = testNickServ;
            }
        }

        if(!Preferences::disableExpansion())
        {
            // replace placeholders
            inputLine.replace("%%","%\x01");      // make sure to protect double %%
            inputLine.replace("%B","\x02");       // replace %B with bold char
            inputLine.replace("%C","\x03");       // replace %C with color char
            inputLine.replace("%G","\x07");       // replace %G with ASCII BEL 0x07
            inputLine.replace("%I","\x09");       // replace %I with italics char
            inputLine.replace("%O","\x0f");       // replace %O with reset to default char
            inputLine.replace("%S","\x13");       // replace %S with strikethru char
            //  inputLine.replace(TQRegExp("%?"),"\x15");
            inputLine.replace("%R","\x16");       // replace %R with reverse char
            inputLine.replace("%U","\x1f");       // replace %U with underline char
            inputLine.replace("%\x01","%");       // restore double %% as single %
        }

        TQString line=inputLine.lower();

        // Convert double command chars at the beginning to single ones
        if(line.startsWith(commandChar+commandChar) && !destination.isEmpty())
        {
            inputLine=inputLine.mid(1);
            goto BYPASS_COMMAND_PARSING;
        }
        // Server command?
        else if(line.startsWith(commandChar))
        {
            TQString command = inputLine.section(' ', 0, 0).mid(1).lower();
            TQString parameter = inputLine.section(' ', 1);

            if (command !="topic")
                parameter = parameter.stripWhiteSpace();

            if     (command == "join")     result = parseJoin(parameter);
            else if(command == "part")     result = parsePart(parameter);
            else if(command == "leave")    result = parsePart(parameter);
            else if(command == "quit")     result = parseQuit(parameter);
            else if(command == "close")    result = parseClose(parameter);
            else if(command == "notice")   result = parseNotice(parameter);
            else if(command == "j")        result = parseJoin(parameter);
            else if(command == "me")       result = parseMe(parameter, destination);
            else if(command == "msg")      result = parseMsg(myNick,parameter, false);
            else if(command == "m")        result = parseMsg(myNick,parameter, false);
            else if(command == "smsg")     result = parseSMsg(parameter);
            else if(command == "query")    result = parseMsg(myNick,parameter, true);
            else if(command == "op")       result = parseOp(parameter);
            else if(command == "deop")     result = parseDeop(myNick,parameter);
            else if(command == "hop")      result = parseHop(parameter);
            else if(command == "dehop")    result = parseDehop(myNick,parameter);
            else if(command == "voice")    result = parseVoice(parameter);
            else if(command == "unvoice")  result = parseUnvoice(myNick,parameter);
            else if(command == "devoice")  result = parseUnvoice(myNick,parameter);
            else if(command == "ctcp")     result = parseCtcp(parameter);
            else if(command == "ping")     result = parseCtcp(parameter.section(' ', 0, 0) + " ping");
            else if(command == "kick")     result = parseKick(parameter);
            else if(command == "topic")    result = parseTopic(parameter);
            else if(command == "away")     parseAway(parameter);
            else if(command == "unaway")   parseBack();
            else if(command == "back")     parseBack();
            else if(command == "invite")   result = parseInvite(parameter);
            else if(command == "exec")     result = parseExec(parameter);
            else if(command == "notify")   result = parseNotify(parameter);
            else if(command == "oper")     result = parseOper(myNick,parameter);
            else if(command == "ban")      result = parseBan(parameter);
            else if(command == "unban")    result = parseUnban(parameter);
            else if(command == "kickban")  result = parseBan(parameter,true);
            else if(command == "ignore")   result = parseIgnore(parameter);
            else if(command == "unignore") result = parseUnignore(parameter);
            else if(command == "quote")    result = parseQuote(parameter);
            else if(command == "say")      result = parseSay(parameter);
            else if(command == "list")     result = parseList(parameter);
            else if(command == "names")    result = parseNames(parameter);
            else if(command == "raw")      result = parseRaw(parameter);
            else if(command == "dcc")      result = parseDcc(parameter);
            else if(command == "konsole")  parseKonsole();
            else if(command == "aaway")    KonversationApplication::instance()->getAwayManager()->requestAllAway(parameter);
            else if(command == "aunaway")    KonversationApplication::instance()->getAwayManager()->requestAllUnaway();
            else if(command == "aback")    KonversationApplication::instance()->getAwayManager()->requestAllUnaway();
            else if(command == "ame")      result = parseAme(parameter);
            else if(command == "amsg")     result = parseAmsg(parameter);
            else if(command == "omsg")     result = parseOmsg(parameter);
            else if(command == "onotice")  result = parseOnotice(parameter);
            else if(command == "server")   parseServer(parameter);
            else if(command == "reconnect")  emit reconnectServer();
            else if(command == "disconnect") emit disconnectServer();
            else if(command == "charset")  result = parseCharset(parameter);
            else if(command == "encoding")  result = parseCharset(parameter);
            else if(command == "setkey")   result = parseSetKey(parameter);
            else if(command == "delkey")   result = parseDelKey(parameter);
            else if(command == "showkey")  result = parseShowKey(parameter);
            else if(command == "dns")      result = parseDNS(parameter);
            else if(command == "kill")     result = parseKill(parameter);
            else if(command == "queuetuner") result = parseShowTuner(parameter);

            // Forward unknown commands to server
            else
            {
                result.toServer = inputLine.mid(1);
                result.type = Message;
            }
        }
        // Ordinary message to channel/query?
        else if(!destination.isEmpty())
        {
            BYPASS_COMMAND_PARSING:

            TQStringList outputList=splitForEncoding(inputLine, m_server->getPreLength("PRIVMSG", destination));
            if (outputList.count() > 1)
            {
                result.output=TQString();
                result.outputList=outputList;
                for ( TQStringList::Iterator it = outputList.begin(); it != outputList.end(); ++it )
                {
                    result.toServerList += "PRIVMSG " + destination + " :" + *it;
                }
            }
            else
            {
                result.output = inputLine;
                result.toServer = "PRIVMSG " + destination + " :" + inputLine;
            }

            result.type = Message;
        }
        // Eveything else goes to the server unchanged
        else
        {
            result.toServer = inputLine;
            result.output = inputLine;
            result.typeString = i18n("Raw");
            result.type = Program;
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseShowTuner(const TQString &parameter)
    {
        KonversationApplication *konvApp = static_cast<KonversationApplication*>(TDEApplication::kApplication());
        OutputFilterResult result;

        if(parameter.isEmpty() || parameter == "on")
            konvApp->showQueueTuner(true);
        else if(parameter == "off")
            konvApp->showQueueTuner(false);
        else
            result = usage(i18n("Usage: %1queuetuner [on | off]").arg(commandChar));

        return result;
    }

    OutputFilterResult OutputFilter::parseOp(const TQString &parameter)
    {
        return changeMode(parameter,'o','+');
    }

    OutputFilterResult OutputFilter::parseDeop(const TQString &ownNick, const TQString &parameter)
    {
        return changeMode(addNickToEmptyNickList(ownNick,parameter),'o','-');
    }

    OutputFilterResult OutputFilter::parseHop(const TQString &parameter)
    {
        return changeMode(parameter, 'h', '+');
    }

    OutputFilterResult OutputFilter::parseDehop(const TQString &ownNick, const TQString &parameter)
    {
        return changeMode(addNickToEmptyNickList(ownNick,parameter), 'h', '-');
    }

    OutputFilterResult OutputFilter::parseVoice(const TQString &parameter)
    {
        return changeMode(parameter,'v','+');
    }

    OutputFilterResult OutputFilter::parseUnvoice(const TQString &ownNick, const TQString &parameter)
    {
        return changeMode(addNickToEmptyNickList(ownNick,parameter),'v','-');
    }

    OutputFilterResult OutputFilter::parseJoin(TQString& channelName)
    {
        OutputFilterResult result;

        if(channelName.contains(",")) // Protect against #foo,0 tricks
            channelName = channelName.remove(",0");
        //else if(channelName == "0") // FIXME IRC RFC 2812 section 3.2.1

        if (channelName.isEmpty())
        {
            if (destination.isEmpty() || !isAChannel(destination))
                return usage(i18n("Usage: %1JOIN <channel> [password]").arg(commandChar));
            channelName=destination;
        }
        else if (!isAChannel(channelName))
            channelName = "#" + channelName.stripWhiteSpace();

        Channel* channel = m_server->getChannelByName(channelName);

        if (channel)
        {
            // Note that this relies on the channels-flush-nicklists-on-disconnect behavior.
            if (!channel->numberOfNicks())
                result.toServer = "JOIN " + channelName;

            if (channel->joined()) emit showView (channel);
        }
        else
            result.toServer = "JOIN " + channelName;

        return result;
    }

    OutputFilterResult OutputFilter::parseKick(const TQString &parameter)
    {
        OutputFilterResult result;

        if(isAChannel(destination))
        {
            // get nick to kick
            TQString victim = parameter.left(parameter.find(" "));

            if(victim.isEmpty())
            {
                result = usage(i18n("Usage: %1KICK <nick> [reason]").arg(commandChar));
            }
            else
            {
                // get kick reason (if any)
                TQString reason = parameter.mid(victim.length() + 1);

                // if no reason given, take default reason
                if(reason.isEmpty())
                {
                    reason = m_server->getIdentity()->getKickReason();
                }

                result.toServer = "KICK " + destination + ' ' + victim + " :" + reason;
            }
        }
        else
        {
            result = error(i18n("%1KICK only works from within channels.").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parsePart(const TQString &parameter)
    {
        OutputFilterResult result;

        // No parameter, try default part message
        if(parameter.isEmpty())
        {
            // But only if we actually are in a channel
            if(isAChannel(destination))
            {
                result.toServer = "PART " + destination + " :" + m_server->getIdentity()->getPartReason();
            }
            else
            {
                result = error(i18n("%1PART without parameters only works from within a channel or a query.").arg(commandChar));
            }
        }
        else
        {
            // part a given channel
            if(isAChannel(parameter))
            {
                // get channel name
                TQString channel = parameter.left(parameter.find(" "));
                // get part reason (if any)
                TQString reason = parameter.mid(channel.length() + 1);

                // if no reason given, take default reason
                if(reason.isEmpty())
                {
                    reason = m_server->getIdentity()->getPartReason();
                }

                result.toServer = "PART " + channel + " :" + reason;
            }
            // part this channel with a given reason
            else
            {
                if(isAChannel(destination))
                {
                    result.toServer = "PART " + destination + " :" + parameter;
                }
                else
                {
                    result = error(i18n("%1PART without channel name only works from within a channel.").arg(commandChar));
                }
            }
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseTopic(const TQString &parameter)
    {
        OutputFilterResult result;

        // No parameter, try to get current topic
        if(parameter.isEmpty())
        {
            // But only if we actually are in a channel
            if(isAChannel(destination))
            {
                result.toServer = "TOPIC " + destination;
            }
            else
            {
                result = error(i18n("%1TOPIC without parameters only works from within a channel.").arg(commandChar));
            }
        }
        else
        {
            // retrieve or set topic of a given channel
            if(isAChannel(parameter))
            {
                // get channel name
                TQString channel=parameter.left(parameter.find(" "));
                // get topic (if any)
                TQString topic=parameter.mid(channel.length()+1);
                // if no topic given, retrieve topic
                if(topic.isEmpty())
                {
                    m_server->requestTopic(channel);
                }
                // otherwise set topic there
                else
                {
                    result.toServer = "TOPIC " + channel + " :";
                    //If we get passed a ^A as a topic its a sign we should clear the topic.
                    //Used to be a \n, but those get smashed by TQStringList::split and readded later
                    //Now is not the time to fight with that. FIXME
                    //If anyone out there *can* actually set the topic to a single ^A, now they have to
                    //specify it twice to get one.
                    if (topic =="\x01\x01")
                        result.toServer += '\x01';
                    else if (topic!="\x01")
                        result.toServer += topic;

                }
            }
            // set this channel's topic
            else
            {
                if(isAChannel(destination))
                {
                    result.toServer = "TOPIC " + destination + " :" + parameter;
                }
                else
                {
                    result = error(i18n("%1TOPIC without channel name only works from within a channel.").arg(commandChar));
                }
            }
        }

        return result;
    }

    void OutputFilter::parseAway(TQString& reason)
    {
        if (reason.isEmpty() && m_server->isAway())
            m_server->requestUnaway();
        else
            m_server->requestAway(reason);
    }

    void OutputFilter::parseBack()
    {
        m_server->requestUnaway();
    }

    OutputFilterResult OutputFilter::parseNames(const TQString &parameter)
    {
        OutputFilterResult result;
        result.toServer = "NAMES ";
        if (parameter.isNull())
        {
            return error(i18n("%1NAMES with no target may disconnect you from the server. Specify '*' if you really want this.").arg(commandChar));
        }
        else if (parameter != TQChar('*'))
        {
            result.toServer.append(parameter);
        }
        return result;
    }

    OutputFilterResult OutputFilter::parseClose(TQString parm)
    {
        if (parm.isEmpty())
            parm=destination;

        if (isAChannel(parm) && m_server->getChannelByName(parm))
            m_server->getChannelByName(parm)->closeYourself(false);
        else if (m_server->getQueryByName(parm))
            m_server->getQueryByName(parm)->closeYourself(false);
        else if (parm.isEmpty()) // this can only mean one thing.. we're in the Server tab
            m_server->closeYourself(false);
        else
            return usage(i18n("Usage: %1close [window] closes the named channel or query tab, or the current tab if none specified.").arg(commandChar));
        return OutputFilterResult();
    }

    OutputFilterResult OutputFilter::parseQuit(const TQString &reason)
    {
        OutputFilterResult result;

        result.toServer = "QUIT :";
        // if no reason given, take default reason
        if(reason.isEmpty())
            result.toServer += m_server->getIdentity()->getQuitReason();
        else
            result.toServer += reason;

        return result;
    }

    OutputFilterResult OutputFilter::parseNotice(const TQString &parameter)
    {
        OutputFilterResult result;
        TQString recipient = parameter.left(parameter.find(" "));
        TQString message = parameter.mid(recipient.length()+1);

        if(parameter.isEmpty() || message.isEmpty())
        {
            result = usage(i18n("Usage: %1NOTICE <recipient> <message>").arg(commandChar));
        }
        else
        {
            result.typeString = i18n("Notice");
            result.toServer = "NOTICE " + recipient + " :" + message;
            result.output=i18n("%1 is the message, %2 the recipient nickname","Sending notice \"%2\" to %1.").arg(recipient).arg(message);
            result.type = Program;
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseMe(const TQString &parameter, const TQString &destination)
    {
        OutputFilterResult result;

        if (!destination.isEmpty() && !parameter.isEmpty())
        {
            result.toServer = "PRIVMSG " + destination + " :" + '\x01' + "ACTION " + parameter + '\x01';
            result.output = parameter;
            result.type = Action;
        }
        else
        {
            result = usage(i18n("Usage: %1ME text").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseMsg(const TQString &myNick, const TQString &parameter, bool isQuery)
    {
        OutputFilterResult result;
        TQString recipient = parameter.section(" ", 0, 0, TQString::SectionSkipEmpty);
        TQString message = parameter.section(" ", 1);
        TQString output;

        if (recipient.isEmpty())
        {
            result = error("Error: You need to specify a recipient.");
            return result;
        }

        if (isQuery && m_server->isAChannel(recipient))
        {
            result = error("Error: You cannot open queries to channels.");
            return result;
        }

        if (message.stripWhiteSpace().isEmpty())
        {
            //empty result - we don't want to send any message to the server
            if (!isQuery)
            {
                 result = error("Error: You need to specify a message.");
                return result;
            }
        }
        else if (message.startsWith(commandChar+"me"))
        {
            result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01';
            output = TQString("* %1 %2").arg(myNick).arg(message.mid(4));
        }
        else
        {
            result.toServer = "PRIVMSG " + recipient + " :" + message;
            output = message;
        }

        ::Query *query;

        if (isQuery || output.isEmpty())
        {
            //if this is a /query, always open a query window.
            //treat "/msg nick" as "/query nick"

            //Note we have to be a bit careful here.
            //We only want to create ('obtain') a new nickinfo if we have done /query
            //or "/msg nick".  Not "/msg nick message".
            NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient);
            query = m_server->addQuery(nickInfo, true /*we initiated*/);
            //force focus if the user did not specify any message
            if (output.isEmpty()) emit showView(query);
        }
        else
        {
            //We have  "/msg nick message"
            query = m_server->getQueryByName(recipient);
        }

        if (query && !output.isEmpty())
        {
            if (message.startsWith(commandChar+"me"))
                                                  //log if and only if the query open
                query->appendAction(m_server->getNickname(), message.mid(4));
            else
                                                  //log if and only if the query open
                query->appendQuery(m_server->getNickname(), output);
        }

        if (output.isEmpty()) return result;       //result should be completely empty;
        //FIXME - don't do below line if query is focused
        result.output = output;
        result.typeString= recipient;
        result.type = PrivateMessage;
        return result;
    }

    OutputFilterResult OutputFilter::parseSMsg(const TQString &parameter)
    {
        OutputFilterResult result;
        TQString recipient = parameter.left(parameter.find(" "));
        TQString message = parameter.mid(recipient.length() + 1);

        if(message.startsWith(commandChar + "me"))
        {
            result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01';
        }
        else
        {
            result.toServer = "PRIVMSG " + recipient + " :" + message;
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseCtcp(const TQString &parameter)
    {
        OutputFilterResult result;
                                                  // who is the recipient?
        TQString recipient = parameter.section(' ', 0, 0);
                                                  // what is the first word of the ctcp?
        TQString request = parameter.section(' ', 1, 1, TQString::SectionSkipEmpty).upper();
                                                  // what is the complete ctcp command?
        TQString message = parameter.section(' ', 2, 0xffffff, TQString::SectionSkipEmpty);

        TQString out = request;
        if (!message.isEmpty())
            out+= ' ' + message;

        if (request == "PING")
        {
            unsigned int time_t = TQDateTime::currentDateTime().toTime_t();
            result.toServer = TQString("PRIVMSG %1 :\x01PING %2\x01").arg(recipient).arg(time_t);
            result.output = i18n("Sending CTCP-%1 request to %2.").arg("PING").arg(recipient);
        }
        else
        {
            result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + out + '\x01';
            result.output = i18n("Sending CTCP-%1 request to %2.").arg(out).arg(recipient);
        }

        result.typeString = i18n("CTCP");
        result.type = Program;
        return result;
    }

    OutputFilterResult OutputFilter::changeMode(const TQString &parameter,char mode,char giveTake)
    {
        OutputFilterResult result;
        // TODO: Make sure this works with +l <limit> and +k <password> also!
        TQString token;
        TQString tmpToken;
        TQStringList nickList = TQStringList::split(' ', parameter);

        if(nickList.count())
        {
            // Check if the user specified a channel
            if(isAChannel(nickList[0]))
            {
                token = "MODE " + nickList[0];
                // remove the first element
                nickList.remove(nickList.begin());
            }
            // Add default destination if it is a channel
            else if(isAChannel(destination))
            {
                token = "MODE " + destination;
            }

            // Only continue if there was no error
            if(token.length())
            {
                unsigned int modeCount = nickList.count();
                TQString modes;
                modes.fill(mode, modeCount);

                token += TQString(" ") + TQChar(giveTake) + modes;
                tmpToken = token;

                for(unsigned int index = 0; index < modeCount; index++)
                {
                    if((index % 3) == 0)
                    {
                        result.toServerList.append(token);
                        token = tmpToken;
                    }
                    token += ' ' + nickList[index];
                }

                if(token != tmpToken)
                {
                    result.toServerList.append(token);
                }
            }
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseDcc(const TQString &parameter)
    {
        OutputFilterResult result;

        // No parameter, just open DCC panel
        if(parameter.isEmpty())
        {
            emit addDccPanel();
        }
        else
        {
            TQString tmpParameter = parameter;
            TQStringList parameterList = TQStringList::split(' ', tmpParameter.replace("\\ ", "%20"));

            TQString dccType = parameterList[0].lower();

            if(dccType=="close")
            {
                emit closeDccPanel();
            }
            else if(dccType=="send")
            {
                if(parameterList.count()==1)      // DCC SEND
                {
                    emit requestDccSend();
                }                                 // DCC SEND <nickname>
                else if(parameterList.count()==2)
                {
                    emit requestDccSend(parameterList[1]);
                }                                 // DCC SEND <nickname> <file> [file] ...
                else if(parameterList.count()>2)
                {
                    // TODO: make sure this will work:
                    //output=i18n("Usage: %1DCC SEND nickname [fi6lename] [filename] ...").arg(commandChar);
                    KURL fileURL(parameterList[2]);

                    //We could easily check if the remote file exists, but then we might
                    //end up asking for creditionals twice, so settle for only checking locally
                    if(!fileURL.isLocalFile() || TQFile::exists( fileURL.path() ))
                    {
                        emit openDccSend(parameterList[1],fileURL);
                    }
                    else
                    {
                        result = error(i18n("File \"%1\" does not exist.").arg(parameterList[2]));
                    }
                }
                else                              // Don't know how this should happen, but ...
                {
                    result = usage(i18n("Usage: %1DCC [SEND nickname filename]").arg(commandChar));
                }
            }
            // TODO: DCC Chat etc. comes here
            else if(dccType=="chat")
            {
                if(parameterList.count()==2)
                {
                    emit openDccChat(parameterList[1]);
                }
                else
                {
                    result = usage(i18n("Usage: %1DCC [CHAT nickname]").arg(commandChar));
                }
            }
            else
            {
                result = error(i18n("Unrecognized command %1DCC %2. Possible commands are SEND, CHAT, CLOSE.").arg(commandChar).arg(parameterList[0]));
            }
        }

        return result;
    }

    OutputFilterResult OutputFilter::sendRequest(const TQString &recipient,const TQString &fileName,const TQString &address,const TQString &port,unsigned long size)
    {
        OutputFilterResult result;
        TQString niftyFileName(fileName);
        /*TQFile file(fileName);
        TQFileInfo info(file);*/

        result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND "
            + fileName
            + ' ' + address + ' ' + port + ' ' + TQString::number(size) + '\x01';

        // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
        if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
            niftyFileName = fileName.mid(1, fileName.length()-2);

        return result;
    }

    OutputFilterResult OutputFilter::passiveSendRequest(const TQString& recipient,const TQString &fileName,const TQString &address,unsigned long size,const TQString &token)
    {
        OutputFilterResult result;
        TQString niftyFileName(fileName);

        result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND "
            + fileName
            + ' ' + address + " 0 " + TQString::number(size) + ' ' + token + '\x01';

        // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
        if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
            niftyFileName = fileName.mid(1, fileName.length()-2);

        return result;
    }

    // Accepting Resume Request
    OutputFilterResult OutputFilter::acceptResumeRequest(const TQString &recipient,const TQString &fileName,const TQString &port,int startAt)
    {
        TQString niftyFileName(fileName);

        OutputFilterResult result;
        result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC ACCEPT " + fileName + ' ' + port
            + ' ' + TQString::number(startAt) + '\x01';

        // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
        if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
            niftyFileName = fileName.mid(1, fileName.length()-2);

        return result;
    }

    OutputFilterResult OutputFilter::resumeRequest(const TQString &sender,const TQString &fileName,const TQString &port,TDEIO::filesize_t startAt)
    {
        TQString niftyFileName(fileName);

        OutputFilterResult result;
        /*TQString newFileName(fileName);
        newFileName.replace(" ", "_");*/
        result.toServer = "PRIVMSG " + sender + " :" + '\x01' + "DCC RESUME " + fileName + ' ' + port + ' '
            + TQString::number(startAt) + '\x01';

        // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
        if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
            niftyFileName = fileName.mid(1, fileName.length()-2);

        return result;
    }

    OutputFilterResult OutputFilter::acceptPassiveSendRequest(const TQString& recipient,const TQString &fileName,const TQString &address,const TQString &port,unsigned long size,const TQString &token)
    {
        OutputFilterResult result;
        TQString niftyFileName(fileName);

        // "DCC SEND" to receive a file sounds weird, but it's ok.
        result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND "
            + fileName
            + ' ' + address + ' ' + port + ' ' + TQString::number(size) + ' ' + token + '\x01';

        // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
        if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
            niftyFileName = fileName.mid(1, fileName.length()-2);

        return result;
    }

    OutputFilterResult OutputFilter::parseInvite(const TQString &parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1INVITE <nick> [channel]").arg(commandChar));
        }
        else
        {
            TQString nick = parameter.section(' ', 0, 0, TQString::SectionSkipEmpty);
            TQString channel = parameter.section(' ', 1, 1, TQString::SectionSkipEmpty);

            if(channel.isEmpty())
            {
                if(isAChannel(destination))
                {
                    channel = destination;
                }
                else
                {
                    result = error(i18n("%1INVITE without channel name works only from within channels.").arg(commandChar));
                }
            }

            if(!channel.isEmpty())
            {
                if(isAChannel(channel))
                {
                    result.toServer = "INVITE " + nick + ' ' + channel;
                }
                else
                {
                    result = error(i18n("%1 is not a channel.").arg(channel));
                }
            }
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseExec(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1EXEC <script> [parameter list]").arg(commandChar));
        }
        else
        {
            TQStringList parameterList = TQStringList::split(' ', parameter);

            if(parameterList[0].find("../") == -1)
            {
                emit launchScript(destination, parameter);
            }
            else
            {
                result = error(i18n("Script name may not contain \"../\"!"));
            }
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseRaw(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty() || parameter == "open")
        {
            emit openRawLog(true);
        }
        else if(parameter == "close")
        {
            emit closeRawLog();
        }
        else
        {
            result = usage(i18n("Usage: %1RAW [OPEN | CLOSE]").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseNotify(const TQString& parameter)
    {
        OutputFilterResult result;

        TQString groupName = m_server->getDisplayName();

        int serverGroupId = -1;

        if (m_server->getServerGroup())
            serverGroupId = m_server->getServerGroup()->id();

        if (!parameter.isEmpty() && serverGroupId != -1)
        {
            TQStringList list = TQStringList::split(' ', parameter);

            for(unsigned int index = 0; index < list.count(); index++)
            {
                // Try to remove current pattern
                if(!Preferences::removeNotify(groupName, list[index]))
                {
                    // If remove failed, try to add it instead
                    if(!Preferences::addNotify(serverGroupId, list[index]))
                    {
                        kdDebug() << "OutputFilter::parseNotify(): Adding failed!" << endl;
                    }
                }
            }                                     // endfor
        }

        // show (new) notify list to user
        TQString list = Preferences::notifyStringByGroupName(groupName) + ' ' + Konversation::Addressbook::self()->allContactsNicksForServer(m_server->getServerName(), m_server->getDisplayName()).join(" ");

        result.typeString = i18n("Notify");

        if(list.isEmpty())
            result.output = i18n("Current notify list is empty.");
        else
            result.output = i18n("Current notify list: %1").arg(list);

        result.type = Program;
        return result;
    }

    OutputFilterResult OutputFilter::parseOper(const TQString& myNick,const TQString& parameter)
    {
        OutputFilterResult result;
        TQStringList parameterList = TQStringList::split(' ', parameter);

        if(parameter.isEmpty() || parameterList.count() == 1)
        {
            TQString nick((parameterList.count() == 1) ? parameterList[0] : myNick);
            TQString password;
            bool keep = false;

            int ret = TDEIO::PasswordDialog::getNameAndPassword
                (
                nick,
                password,
                &keep,
                i18n("Enter user name and password for IRC operator privileges:"),
                false,
                i18n("IRC Operator Password")
                );

            if(ret == TDEIO::PasswordDialog::Accepted)
            {
                result.toServer = "OPER " + nick + ' ' + password;
            }
        }
        else
        {
            result.toServer = "OPER " + parameter;
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseBan(const TQString& parameter, bool kick)
    {
        OutputFilterResult result;
        // assume incorrect syntax first
        bool showUsage = true;

        if(!parameter.isEmpty())
        {
            TQStringList parameterList=TQStringList::split(' ',parameter);
            TQString channel;
            TQString option;
            // check for option
            TQString lowerParameter = parameterList[0].lower();
            bool host = (lowerParameter == "-host");
            bool domain = (lowerParameter == "-domain");
            bool uhost = (lowerParameter == "-userhost");
            bool udomain = (lowerParameter == "-userdomain");

            // remove possible option
            if (host || domain || uhost || udomain)
            {
                option = parameterList[0].mid(1);
                parameterList.pop_front();
            }

            // look for channel / ban mask
            if (parameterList.count())
            {
                // user specified channel
                if (isAChannel(parameterList[0]))
                {
                    channel = parameterList[0];
                    parameterList.pop_front();
                }
                // no channel, so assume current destination as channel
                else if (isAChannel(destination))
                    channel = destination;
                else
                {
                    // destination is no channel => error
                    if (!kick)
                        result = error(i18n("%1BAN without channel name works only from inside a channel.").arg(commandChar));
                    else
                        result = error(i18n("%1KICKBAN without channel name works only from inside a channel.").arg(commandChar));

                    // no usage information after error
                    showUsage = false;
                }
                // signal server to ban this user if all went fine
                if (!channel.isEmpty())
                {
                    if (kick)
                    {
                        TQString victim = parameterList[0];
                        parameterList.pop_front();

                        TQString reason = parameterList.join(" ");

                        result.toServer = "KICK " + channel + ' ' + victim + " :" + reason;

                        emit banUsers(TQStringList(victim),channel,option);
                    }
                    else
                    {
                        emit banUsers(parameterList,channel,option);
                    }

                    // syntax was correct, so reset flag
                    showUsage = false;
                }
            }
        }

        if (showUsage)
        {
            if (!kick)
                result = usage(i18n("Usage: %1BAN [-HOST | -DOMAIN | -USERHOST | -USERDOMAIN] [channel] <user|mask>").arg(commandChar));
            else
                result = usage(i18n("Usage: %1KICKBAN [-HOST | -DOMAIN | -USERHOST | -USERDOMAIN] [channel] <user|mask> [reason]").arg(commandChar));
        }

        return result;
    }

    // finally set the ban
    OutputFilterResult OutputFilter::execBan(const TQString& mask,const TQString& channel)
    {
        OutputFilterResult result;
        result.toServer = "MODE " + channel + " +b " + mask;
        return result;
    }

    OutputFilterResult OutputFilter::parseUnban(const TQString& parameter)
    {
        OutputFilterResult result;
        // assume incorrect syntax first
        bool showUsage=true;

        if(!parameter.isEmpty())
        {
            TQStringList parameterList = TQStringList::split(' ', parameter);
            TQString channel;
            TQString mask;

            // if the user specified a channel
            if(isAChannel(parameterList[0]))
            {
                // get channel
                channel = parameterList[0];
                // remove channel from parameter list
                parameterList.pop_front();
            }
            // otherwise the current destination must be a channel
            else if(isAChannel(destination))
                channel = destination;
            else
            {
                // destination is no channel => error
                result = error(i18n("%1UNBAN without channel name works only from inside a channel.").arg(commandChar));
                // no usage information after error
                showUsage = false;
            }
            // if all went good, signal server to unban this mask
            if(!channel.isEmpty())
            {
                emit unbanUsers(parameterList[0], channel);
                // syntax was correct, so reset flag
                showUsage = false;
            }
        }

        if(showUsage)
        {
            result = usage(i18n("Usage: %1UNBAN [channel] pattern").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::execUnban(const TQString& mask,const TQString& channel)
    {
        OutputFilterResult result;
        result.toServer = "MODE " + channel + " -b " + mask;
        return result;
    }

    OutputFilterResult OutputFilter::parseIgnore(const TQString& parameter)
    {
        OutputFilterResult result;
        // assume incorrect syntax first
        bool showUsage = true;

        // did the user give parameters at all?
        if(!parameter.isEmpty())
        {
            TQStringList parameterList = TQStringList::split(' ', parameter);

            // if nothing else said, only ignore channels and queries
            int value = Ignore::Channel | Ignore::Query;

            // user specified -all option
            if(parameterList[0].lower() == "-all")
            {
                // ignore everything
                value = Ignore::All;
                parameterList.pop_front();
            }

            // were there enough parameters?
            if(parameterList.count() >= 1)
            {
                for(unsigned int index=0;index<parameterList.count();index++)
                {
                    if(!parameterList[index].contains('!'))
                    {
                        parameterList[index] += "!*";
                    }

                    Preferences::addIgnore(parameterList[index] + ',' + TQString::number(value));
                }

                result.output = i18n("Added %1 to your ignore list.").arg(parameterList.join(", "));
                result.typeString = i18n("Ignore");
                result.type = Program;

                // all went fine, so show no error message
                showUsage = false;
            }
        }

        if(showUsage)
        {
            result = usage(i18n("Usage: %1IGNORE [ -ALL ] <user 1> <user 2> ... <user n>").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseUnignore(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1UNIGNORE <user 1> <user 2> ... <user n>").arg(commandChar));
        }
        else
        {
            TQString unignore = parameter.simplifyWhiteSpace();
            TQStringList unignoreList = TQStringList::split(' ',unignore);

            TQStringList succeeded;
            TQStringList failed;

            // Iterate over potential unignores
            for (TQStringList::Iterator it = unignoreList.begin(); it != unignoreList.end(); ++it) 
            {
                // If pattern looks incomplete, try to complete it
                if (!(*it).contains('!'))
                {
                    TQString fixedPattern = (*it);
                    fixedPattern += "!*";

                    bool success = false;

                    // Try to remove completed pattern
                    if (Preferences::removeIgnore(fixedPattern))
                    {
                        succeeded.append(fixedPattern);
                        success = true;
                    }

                    // Try to remove the incomplete version too, in case it was added via the GUI ...
                    // FIXME: Validate patterns in GUI?
                    if (Preferences::removeIgnore((*it)))
                    {
                        succeeded.append((*it));
                        success = true;
                    }

                    if (!success)
                        failed.append((*it) + "[!*]");
                }
                // Try to remove seemingly complete pattern
                else if (Preferences::removeIgnore((*it)))
                    succeeded.append((*it));
                // Failed to remove given complete pattern
                else
                    failed.append((*it));
            }

            // Print all successful unignores, in case there were any
            if (succeeded.count()>=1)
            {
                m_server->appendMessageToFrontmost(i18n("Ignore"),i18n("Removed %1 from your ignore list.").arg(succeeded.join(", ")));
            }

            // One failed unignore
            if (failed.count()==1)
            {
                m_server->appendMessageToFrontmost(i18n("Error"),i18n("No such ignore: %1").arg(failed.join(", ")));
            }

            // Multiple failed unignores
            if (failed.count()>1)
            {
                m_server->appendMessageToFrontmost(i18n("Error"),i18n("No such ignores: %1").arg(failed.join(", ")));
            }
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseQuote(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1QUOTE command list").arg(commandChar));
        }
        else
        {
            result.toServer = parameter;
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseSay(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1SAY text").arg(commandChar));
        }
        else
        {
            result.toServer = "PRIVMSG " + destination + " :" + parameter;
            result.output = parameter;
        }

        return result;
    }

    void OutputFilter::parseKonsole()
    {
        emit openKonsolePanel();
    }

    // Accessors

    void OutputFilter::setCommandChar() { commandChar=Preferences::commandChar(); }

    // # & + and ! are *often*, but not necessarily, channel identifiers. + and ! are non-RFC, so if a server doesn't offer 005 and
    // supports + and ! channels, I think thats broken behaviour on their part - not ours.
    bool OutputFilter::isAChannel(const TQString &check)
    {
        Q_ASSERT(m_server);
                                                  // XXX if we ever see the assert, we need the ternary
        return m_server? m_server->isAChannel(check) : TQString("#&").contains(check.at(0));
    }

    OutputFilterResult OutputFilter::usage(const TQString& string)
    {
        OutputFilterResult result;
        result.typeString = i18n("Usage");
        result.output = string;
        result.type = Program;
        return result;
    }

    OutputFilterResult OutputFilter::info(const TQString& string)
    {
        OutputFilterResult result;
        result.typeString = i18n("Info");
        result.output = string;
        result.type = Program;
        return result;
    }

    OutputFilterResult OutputFilter::error(const TQString& string)
    {
        OutputFilterResult result;
        result.typeString = i18n("Error");
        result.output = string;
        result.type = Program;
        return result;
    }

    OutputFilterResult OutputFilter::parseAme(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1AME text").arg(commandChar));
        }

        emit multiServerCommand("me", parameter);
        return result;
    }

    OutputFilterResult OutputFilter::parseAmsg(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1AMSG text").arg(commandChar));
        }

        emit multiServerCommand("msg", parameter);
        return result;
    }

    void OutputFilter::parseServer(const TQString& parameter)
    {
        if (parameter.isEmpty() && !m_server->isConnected() && !m_server->isConnecting())
            emit reconnectServer();
        else
        {
            TQStringList splitted = TQStringList::split(" ", parameter);
            TQString host = splitted[0];
            TQString port = "6667";
            TQString password;

            if (splitted.count() == 3)
                emit connectTo(Konversation::CreateNewConnection, splitted[0], splitted[1], splitted[2]);
            else if (splitted.count() == 2)
            {
                if (splitted[0].contains(TQRegExp(":[0-9]+$")))
                    emit connectTo(Konversation::CreateNewConnection, splitted[0], "", splitted[1]);
                else
                    emit connectTo(Konversation::CreateNewConnection, splitted[0], splitted[1]);
            }
            else
                emit connectTo(Konversation::CreateNewConnection, splitted[0]);
        }
    }

    OutputFilterResult OutputFilter::parseOmsg(const TQString& parameter)
    {
        OutputFilterResult result;

        if(!parameter.isEmpty())
        {
            result.toServer = "PRIVMSG @"+destination+" :"+parameter;
        }
        else
        {
            result = usage(i18n("Usage: %1OMSG text").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseOnotice(const TQString& parameter)
    {
        OutputFilterResult result;

        if(!parameter.isEmpty())
        {
            result.toServer = "NOTICE @"+destination+" :"+parameter;
            result.typeString = i18n("Notice");
            result.type = Program;
            result.output = i18n("Sending notice \"%1\" to %2.").arg(parameter, destination);
        }
        else
        {
            result = usage(i18n("Usage: %1ONOTICE text").arg(commandChar));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseCharset(const TQString& charset)
    {
        OutputFilterResult result;

        if (charset.isEmpty ())
        {
            result = info (i18n("Current encoding is: %1")
                    .arg(m_server->getIdentity()->getCodec()->name()));
            return result;
        }

        TQString shortName = Konversation::IRCCharsets::self()->ambiguousNameToShortName(charset);

        if(!shortName.isEmpty())
        {
            m_server->getIdentity()->setCodecName(shortName);
            emit encodingChanged();
            result = info (i18n("Switched to %1 encoding.").arg(shortName));
        }
        else
        {
            result = error(i18n("%1 is not a valid encoding.").arg (charset));
        }

        return result;
    }

    OutputFilterResult OutputFilter::parseSetKey(const TQString& parameter)
    {

        TQStringList parms = TQStringList::split(" ", parameter);

        if (parms.count() == (0 >> parms.count() > 2))
            return usage(i18n("Usage: %1setkey [<nick|channel>] <key> sets the encryption key for nick or channel. %2setkey <key> when in a channel or query tab to set the key for it.").arg(commandChar).arg(commandChar) );
        else if (parms.count() == 1)
            parms.prepend(destination);

        m_server->setKeyForRecipient(parms[0], parms[1].local8Bit());

        if (isAChannel(parms[0]) && m_server->getChannelByName(parms[0]))
            m_server->getChannelByName(parms[0])->setEncryptedOutput(true);
        else if (m_server->getQueryByName(parms[0]))
            m_server->getQueryByName(parms[0])->setEncryptedOutput(true);

        return info(i18n("The key for %1 has been set.").arg(parms[0]));
    }

    OutputFilterResult OutputFilter::parseDelKey(const TQString& prametr)
    {
        TQString parameter(prametr.isEmpty()?destination:prametr);

        if(parameter.isEmpty() || parameter.contains(' '))
            return usage(i18n("Usage: %1delkey <nick> or <channel> deletes the encryption key for nick or channel").arg(commandChar));

        m_server->setKeyForRecipient(parameter, "");

        if (isAChannel(parameter) && m_server->getChannelByName(parameter))
            m_server->getChannelByName(parameter)->setEncryptedOutput(false);
        else if (m_server->getQueryByName(parameter))
            m_server->getQueryByName(parameter)->setEncryptedOutput(false);

        return info(i18n("The key for %1 has been deleted.").arg(parameter));
    }

    OutputFilterResult OutputFilter::parseShowKey(const TQString& prametr)
    {
        TQString parameter(prametr.isEmpty()?destination:prametr);
        TQString key(m_server->getKeyForRecipient(parameter));
        TQWidget *mw=KonversationApplication::instance()->getMainWindow();
        if (!key.isEmpty())
            KMessageBox::information(mw, i18n("The key for %1 is \"%2\".").arg(parameter).arg(key), i18n("Blowfish"));
        else
            KMessageBox::information(mw, i18n("No key has been set for %1.").arg(parameter));
        OutputFilterResult result;
        return result;
    }

    OutputFilterResult OutputFilter::parseList(const TQString& parameter)
    {
        OutputFilterResult result;

        emit openChannelList(parameter, true);

        return result;
    }

    OutputFilterResult OutputFilter::parseDNS(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1DNS <nick>").arg(commandChar));
        }
        else
        {
            TQStringList splitted = TQStringList::split(" ", parameter);
            TQString target = splitted[0];

            KIpAddress address(target);

            // Parameter is an IP address
            if (address.isIPv4Addr() || address.isIPv6Addr())
            {
            // Disable the reverse resolve codepath on older KDE versions due to many
            // distributions shipping visibility-enabled KDE 3.4 and KNetwork not 
            // coping with it.
#if KDE_IS_VERSION(3,5,1)
                KNetwork:: KInetSocketAddress socketAddress(address,0);
                TQString resolvedTarget;
                TQString serv; // We don't need this, but KReverseResolver::resolve does.

                if (KNetwork::KReverseResolver::resolve(socketAddress,resolvedTarget,serv))
                {
                    result.typeString = i18n("DNS");
                    result.output = i18n("Resolved %1 to: %2").arg(target).arg(resolvedTarget);
                    result.type = Program;
                }
                else
                {
                    result = error(i18n("Unable to resolve %1").arg(target));
                }
#else
                result = error(i18n("Reverse-resolving requires KDE version 3.5.1 or higher."));
#endif
            }
            // Parameter is presumed to be a host due to containing a dot. Yeah, it's dumb.
            // FIXME: The reason we detect the host by occurrence of a dot is the large penalty
            // we would incur by using inputfilter to find out if there's a user==target on the
            // server - once we have a better API for this, switch to it.
            else if (target.contains('.'))
            {
                KNetwork::KResolverResults resolved = KNetwork::KResolver::resolve(target,"");
                if(resolved.error() == KResolver::NoError && resolved.size() > 0)
                {
                    TQString resolvedTarget = resolved.first().address().nodeName();
                    result.typeString = i18n("DNS");
                    result.output = i18n("Resolved %1 to: %2").arg(target).arg(resolvedTarget);
                    result.type = Program;
                }
                else
                {
                    result = error(i18n("Unable to resolve %1").arg(target));
                }
            }
            // Parameter is either host nor IP, so request a lookup from server, which in
            // turn lets inputfilter do the job.
            else
            {
                m_server->resolveUserhost(target);
            }
        }

        return result;
    }


    TQString OutputFilter::addNickToEmptyNickList(const TQString& nick, const TQString& parameter)
    {
        TQStringList nickList = TQStringList::split(' ', parameter);
        TQString newNickList;

        if (nickList.count() == 0)
        {
            newNickList = nick;
        }
        // check if list contains only target channel
        else if (nickList.count() == 1 && isAChannel(nickList[0]))
        {
            newNickList = nickList[0] + ' ' + nick;
        }
        // list contains at least one nick
        else
        {
            newNickList = parameter;
        }
        
        return newNickList;
    }

    OutputFilterResult OutputFilter::parseKill(const TQString& parameter)
    {
        OutputFilterResult result;

        if(parameter.isEmpty())
        {
            result = usage(i18n("Usage: %1KILL <nick> [comment]").arg(commandChar));
        }
        else
        {
            TQString victim = parameter.section(' ', 0, 0);
            result.toServer = "KILL " + victim + " :" + parameter.mid(victim.length() + 1);
        }

        return result;
    }
}
#include "outputfilter.moc"
